001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.lexer.gen;
043:
044: import java.lang.reflect.Field;
045: import java.io.IOException;
046: import java.util.ArrayList;
047: import java.util.Arrays;
048: import java.util.HashMap;
049: import java.util.Set;
050: import java.util.List;
051:
052: import javax.xml.parsers.SAXParser;
053: import javax.xml.parsers.SAXParserFactory;
054: import javax.xml.parsers.ParserConfigurationException;
055:
056: import org.xml.sax.Locator;
057: import org.xml.sax.Attributes;
058: import org.xml.sax.XMLReader;
059: import org.xml.sax.SAXException;
060: import org.xml.sax.SAXParseException;
061: import org.xml.sax.InputSource;
062: import org.xml.sax.helpers.DefaultHandler;
063:
064: import org.netbeans.api.lexer.TokenId;
065: import org.netbeans.modules.lexer.gen.util.LexerGenUtilities;
066:
067: /**
068: * Updates the language data by reading and interpreting
069: * a given xml file with the language description updates.
070: * <BR>The xml file is read in two rounds. In the first
071: * round all the hidden token types elements
072: * are interpreted. In the second round the rest
073: * of the elements are interpreted.
074: *
075: * @author Miloslav Metelka
076: * @version 1.00
077: */
078:
079: public class DescriptionReader extends DefaultHandler {
080:
081: // Elements names
082: private static final String LANGUAGE_ELEM = "Language";
083: private static final String TOKEN_ID_ELEM = "TokenId";
084: private static final String HIDDEN_TOKEN_TYPE_ELEM = "HiddenTokenType";
085: private static final String CATEGORY_ELEM = "Category";
086: private static final String SAMPLE_TEXT_ELEM = "SampleText";
087: private static final String COMMENT_ELEM = "Comment";
088:
089: private static final String NAME_ATTR = "name";
090: private static final String TOKEN_TYPE_ATTR = "tokenType";
091: private static final String SAMPLE_TEXT_CHECK_ATTR = "sampleTextCheck";
092: private static final String CASE_INSENSITIVE_ATTR = "caseInsensitive";
093: private static final String RESET_SAMPLES_ATTR = "resetSamples";
094:
095: private String systemId;
096:
097: /** Active language data into which the input xml is parsed. */
098: protected LanguageData languageData;
099:
100: /**
101: * Whether processing HiddenTokenType elements (1st round)
102: * or the rest of the elements (2nd round).
103: */
104: private boolean processingHiddenTokenTypes;
105:
106: /** Currently processed tokenId. */
107: protected MutableTokenId id;
108:
109: /* Currently in Comment element. */
110: private boolean inCommentElement;
111:
112: /* Currently in SampleText element. */
113: private boolean inSampleTextElement;
114:
115: /**
116: * Create the description reader over the given systemId.
117: * @param systemId identification of the source xml language description file.
118: */
119: public DescriptionReader(String systemId) {
120: this .systemId = systemId;
121: }
122:
123: /**
124: * Parse the xml determined by systemId and update the languageData.
125: * @param languageData update the language data by updating/adding mutable
126: * tokenIds. If there is an TokenId element with a name not yet present
127: * in the languageData it will be added. Otherwise the attributes of existing
128: * mutable tokenId will be updated.
129: * @throws javax.xml.parsers.SAXException (also encapsulates ParserConfigurationException)
130: * and IOException
131: */
132: public synchronized void applyTo(LanguageData languageData)
133: throws SAXException, IOException {
134:
135: this .languageData = languageData;
136:
137: try {
138: SAXParser parser = SAXParserFactory.newInstance()
139: .newSAXParser();
140:
141: processingHiddenTokenTypes = true;
142:
143: XMLReader reader = parser.getXMLReader();
144: reader.setContentHandler(this );
145: reader.parse(new InputSource(systemId)); // process hidden token types only
146:
147: processingHiddenTokenTypes = false;
148:
149: reader.parse(new InputSource(systemId)); // process tokenId elements
150:
151: } catch (ParserConfigurationException e) {
152: throw new SAXException(e);
153: }
154:
155: this .languageData = null;
156: }
157:
158: public void startElement(String uri, String localName,
159: String qname, Attributes attributes) throws SAXException {
160: if (LANGUAGE_ELEM.equals(qname)) {
161: // no info
162:
163: } else if (TOKEN_ID_ELEM.equals(qname)) {
164: if (!processingHiddenTokenTypes) {
165: // Create a new tokenId or start updating the existing one
166: String name = empty2nullFromSource(attributes
167: .getValue(NAME_ATTR));
168: id = languageData.findId(name);
169: if (id == null) {
170: id = languageData.newId(name);
171: }
172:
173: // Possibly update the tokenId by the given token type
174: String tokenTypeName = empty2nullFromSource(attributes
175: .getValue(TOKEN_TYPE_ATTR));
176: if (tokenTypeName != null) {
177: id.updateByTokenType(tokenTypeName);
178: }
179:
180: // Possibly reset the existing samples (usually got from token type)
181: if (toBoolean(attributes.getValue(RESET_SAMPLES_ATTR))) {
182: id.resetSamples();
183: }
184:
185: // Update case insensitivity
186: id.setCaseInsensitive(toBoolean(attributes
187: .getValue(CASE_INSENSITIVE_ATTR)));
188:
189: // Possibly update type of sample text checking
190: String stc = empty2nullFromSource(attributes
191: .getValue(SAMPLE_TEXT_CHECK_ATTR));
192: if (stc != null) {
193: id.setSampleTextCheck(stc);
194: }
195: }
196:
197: } else if (HIDDEN_TOKEN_TYPE_ELEM.equals(qname)) {
198: if (processingHiddenTokenTypes) {
199: String tokenTypeName = empty2nullFromSource(attributes
200: .getValue(NAME_ATTR));
201: MutableTokenId id = languageData
202: .findIdByTokenTypeName(tokenTypeName);
203: if (id != null) {
204: languageData.remove(id);
205: }
206: }
207:
208: } else if (CATEGORY_ELEM.equals(qname)) {
209: if (!processingHiddenTokenTypes) {
210: id.getCategoryNames().add(
211: attributes.getValue(NAME_ATTR));
212: }
213:
214: } else if (COMMENT_ELEM.equals(qname)) {
215: if (!processingHiddenTokenTypes) {
216: inCommentElement = true;
217: }
218:
219: } else if (SAMPLE_TEXT_ELEM.equals(qname)) {
220: if (!processingHiddenTokenTypes) {
221: inSampleTextElement = true;
222: }
223:
224: } else {
225: throw new IllegalStateException("Unknown element qname="
226: + qname);
227: }
228:
229: }
230:
231: /** End element. */
232: public void endElement(String uri, String localName, String qname) {
233: if (TOKEN_ID_ELEM.equals(qname)) {
234: if (!processingHiddenTokenTypes) {
235: id = null;
236: }
237:
238: } else if (COMMENT_ELEM.equals(qname)) {
239: if (!processingHiddenTokenTypes) {
240: inCommentElement = false;
241: }
242:
243: } else if (SAMPLE_TEXT_ELEM.equals(qname)) {
244: if (!processingHiddenTokenTypes) {
245: inSampleTextElement = false;
246: }
247: }
248: }
249:
250: /** Characters in element */
251: public void characters(char ch[], int start, int length)
252: throws SAXException {
253: if (id != null) {
254: if (inCommentElement) {
255: String comment = empty2nullFromSource(new String(ch,
256: start, length));
257: if (comment != null) {
258: id.setComment(comment);
259: }
260:
261: } else if (inSampleTextElement) {
262: if (length > 0) { // only non-empty SampleTexts are supported
263: id.addSampleText(empty2nullFromSource(new String(
264: ch, start, length)));
265: }
266: }
267: }
268: }
269:
270: private static String empty2null(String s) {
271: if ("".equals(s)) {
272: s = null;
273: }
274:
275: return s;
276: }
277:
278: private static String empty2nullFromSource(String s) {
279: s = empty2null(s);
280: if (s != null) {
281: s = LexerGenUtilities.fromSource(s);
282: }
283: return s;
284: }
285:
286: private static boolean toBoolean(String s) {
287: return "true".equals(s);
288: }
289:
290: }
|