001: /*
002: * JacORB - a free Java ORB
003: *
004: * Copyright (C) 1997-2004 Gerald Brose.
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Library General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Library General Public License for more details.
015: *
016: * You should have received a copy of the GNU Library General Public
017: * License along with this library; if not, write to the Free
018: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
019: */
020:
021: package org.jacorb.idl;
022:
023: /**
024: * A table of defined names
025: *
026: * @author Gerald Brose
027: * @version $Id: NameTable.java,v 1.27 2006/07/11 14:16:07 nick.cross Exp $
028: */
029:
030: import java.util.Enumeration;
031: import java.util.Hashtable;
032: import java.util.Map;
033: import java.util.StringTokenizer;
034:
035: public class NameTable {
036: private static final Hashtable names = new Hashtable(10000);
037:
038: private static final Map shadows = new Hashtable();
039:
040: private static final Map ancestors = new Hashtable();
041:
042: /**
043: key: operation name,
044: value: interface this operation was originally defined in
045: necessary to track legal diamond inheritance of operations
046: */
047:
048: private static final Map operationSources = new Hashtable();
049:
050: public static final Map parsed_interfaces = new Hashtable();
051:
052: static org.apache.log.Logger logger;
053:
054: public static void init() {
055: names.clear();
056: operationSources.clear();
057: shadows.clear();
058: ancestors.clear();
059:
060: operationSources.clear();
061: parsed_interfaces.clear();
062:
063: names.put("char", "type");
064: names.put("boolean", "type");
065: names.put("long", "type");
066: names.put("long", "type");
067: names.put("short", "type");
068: names.put("int", "type");
069: names.put("float", "type");
070: names.put("double", "type");
071: names.put("byte", "type");
072: names.put("void", "type");
073: names.put("org.omg.CORBA.Any", "type");
074: names.put("org.omg.CORBA.Object", "interface");
075:
076: logger = parser.getLogger();
077: }
078:
079: /**
080: * check IDL scoping rules
081: * @throws NameAlreadyDefined, or the derived IllegalRedefinition
082: */
083:
084: private static void checkScopingRules(String name, String kind)
085: throws NameAlreadyDefined {
086: if (logger.isDebugEnabled()) {
087: logger.debug("NameTable.checkScopingRules: " + name
088: + " kind: " + kind);
089: }
090:
091: if (kind.equals("argument")) {
092: return; // no checks in outer scopes ???
093: }
094:
095: StringTokenizer strtok = new StringTokenizer(
096: name.toUpperCase(), ".");
097:
098: String scopes[] = new String[strtok.countTokens()];
099:
100: for (int i = 0; strtok.hasMoreTokens(); i++) {
101: scopes[i] = strtok.nextToken();
102: }
103:
104: if (logger.isDebugEnabled()) {
105: logger.debug("NameTable.checkScopingRules2: " + name
106: + " kind: " + kind);
107: }
108:
109: if (scopes.length > 1
110: && scopes[scopes.length - 2]
111: .equals(scopes[scopes.length - 1])) {
112: throw new IllegalRedefinition(name);
113: }
114: }
115:
116: /**
117: * define a name. If it has already been defined in this scope,
118: * an exception is thrown
119: *
120: * @param name The name to be defined
121: * @param kind the type of name, e.g. "type"
122: * @throws NameAlreadyDefined if the name is already defined
123: */
124:
125: public static void define(String name, String kind)
126: throws NameAlreadyDefined {
127: if (logger.isInfoEnabled()) {
128: logger.info("NameTable.define2: putting " + name + " kind "
129: + kind + " hash: " + name.hashCode());
130: }
131:
132: /* check also for the all uppercase version of this name,
133: (which is also reserved to block identifiers that
134: only differ in case) */
135:
136: if (names.containsKey(name)
137: || names.containsKey(name.toUpperCase())) {
138: // if this name has been inherited, it is "shadowed"
139: // in this case, it is redefined if it is not an operation
140: // or interface name. If it has been
141: // explicitly defined in this scope, we have an error
142:
143: if (kind.equals("module")) {
144: // modules may be "reopened", no further checks or table entries
145: return;
146: }
147: // This check ensures that we can't redefine a name with a different type.
148: // We also need to ignore any pending forward declarations.
149: else if (names.containsKey(name)
150: && !names.get(name).equals(kind)
151: && parser.get_pending(name) == null) {
152: throw new IllegalRedefinition(name);
153: } else if (!shadows.containsKey(name)
154: || kind.equals("operation")
155: || kind.equals("interface")) {
156: throw new NameAlreadyDefined(name);
157: } else {
158: // redefine
159: if (logger.isInfoEnabled()) {
160: logger.info("NameTable.define2: redefining "
161: + name);
162: }
163:
164: shadows.remove(name);
165: names.remove(name);
166:
167: if (kind.startsWith("type")) {
168: // remove the inherited type definition, a new one will be
169: // added soon under this name! Addition of this line fixes
170: // bug #345
171: TypeMap.removeDefinition(name);
172: }
173: }
174: }
175:
176: if (org.jacorb.idl.parser.strict_names) {
177: checkScopingRules(name, kind);
178: }
179:
180: names.put(name, kind);
181:
182: /* block identifiers that only differ in case */
183: if (org.jacorb.idl.parser.strict_names) {
184: names.put(name.toUpperCase(), "dummy");
185: }
186:
187: if (kind.equals("operation")) {
188: operationSources.put(name, name.substring(0, name
189: .lastIndexOf(".")));
190: }
191: }
192:
193: private static void defineInheritedOperation(String name,
194: String inheritedFrom) throws NameAlreadyDefined {
195:
196: if (names.containsKey(name)) {
197: String source = null;
198: String opName = (name.indexOf(".") < 0 ? name : name
199: .substring(name.lastIndexOf(".") + 1));
200: String presentOpName = name;
201:
202: while ((source = (String) operationSources
203: .get(presentOpName)) != null) {
204: if (presentOpName.equals(source + "." + opName)) {
205: break;
206: }
207: presentOpName = source + "." + opName;
208:
209: }
210: if (logger.isInfoEnabled()) {
211: logger.info("NameTable source of " + name + " is "
212: + presentOpName);
213: }
214:
215: String otherOpName = inheritedFrom + "." + opName;
216:
217: while ((source = (String) operationSources.get(otherOpName)) != null) {
218: if (otherOpName.equals(source + "." + opName)) {
219: break;
220: }
221: otherOpName = source + "." + opName;
222: }
223: if (logger.isInfoEnabled()) {
224: logger.info("NameTable other source of " + name
225: + " is " + otherOpName);
226: }
227:
228: if (otherOpName.equals(presentOpName)) {
229: // found an operation that is inherited from
230: // the same ultimate base via different paths
231: // do nothing, as it is already defined for this
232: // interface
233: return;
234: }
235: // illegal multiple inheritance of a the same op name
236: throw new NameAlreadyDefined(name);
237: }
238: names.put(name, "operation");
239: operationSources.put(name, inheritedFrom);
240: }
241:
242: /**
243: * define a shadowed name, i.e. an inherited name
244: * @throws NameAlreadyDefined if a name is already defined
245: */
246:
247: private static void defineShadows(Hashtable shadowEntries)
248: throws NameAlreadyDefined {
249: String firstViolation = null;
250: for (Enumeration e = shadowEntries.keys(); e.hasMoreElements();) {
251: String name = (String) e.nextElement();
252: String kind = (String) shadowEntries.get(name);
253: if (names.containsKey(name)) {
254: firstViolation = name;
255: } else {
256: names.put(name, kind);
257: if (logger.isDebugEnabled()) {
258: logger.debug("Put shadow " + name);
259: }
260: shadows.put(name, "");
261: if (kind.equals("operation")) {
262: operationSources.put(name, name.substring(0, name
263: .lastIndexOf(".")));
264: }
265: }
266: }
267: if (firstViolation != null) {
268: throw new NameAlreadyDefined(firstViolation);
269: }
270: }
271:
272: /**
273: * copy names declared in an ancestor interface to the local scope
274: * @throws NameAlreadyDefined
275: */
276:
277: public static synchronized void inheritFrom(String name,
278: SymbolList ancestors) throws NameAlreadyDefined {
279: Hashtable shadowNames = new Hashtable();
280: for (Enumeration e = names.keys(); e.hasMoreElements();) {
281: String key = (String) e.nextElement();
282: String s = null;
283: if (key.indexOf('.') > 0) {
284: s = key.substring(0, key.lastIndexOf('.'));
285: } else {
286: continue;
287: }
288:
289: for (Enumeration i = ancestors.v.elements(); i
290: .hasMoreElements();) {
291: String anc = ((ScopedName) (i.nextElement()))
292: .resolvedName();
293: if (s.equals(anc)) {
294: String kind = (String) names.get(key);
295: if (logger.isDebugEnabled()) {
296: logger.debug("NameTable.inheritFrom ancestor "
297: + anc + " : key " + key + " kind "
298: + kind);
299: }
300:
301: String shadowKey = name
302: + key.substring(key.lastIndexOf('.'));
303: shadowNames.put(shadowKey, kind);
304:
305: // if the name we inherit is a typedef'd name, we need
306: // to typedef the inherited name as well
307:
308: if (kind.startsWith("type")) {
309: if (logger.isDebugEnabled())
310: logger
311: .debug("- NameTable.inherit type from: "
312: + key);
313:
314: TypeSpec t = TypeMap.map(anc
315: + key.substring(key.lastIndexOf('.')));
316:
317: // t can be null for some cases where we had to put
318: // Java type names (e.g. for sequence s) into the
319: // name table. These need not be typedef'd again here
320:
321: if (t != null) {
322: TypeMap.typedef(name
323: + key.substring(key
324: .lastIndexOf('.')), t);
325: }
326: shadowNames.put(name
327: + key.substring(key.lastIndexOf('.')),
328: kind);
329: } else if (kind.equals("operation")) {
330: if (logger.isDebugEnabled())
331: logger
332: .debug("- NameTable.inherit operation from: "
333: + key);
334:
335: NameTable.defineInheritedOperation(name
336: + key.substring(key.lastIndexOf('.')),
337: anc);
338: } else {
339: if (logger.isDebugEnabled()) {
340: logger.debug("- NameTable.inherit " + kind
341: + " from: " + key);
342: }
343: }
344:
345: if (!isDefined(key)) {
346: throw new RuntimeException("CompilerError!");
347: }
348: }
349: }
350: }
351:
352: /* update the hashtable */
353:
354: try {
355: defineShadows(shadowNames);
356: } catch (NameAlreadyDefined nad) {
357: if (logger.isDebugEnabled())
358: logger.debug("Exception ", nad);
359: }
360: }
361:
362: /**
363: * check whether name is already defined
364: */
365:
366: public static boolean isDefined(String name) {
367: return (names.containsKey(name));
368: }
369:
370: public static boolean isDefined(String name, String kind) {
371: if (!names.containsKey(name)) {
372: return false;
373: }
374: String k = (String) names.get(name);
375: return (k.compareTo(kind) == 0);
376: }
377:
378: static boolean baseType(String _s) {
379: return (_s.equals("int") || _s.equals("short")
380: || _s.equals("long") || _s.equals("float")
381: || _s.equals("boolean") || _s.equals("double")
382: || _s.equals("byte") || _s.equals("char")
383: || _s.equals("void")
384: || _s.equals("org.omg.CORBA.Object")
385: || _s.equals("org.omg.CORBA.Any") || _s
386: .equals("<anon>"));
387: }
388: }
|