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.util.List;
045: import java.util.ArrayList;
046: import java.util.Collections;
047: import java.util.Comparator;
048: import java.util.HashMap;
049: import java.util.Iterator;
050: import java.util.Map;
051: import org.netbeans.spi.lexer.util.LexerUtilities;
052: import org.netbeans.modules.lexer.gen.util.LexerGenUtilities;
053:
054: /**
055: * Information about the language necessary for generating
056: * language class source. It's obtained by parsing a XML description
057: * and it can optionally be modified.
058: * <BR>There is also possibility to generate xml description
059: * describing the token ids. The generated description
060: * can then be edited and extended by e.g. adding sample text(s)
061: * or assigning token ids into token categories.
062: * The language xml description then serves
063: * as a source of extended information for the generation of the language.
064: *
065: * @author Miloslav Metelka
066: * @version 1.00
067: */
068:
069: public class LanguageData {
070:
071: public static final Comparator IDS_FIELD_NAME_COMPARATOR = new IdsFieldNameComparator();
072:
073: public static final Comparator IDS_INT_ID_COMPARATOR = new IdsIntIdComparator();
074:
075: private TokenTypes tokenTypes;
076:
077: private String languageClassName;
078:
079: private String lexerClassName;
080:
081: private List ids;
082:
083: private List unmodifiableIds;
084:
085: private int uniqueIntId;
086:
087: public LanguageData() {
088: /* Start with 1 to ensure the 0 is free
089: * for yacc-like tools that use 0 for EOF.
090: */
091: uniqueIntId = 1;
092: ids = new ArrayList();
093: unmodifiableIds = Collections.unmodifiableList(ids);
094: }
095:
096: /**
097: * Get the token-types registered with this language data.
098: */
099: public final TokenTypes getTokenTypes() {
100: return tokenTypes;
101: }
102:
103: /**
104: * Register the token-types into this language data
105: * and apply them to this language data.
106: */
107: public void registerTokenTypes(TokenTypes tokenTypes) {
108: this .tokenTypes = tokenTypes;
109:
110: // Let the token types update this data
111: tokenTypes.updateData(this );
112: }
113:
114: /**
115: * @return class name of the generated language class without package.
116: */
117: public String getLanguageClassName() {
118: return languageClassName;
119: }
120:
121: public void setLanguageClassName(String languageClassName) {
122: this .languageClassName = languageClassName;
123: }
124:
125: /**
126: * @return full classname of the generated lexer class (including package).
127: */
128: public String getLexerClassName() {
129: return lexerClassName;
130: }
131:
132: public void setLexerClassName(String lexerClassName) {
133: this .lexerClassName = lexerClassName;
134: }
135:
136: /**
137: * @return unmodifiable list of tokenIds planned to be generated.
138: * To create a new mutable tokenId the {@link #newId(String)}
139: * can be used.
140: */
141: public List getIds() {
142: return unmodifiableIds;
143: }
144:
145: /**
146: * Get copy of ids list sorted by a specified comparator.
147: * @param c comparator to be used. {@link #IDS_FIELD_NAME_COMPARATOR}
148: * or {@link #IDS_INT_ID_COMPARATOR} can be used among others.
149: * @return copy of ids list sorted by a specified comparator.
150: */
151: public List getSortedIds(Comparator c) {
152: List idsCopy = new ArrayList(ids);
153: Collections.sort(idsCopy, c);
154: return idsCopy;
155: }
156:
157: /**
158: * Create new mutable token id and add it to the list of the current ids.
159: * @return created and added mutable token id.
160: */
161: public MutableTokenId newId(String name) {
162: if (findId(name) != null) {
163: throw new IllegalArgumentException("Id named " + name
164: + " already exists.");
165: }
166:
167: MutableTokenId id = createId(name);
168: ids.add(id);
169:
170: return id;
171: }
172:
173: /**
174: * Remove the given id from the list of the token ids.
175: */
176: public void remove(MutableTokenId id) {
177: for (Iterator it = ids.iterator(); it.hasNext();) {
178: if (id == it.next()) {
179: it.remove();
180: return;
181: }
182: }
183: throw new IllegalArgumentException("id=" + id + " not found");
184: }
185:
186: /**
187: * Find the mutable tokenId with the given name.
188: * @return mutable token id with the given name.
189: */
190: public MutableTokenId findId(String name) {
191: for (Iterator it = getIds().iterator(); it.hasNext();) {
192: MutableTokenId id = (MutableTokenId) it.next();
193: if (name.equals(id.getName())) {
194: return id;
195: }
196: }
197: return null;
198: }
199:
200: /**
201: * Find the mutable tokenId by its integer identification.
202: * @param intId integer identification of the tokenId.
203: * @return mutable tokenId with given integer identification.
204: */
205: public MutableTokenId findId(int intId) {
206: List ids = getIds();
207: int cnt = ids.size();
208: for (int i = 0; i < cnt; i++) {
209: MutableTokenId id = (MutableTokenId) ids.get(i);
210: if (id.getIntId() == intId) {
211: return id;
212: }
213: }
214: return null;
215: }
216:
217: /**
218: * Find the mutable tokenId by its token type name returned
219: * by {@link MutableTokenId#getTokenTypeName()}.
220: * @param tokenTypeName name of the field in the token-types class.
221: * @return mutable tokenId with the given tokenTypeName.
222: */
223: public MutableTokenId findIdByTokenTypeName(String tokenTypeName) {
224: List ids = getIds();
225: int cnt = ids.size();
226: for (int i = 0; i < cnt; i++) {
227: MutableTokenId id = (MutableTokenId) ids.get(i);
228: if (tokenTypeName.equals(id.getTokenTypeName())) {
229: return id;
230: }
231: }
232: return null;
233: }
234:
235: /**
236: * @return maximum intId among all the tokenIds.
237: * <BR><B>Note:</B>It's necessary that all the tokenIds
238: * have the non-numeric tokenIds resolved to numbers
239: * prior invocation of this method.<B>
240: * The {@link #findNonNumericIntIds()} can be used to find those.
241: * The non-numeric intIds are silently ignored by the method.
242: */
243: public int findMaxIntId() {
244: List ids = getIds();
245: int cnt = ids.size();
246: int maxIntId = 0;
247: for (int i = 0; i < cnt; i++) {
248: MutableTokenId id = (MutableTokenId) ids.get(i);
249: maxIntId = Math.max(maxIntId, id.getIntId());
250: }
251:
252: return maxIntId;
253: }
254:
255: /**
256: * @return unique intId not yet used for other intIds.
257: */
258: public int uniqueIntId() {
259: return uniqueIntId++;
260: }
261:
262: /**
263: * Skip the given intId (and all intIds that are lower than it)
264: * so they will not be returned by {@link #uniqueIntId()}.
265: * @param intId id to skip.
266: */
267: public void skipIntId(int intId) {
268: uniqueIntId = Math.max(uniqueIntId, ++intId);
269: }
270:
271: /**
272: * @return newly created mutable tokenId instance.
273: * <BR>Subclasses may override this method to return
274: * subclass of {@link MutableTokenId).
275: */
276: protected MutableTokenId createId(String name) {
277: return new MutableTokenId(this , name);
278: }
279:
280: /**
281: * Generate unique intIds for the tokenIds that have
282: * intId set to implicit value -1.
283: * <BR>The {@link #uniqueIntId()} is used to obtain
284: * the intIds.
285: */
286: public void updateUnassignedIntIds() {
287: for (Iterator it = getIds().iterator(); it.hasNext();) {
288: MutableTokenId id = (MutableTokenId) it.next();
289: if (id.getIntId() == -1) {
290: id.assignUniqueIntId();
291: }
292: }
293: }
294:
295: /**
296: * Check whether this language data object is in a consistent state.
297: * The test includes checking for duplicate intIds.
298: */
299: public void check() {
300: Map info = new HashMap();
301:
302: for (Iterator it = getIds().iterator(); it.hasNext();) {
303: MutableTokenId id = (MutableTokenId) it.next();
304: int intId = id.getIntId();
305: if (intId < 0) {
306: throw new IllegalStateException("Id " + id.getName()
307: + " has invalid intId=" + intId
308: + "\nLanguage data dump follows:\n"
309: + toString()
310:
311: );
312: }
313:
314: MutableTokenId dupIntId = (MutableTokenId) info.put(
315: new Integer(intId), id);
316: if (dupIntId != null) {
317: throw new IllegalStateException("Ids " + id.getName()
318: + " and " + dupIntId.getName()
319: + " have the same intId=" + intId
320: + "\nLanguage data dump follows:\n"
321: + toString());
322: }
323: }
324: }
325:
326: public String toString() {
327: StringBuffer sb = new StringBuffer();
328: List mtids = getIds();
329: int mtidsCount = mtids.size();
330: for (int i = 0; i < mtidsCount; i++) {
331: sb.append(mtids.get(i));
332: sb.append('\n');
333: }
334:
335: return sb.toString();
336: }
337:
338: /**
339: * Produce textual xml description for the integer token constants fields
340: * found in the token constants class file.
341: * <BR>This description can be used as a skeleton for the future
342: * xml description file.
343: */
344: public String createDescription() {
345:
346: StringBuffer sb = new StringBuffer();
347: appendDescriptionStart(sb);
348:
349: for (Iterator idsIterator = getIds().iterator(); idsIterator
350: .hasNext();) {
351:
352: MutableTokenId id = (MutableTokenId) idsIterator.next();
353: String idName = id.getName();
354: if (idName != null) {
355: LexerGenUtilities.appendSpaces(sb, 4);
356: sb.append("<TokenId name=\"");
357: sb.append(LexerGenUtilities.idToLowerCase(idName));
358: sb.append("\">\n");
359: LexerGenUtilities.appendSpaces(sb, 4);
360: sb.append("</TokenId>\n");
361: }
362: }
363:
364: appendDescriptionEnd(sb);
365:
366: return sb.toString();
367: }
368:
369: protected void appendDescriptionStart(StringBuffer sb) {
370: sb.append("<?xml version=\"1.0\"?>\n");
371: sb.append("<!DOCTYPE Language SYSTEM ");
372: sb.append("\"???/src/org/netbeans/lexer/language.dtd\">\n");
373: sb.append("<Language>\n");
374: }
375:
376: protected void appendDescriptionEnd(StringBuffer sb) {
377: sb.append("</Language>\n");
378: }
379:
380: private static final class IdsFieldNameComparator implements
381: Comparator {
382:
383: public int compare(Object o1, Object o2) {
384: MutableTokenId id1 = (MutableTokenId) o1;
385: MutableTokenId id2 = (MutableTokenId) o2;
386: return id1.getFieldName().compareTo(id2.getFieldName());
387: }
388:
389: }
390:
391: private static final class IdsIntIdComparator implements Comparator {
392:
393: public int compare(Object o1, Object o2) {
394: MutableTokenId id1 = (MutableTokenId) o1;
395: MutableTokenId id2 = (MutableTokenId) o2;
396: return id1.getIntId() - id2.getIntId();
397: }
398:
399: }
400:
401: }
|