1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_framework.hxx"
30 #include <xml/acceleratorconfigurationreader.hxx>
31 
32 //_______________________________________________
33 // own includes
34 
35 #ifndef __FRAMEWORK_ACCELERATORCONST_H_
36 #include <acceleratorconst.h>
37 #endif
38 
39 //_______________________________________________
40 // interface includes
41 #include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
42 #include <com/sun/star/awt/KeyModifier.hpp>
43 #include <com/sun/star/awt/KeyEvent.hpp>
44 #include <com/sun/star/awt/Key.hpp>
45 #include <com/sun/star/container/ElementExistException.hpp>
46 
47 //_______________________________________________
48 // other includes
49 #include <vcl/svapp.hxx>
50 #include <rtl/ustrbuf.hxx>
51 
52 //_______________________________________________
53 // namespace
54 
55 namespace framework{
56 
57 //-----------------------------------------------
58 /* Throws a SaxException in case a wrong formated XML
59    structure was detected.
60 
61    This macro combined the given comment with a generic
62    way to find out the XML line (where the error occured)
63    to format a suitable message.
64 
65    @param   COMMENT
66             an ascii string, which describe the problem.
67  */
68 #define THROW_PARSEEXCEPTION(COMMENT)                                   \
69     {                                                                   \
70         ::rtl::OUStringBuffer sMessage(256);                            \
71         sMessage.append     (implts_getErrorLineString());              \
72         sMessage.appendAscii(COMMENT                    );              \
73                                                                         \
74 		throw css::xml::sax::SAXException(                              \
75                 sMessage.makeStringAndClear(),                          \
76                 static_cast< css::xml::sax::XDocumentHandler* >(this),  \
77                 css::uno::Any());                                       \
78     }
79 
80 //-----------------------------------------------
81 // XInterface
82 DEFINE_XINTERFACE_1(AcceleratorConfigurationReader                   ,
83                     OWeakObject                                      ,
84                     DIRECT_INTERFACE(css::xml::sax::XDocumentHandler))
85 
86 //-----------------------------------------------
87 AcceleratorConfigurationReader::AcceleratorConfigurationReader(AcceleratorCache& rContainer)
88     : ThreadHelpBase          (&Application::GetSolarMutex())
89     , OWeakObject             (                             )
90     , m_rContainer            (rContainer                   )
91     , m_bInsideAcceleratorList(sal_False                    )
92     , m_bInsideAcceleratorItem(sal_False                    )
93 {
94 }
95 
96 //-----------------------------------------------
97 AcceleratorConfigurationReader::~AcceleratorConfigurationReader()
98 {
99 }
100 
101 //-----------------------------------------------
102 void SAL_CALL AcceleratorConfigurationReader::startDocument()
103     throw(css::xml::sax::SAXException,
104           css::uno::RuntimeException )
105 {
106 }
107 
108 //-----------------------------------------------
109 void SAL_CALL AcceleratorConfigurationReader::endDocument()
110     throw(css::xml::sax::SAXException,
111           css::uno::RuntimeException )
112 {
113     // The xml file seems to be corrupted.
114     // Because we found no end-tags ... at least for
115     // one list or item.
116     if (
117         (m_bInsideAcceleratorList) ||
118         (m_bInsideAcceleratorItem)
119        )
120     {
121         THROW_PARSEEXCEPTION("No matching start or end element 'acceleratorlist' found!")
122     }
123 }
124 
125 //-----------------------------------------------
126 void SAL_CALL AcceleratorConfigurationReader::startElement(const ::rtl::OUString&                                      sElement      ,
127                                                            const css::uno::Reference< css::xml::sax::XAttributeList >& xAttributeList)
128     throw(css::xml::sax::SAXException,
129           css::uno::RuntimeException )
130 {
131     EXMLElement eElement = AcceleratorConfigurationReader::implst_classifyElement(sElement);
132 
133     // Note: We handle "accel:item" before "accel:acceleratorlist" to perform this operation.
134     // Because an item occures very often ... a list should occure one times only!
135     if (eElement == E_ELEMENT_ITEM)
136     {
137         if (!m_bInsideAcceleratorList)
138             THROW_PARSEEXCEPTION("An element \"accel:item\" must be embeded into 'accel:acceleratorlist'.")
139         if (m_bInsideAcceleratorItem)
140             THROW_PARSEEXCEPTION("An element \"accel:item\" is not a container.")
141         m_bInsideAcceleratorItem = sal_True;
142 
143         ::rtl::OUString    sCommand;
144         css::awt::KeyEvent aEvent  ;
145 
146         sal_Int16 c = xAttributeList->getLength();
147         sal_Int16 i = 0;
148         for (i=0; i<c; ++i)
149         {
150             ::rtl::OUString sAttribute = xAttributeList->getNameByIndex(i);
151             ::rtl::OUString sValue     = xAttributeList->getValueByIndex(i);
152             EXMLAttribute   eAttribute = AcceleratorConfigurationReader::implst_classifyAttribute(sAttribute);
153             switch(eAttribute)
154             {
155                 case E_ATTRIBUTE_URL :
156                     sCommand = sValue.intern();
157                     break;
158 
159                 case E_ATTRIBUTE_KEYCODE :
160                     aEvent.KeyCode = m_rKeyMapping->mapIdentifierToCode(sValue);
161                     break;
162 
163                 case E_ATTRIBUTE_MOD_SHIFT :
164                     aEvent.Modifiers |= css::awt::KeyModifier::SHIFT;
165                     break;
166 
167                 case E_ATTRIBUTE_MOD_MOD1  :
168                     aEvent.Modifiers |= css::awt::KeyModifier::MOD1;
169                     break;
170 
171                 case E_ATTRIBUTE_MOD_MOD2  :
172                     aEvent.Modifiers |= css::awt::KeyModifier::MOD2;
173                     break;
174 
175                 case E_ATTRIBUTE_MOD_MOD3  :
176                     aEvent.Modifiers |= css::awt::KeyModifier::MOD3;
177             }
178         }
179 
180         // validate command and key event.
181         if (
182             (!sCommand.getLength()) ||
183             (aEvent.KeyCode == 0  )
184            )
185         {
186             THROW_PARSEEXCEPTION("XML element does not describe a valid accelerator nor a valid command.")
187         }
188 
189         // register key event + command inside cache ...
190         // Check for already existing items there.
191         if (!m_rContainer.hasKey(aEvent))
192             m_rContainer.setKeyCommandPair(aEvent, sCommand);
193         #ifdef ENABLE_WARNINGS
194         else
195         {
196             // Attention: Its not realy a reason to throw an exception and kill the office, if the configuration contains
197             // multiple registrations for the same key :-) Show a warning ... and ignore the second item.
198             // THROW_PARSEEXCEPTION("Command is registered for the same key more then once.")
199             ::rtl::OUStringBuffer sMsg(256);
200             sMsg.appendAscii("Double registration detected.\nCommand = \"");
201             sMsg.append     (sCommand                                     );
202             sMsg.appendAscii("\"\nKeyCode = "                             );
203             sMsg.append     ((sal_Int32)aEvent.KeyCode                    );
204             sMsg.appendAscii("\nModifiers = "                             );
205             sMsg.append     ((sal_Int32)aEvent.Modifiers                  );
206             sMsg.appendAscii("\nIgnore this item!"                        );
207             LOG_WARNING("AcceleratorConfigurationReader::startElement()", U2B(sMsg.makeStringAndClear()))
208         }
209         #endif // ENABLE_WARNINGS
210     }
211 
212     if (eElement == E_ELEMENT_ACCELERATORLIST)
213     {
214         if (m_bInsideAcceleratorList)
215             THROW_PARSEEXCEPTION("An element \"accel:acceleratorlist\" cannot be used recursive.")
216         m_bInsideAcceleratorList = sal_True;
217         return;
218     }
219 }
220 
221 //-----------------------------------------------
222 void SAL_CALL AcceleratorConfigurationReader::endElement(const ::rtl::OUString& sElement)
223     throw(css::xml::sax::SAXException,
224           css::uno::RuntimeException )
225 {
226     EXMLElement eElement = AcceleratorConfigurationReader::implst_classifyElement(sElement);
227 
228     // Note: We handle "accel:item" before "accel:acceleratorlist" to perform this operation.
229     // Because an item occures very often ... a list should occure one times only!
230     if (eElement == E_ELEMENT_ITEM)
231     {
232         if (!m_bInsideAcceleratorItem)
233             THROW_PARSEEXCEPTION("Found end element 'accel:item', but no start element.")
234         m_bInsideAcceleratorItem = sal_False;
235     }
236 
237     if (eElement == E_ELEMENT_ACCELERATORLIST)
238     {
239         if (!m_bInsideAcceleratorList)
240             THROW_PARSEEXCEPTION("Found end element 'accel:acceleratorlist', but no start element.")
241         m_bInsideAcceleratorList = sal_False;
242     }
243 }
244 
245 //-----------------------------------------------
246 void SAL_CALL AcceleratorConfigurationReader::characters(const ::rtl::OUString&)
247     throw(css::xml::sax::SAXException,
248           css::uno::RuntimeException )
249 {
250 }
251 
252 //-----------------------------------------------
253 void SAL_CALL AcceleratorConfigurationReader::ignorableWhitespace(const ::rtl::OUString&)
254     throw(css::xml::sax::SAXException,
255           css::uno::RuntimeException )
256 {
257 }
258 
259 //-----------------------------------------------
260 void SAL_CALL AcceleratorConfigurationReader::processingInstruction(const ::rtl::OUString& /*sTarget*/,
261                                                                     const ::rtl::OUString& /*sData*/  )
262     throw(css::xml::sax::SAXException,
263           css::uno::RuntimeException )
264 {
265 }
266 
267 //-----------------------------------------------
268 void SAL_CALL AcceleratorConfigurationReader::setDocumentLocator(const css::uno::Reference< css::xml::sax::XLocator >& xLocator)
269     throw(css::xml::sax::SAXException,
270           css::uno::RuntimeException )
271 {
272     m_xLocator = xLocator;
273 }
274 
275 //-----------------------------------------------
276 AcceleratorConfigurationReader::EXMLElement AcceleratorConfigurationReader::implst_classifyElement(const ::rtl::OUString& sElement)
277 {
278     AcceleratorConfigurationReader::EXMLElement eElement;
279 
280     if (sElement.equals(NS_ELEMENT_ACCELERATORLIST))
281         eElement = E_ELEMENT_ACCELERATORLIST;
282     else
283     if (sElement.equals(NS_ELEMENT_ITEM))
284         eElement = E_ELEMENT_ITEM;
285     else
286         throw css::uno::RuntimeException(
287                 DECLARE_ASCII("Unknown XML element detected!"),
288                 css::uno::Reference< css::xml::sax::XDocumentHandler >());
289 
290     return eElement;
291 }
292 
293 //-----------------------------------------------
294 AcceleratorConfigurationReader::EXMLAttribute AcceleratorConfigurationReader::implst_classifyAttribute(const ::rtl::OUString& sAttribute)
295 {
296     AcceleratorConfigurationReader::EXMLAttribute eAttribute;
297 
298     if (sAttribute.equals(NS_ATTRIBUTE_KEYCODE))
299         eAttribute = E_ATTRIBUTE_KEYCODE;
300     else
301     if (sAttribute.equals(NS_ATTRIBUTE_MOD_SHIFT))
302         eAttribute = E_ATTRIBUTE_MOD_SHIFT;
303     else
304     if (sAttribute.equals(NS_ATTRIBUTE_MOD_MOD1))
305         eAttribute = E_ATTRIBUTE_MOD_MOD1;
306     else
307     if (sAttribute.equals(NS_ATTRIBUTE_MOD_MOD2))
308         eAttribute = E_ATTRIBUTE_MOD_MOD2;
309     else
310     if (sAttribute.equals(NS_ATTRIBUTE_MOD_MOD3))
311         eAttribute = E_ATTRIBUTE_MOD_MOD3;
312     else
313     if (sAttribute.equals(NS_ATTRIBUTE_URL))
314         eAttribute = E_ATTRIBUTE_URL;
315     else
316         throw css::uno::RuntimeException(
317                 DECLARE_ASCII("Unknown XML attribute detected!"),
318                 css::uno::Reference< css::xml::sax::XDocumentHandler >());
319 
320     return eAttribute;
321 }
322 
323 //-----------------------------------------------
324 ::rtl::OUString AcceleratorConfigurationReader::implts_getErrorLineString()
325 {
326     if (!m_xLocator.is())
327         return DECLARE_ASCII("Error during parsing XML. (No further info available ...)");
328 
329     ::rtl::OUStringBuffer sMsg(256);
330     sMsg.appendAscii("Error during parsing XML in\nline = ");
331     sMsg.append     (m_xLocator->getLineNumber()           );
332     sMsg.appendAscii("\ncolumn = "                         );
333     sMsg.append     (m_xLocator->getColumnNumber()         );
334     sMsg.appendAscii("."                                   );
335     return sMsg.makeStringAndClear();
336 }
337 
338 } // namespace framework
339