001    /*
002     * Copyright 2002 - 2008 JEuclid, http://jeuclid.sf.net
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    /* $Id: OperatorDictionary2.java,v 241d8c4d0dc1 2009/09/04 14:15:03 max $ */
018    
019    package net.sourceforge.jeuclid.elements.support.operatordict;
020    
021    import java.io.IOException;
022    import java.io.InputStream;
023    import java.io.Serializable;
024    import java.util.EnumMap;
025    import java.util.Map;
026    import java.util.TreeMap;
027    
028    import javax.xml.parsers.ParserConfigurationException;
029    import javax.xml.parsers.SAXParserFactory;
030    
031    import net.sourceforge.jeuclid.elements.presentation.token.Mo;
032    
033    import org.apache.commons.logging.Log;
034    import org.apache.commons.logging.LogFactory;
035    import org.xml.sax.Attributes;
036    import org.xml.sax.InputSource;
037    import org.xml.sax.SAXException;
038    import org.xml.sax.XMLReader;
039    import org.xml.sax.helpers.DefaultHandler;
040    
041    /**
042     * Read default values of operators from xml file.
043     * 
044     * @version $Revision: 241d8c4d0dc1 $
045     */
046    public final class OperatorDictionary2 extends AbstractOperatorDictionary
047            implements Serializable {
048    
049        /**
050         * Logger for this class.
051         */
052        private static final Log LOGGER = LogFactory
053                .getLog(OperatorDictionary2.class);
054    
055        /**
056         * 
057         */
058        private static final long serialVersionUID = 1L;
059    
060        /**
061         * MathML dictionary resource.
062         */
063        private static final String DICTIONARY_FILE = "/net/sourceforge/jeuclid/moDictionary.xml";
064    
065        /**
066         * MathML dictionary serialized resource.
067         */
068        private static final String DICTIONARY_SERIALIZED = "/net/sourceforge/jeuclid/moDictionary.ser";
069    
070        /**
071         * The instance of the Dictionary
072         */
073        private static OperatorDictionary instance;
074    
075        private OperatorDictionary2() {
076            // everything is done in superclass.
077        }
078    
079        /**
080         * Get the for singleton instance.
081         * 
082         * @return an instance of OperatorDictionary.
083         */
084        public static OperatorDictionary getInstance() {
085            synchronized (OperatorDictionary2.class) {
086                if (OperatorDictionary2.instance == null) {
087                    final OperatorDictionary newDict = AbstractOperatorDictionary
088                            .deserialize(OperatorDictionary2.DICTIONARY_SERIALIZED);
089                    if (newDict == null) {
090                        OperatorDictionary2.instance = new OperatorDictionary2();
091                    } else {
092                        OperatorDictionary2.instance = newDict;
093                    }
094                }
095            }
096            return OperatorDictionary2.instance;
097        }
098    
099        /** {@inheritDoc} */
100        @Override
101        protected void initializeFromXML(
102                final Map<OperatorAttribute, Map<String, Map<OperatorForm, String>>> dict) {
103            InputStream dictInput = null;
104            try {
105                dictInput = OperatorDictionary2.class
106                        .getResourceAsStream(OperatorDictionary2.DICTIONARY_FILE);
107                final SAXParserFactory factory = SAXParserFactory.newInstance();
108                final XMLReader reader = factory.newSAXParser().getXMLReader();
109                reader.setContentHandler(new DictionaryReader(dict));
110                reader.parse(new InputSource(dictInput));
111            } catch (final ParserConfigurationException e) {
112                OperatorDictionary2.LOGGER.warn("Cannot get SAXParser:"
113                        + e.getMessage());
114            } catch (final SAXException e) {
115                OperatorDictionary2.LOGGER
116                        .warn("SAXException while parsing dictionary:"
117                                + e.getMessage());
118            } catch (final IOException e) {
119                OperatorDictionary2.LOGGER.warn(
120                        "Read error while accessing XML dictionary", e);
121            } finally {
122                if (dictInput != null) {
123                    try {
124                        dictInput.close();
125                    } catch (final IOException io) {
126                        OperatorDictionary2.LOGGER.warn(
127                                "Error closing XML dictionary", io);
128                    }
129                }
130            }
131        }
132    
133        /**
134         * The DictionaryReader reads dictionary XML file and initializes Dictionary
135         * fields.
136         */
137        private class DictionaryReader extends DefaultHandler {
138            private static final String ELEMENT_ELEMENT = "element";
139    
140            private String currentOperator;
141    
142            private OperatorForm currentFormIndex;
143    
144            private Map<OperatorAttribute, String> currentEntry;
145            private final Map<OperatorAttribute, Map<String, Map<OperatorForm, String>>> dict;
146    
147            public DictionaryReader(
148                    final Map<OperatorAttribute, Map<String, Map<OperatorForm, String>>> d) {
149                // makes findbugs happy
150                this.dict = d;
151                this.currentEntry = null;
152            }
153    
154            @Override
155            public void startDocument() throws SAXException {
156                // nothing to do.
157            }
158    
159            @Override
160            public void endDocument() throws SAXException {
161                // nothing to do.
162            }
163    
164            @Override
165            public void startElement(final String uri, final String localName,
166                    final String rawName, final Attributes attlist)
167                    throws SAXException {
168    
169                if (rawName
170                        .equals(OperatorDictionary2.DictionaryReader.ELEMENT_ELEMENT)) {
171                    this.currentEntry = new TreeMap<OperatorAttribute, String>();
172                    final String form = attlist.getValue(Mo.ATTR_FORM);
173                    if (form == null) {
174                        // it is impossible because "form" is required attribute
175                        // for the dictionary.
176                        OperatorDictionary2.LOGGER
177                                .fatal("Error in dictionary, attribute 'form' is required attribute for the dictionary");
178                        this.currentFormIndex = OperatorForm.INFIX;
179                    } else {
180                        this.currentFormIndex = OperatorForm
181                                .parseOperatorForm(form);
182                    }
183                    for (int i = 0; i < attlist.getLength(); i++) {
184                        final String attName = attlist.getQName(i);
185                        final String attValue = attlist.getValue(i);
186                        if (!attName.equals(Mo.ATTR_FORM)) {
187                            try {
188                                this.currentEntry.put(OperatorAttribute
189                                        .parseOperatorAttribute(attName), attValue);
190                            } catch (final UnknownAttributeException e) {
191                                OperatorDictionary2.LOGGER.fatal(e.getMessage());
192                            }
193                        }
194                    }
195                }
196            }
197    
198            @Override
199            public void endElement(final String uri, final String localName,
200                    final String rawName) throws SAXException {
201                if (rawName
202                        .equals(OperatorDictionary2.DictionaryReader.ELEMENT_ELEMENT)) {
203    
204                    for (final Map.Entry<OperatorAttribute, String> attributeValues : this.currentEntry
205                            .entrySet()) {
206                        final OperatorAttribute attribute = attributeValues
207                                .getKey();
208                        final String value = attributeValues.getValue();
209                        Map<String, Map<OperatorForm, String>> mapForAttr = this.dict
210                                .get(attribute);
211                        if (mapForAttr == null) {
212                            mapForAttr = new TreeMap<String, Map<OperatorForm, String>>();
213                            this.dict.put(attribute, mapForAttr);
214                        }
215                        Map<OperatorForm, String> valueForForm = mapForAttr
216                                .get(this.currentOperator);
217                        if (valueForForm == null) {
218                            valueForForm = new EnumMap<OperatorForm, String>(
219                                    OperatorForm.class);
220                            mapForAttr.put(this.currentOperator, valueForForm);
221                        }
222                        valueForForm.put(this.currentFormIndex, value);
223                    }
224                }
225                this.currentEntry = null;
226                this.currentOperator = null;
227            }
228    
229            @Override
230            public void characters(final char[] data, final int start,
231                    final int length) throws SAXException {
232                if (this.currentEntry != null) {
233                    final char[] temp = new char[length];
234                    System.arraycopy(data, start, temp, 0, length);
235                    if (this.currentOperator == null) {
236                        this.currentOperator = new String(temp);
237                    } else {
238                        this.currentOperator += new String(temp);
239                    }
240                    this.currentOperator = this.currentOperator.trim();
241                }
242            }
243        }
244    
245    }