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