summaryrefslogtreecommitdiffstats
path: root/sca-java-1.x/branches/sca-java-0.91/modules/binding-ejb/src/main/java/org/apache/tuscany/sca/binding/ejb/java2idl/ContainerType.java
diff options
context:
space:
mode:
Diffstat (limited to 'sca-java-1.x/branches/sca-java-0.91/modules/binding-ejb/src/main/java/org/apache/tuscany/sca/binding/ejb/java2idl/ContainerType.java')
-rw-r--r--sca-java-1.x/branches/sca-java-0.91/modules/binding-ejb/src/main/java/org/apache/tuscany/sca/binding/ejb/java2idl/ContainerType.java791
1 files changed, 791 insertions, 0 deletions
diff --git a/sca-java-1.x/branches/sca-java-0.91/modules/binding-ejb/src/main/java/org/apache/tuscany/sca/binding/ejb/java2idl/ContainerType.java b/sca-java-1.x/branches/sca-java-0.91/modules/binding-ejb/src/main/java/org/apache/tuscany/sca/binding/ejb/java2idl/ContainerType.java
new file mode 100644
index 0000000000..18216db742
--- /dev/null
+++ b/sca-java-1.x/branches/sca-java-0.91/modules/binding-ejb/src/main/java/org/apache/tuscany/sca/binding/ejb/java2idl/ContainerType.java
@@ -0,0 +1,791 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.tuscany.sca.binding.ejb.java2idl;
+
+import java.io.Externalizable;
+import java.io.ObjectStreamClass;
+import java.io.Serializable;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.rmi.RemoteException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+/**
+ * Common base class of ValueType and InterfaceType.
+ */
+public abstract class ContainerType extends ClassType {
+
+ /** Flags a method as overloaded. */
+ protected final byte M_OVERLOADED = 1;
+ /** Flags a method as the accessor of a read-write property. */
+ protected final byte M_READ = 2;
+ /** Flags a method as the mutator of a read-write property. */
+ protected final byte M_WRITE = 4;
+ /** Flags a method as the accessor of a read-only property. */
+ protected final byte M_READONLY = 8;
+ /** Flags a method as being inherited. */
+ protected final byte M_INHERITED = 16;
+ /**
+ * Flags a method as being the writeObject() method used for serialization.
+ */
+ protected final byte M_WRITEOBJECT = 32;
+ /** Flags a field as being a constant (public final static). */
+ protected final byte F_CONSTANT = 1;
+ /**
+ * Flags a field as being the special <code> public final static
+ * java.io.ObjectStreamField[] serialPersistentFields</code>
+ * field.
+ */
+ protected final byte F_SPFFIELD = 2;
+
+ /**
+ * Array of all java methods.
+ */
+ protected Method[] methods;
+ /**
+ * Array with flags for all java methods.
+ */
+ protected byte[] m_flags;
+ /**
+ * Index of the mutator for read-write attributes. Only entries
+ * <code>i</code> where <code>(m_flags[i]&M_READ) != 0</code> are used.
+ * These entries contain the index of the mutator method corresponding to
+ * the accessor method.
+ */
+ protected int[] setters;
+ /**
+ * Array of all java fields.
+ */
+ protected Field[] fields;
+ /**
+ * Array with flags for all java fields.
+ */
+ protected byte[] f_flags;
+ /**
+ * The class hash code, as specified in "The Common Object Request Broker:
+ * Architecture and Specification" (01-02-33), section 10.6.2.
+ */
+ protected long classHashCode = 0;
+ /**
+ * The repository ID. This is in the RMI hashed format, like
+ * "RMI:java.util.Hashtable:C03324C0EA357270:13BB0F25214AE4B8".
+ */
+ protected String repositoryId;
+ /**
+ * The prefix and postfix of members repository ID. These are used to
+ * calculate member repository IDs and are like "RMI:java.util.Hashtable."
+ * and ":C03324C0EA357270:13BB0F25214AE4B8".
+ */
+ protected String memberPrefix, memberPostfix;
+ /**
+ * Array of type of the interfaces implemented/extended here.
+ */
+ protected InterfaceType[] interfaces;
+ /**
+ * Array of type of the abstract base valuetypes implemented/extended here.
+ */
+ protected ValueType[] abstractBaseValuetypes;
+ /**
+ * Array of attributes.
+ */
+ protected AttributeType[] attributes;
+ /**
+ * Array of Constants.
+ */
+ protected ConstantType[] constants;
+ /**
+ * Array of operations.
+ */
+ protected OperationType[] operations;
+
+ /**
+ * A cache for the fully qualified IDL name of the IDL module we belong to.
+ */
+ private String idlModuleName = null;
+
+ protected ContainerType(Class cls) {
+ super(cls);
+ if (cls == java.lang.Object.class || cls == java.io.Serializable.class || cls == java.io.Externalizable.class)
+ throw new IllegalArgumentException("Cannot parse special class: " + cls.getName());
+ this.javaClass = cls;
+ }
+
+ protected void parse() {
+ parseInterfaces();
+ parseMethods();
+ parseFields();
+ calculateClassHashCode();
+ calculateRepositoryId();
+ parseAttributes();
+ parseConstants();
+ parseOperations();
+ fixupOverloadedOperationNames();
+ }
+
+ /**
+ * Return the interfaces.
+ */
+ public InterfaceType[] getInterfaces() {
+ return (InterfaceType[])interfaces.clone();
+ }
+
+ /**
+ * Return the abstract base valuetypes.
+ */
+ public ValueType[] getAbstractBaseValuetypes() {
+ return (ValueType[])abstractBaseValuetypes.clone();
+ }
+
+ /**
+ * Return the attributes.
+ */
+ public AttributeType[] getAttributes() {
+ return (AttributeType[])attributes.clone();
+ }
+
+ /**
+ * Return the constants.
+ */
+ public ConstantType[] getConstants() {
+ return (ConstantType[])constants.clone();
+ }
+
+ /**
+ * Return the operations.
+ */
+ public OperationType[] getOperations() {
+ return (OperationType[])operations.clone();
+ }
+
+ /**
+ * Return the repository ID.
+ */
+ public String getRepositoryId() {
+ return repositoryId;
+ }
+
+ /**
+ * Return a repository ID for a member.
+ *
+ * @param memberName The Java name of the member.
+ */
+ public String getMemberRepositoryId(String memberName) {
+ return memberPrefix + escapeIRName(memberName) + memberPostfix;
+ }
+
+ /**
+ * Return the fully qualified IDL module name that this type should be
+ * placed in.
+ */
+ public String getIDLModuleName() {
+ if (idlModuleName == null) {
+ String pkgName = javaClass.getPackage().getName();
+ StringBuffer b = new StringBuffer();
+ while (!"".equals(pkgName)) {
+ int idx = pkgName.indexOf('.');
+ String n = (idx == -1) ? pkgName : pkgName.substring(0, idx);
+ b.append("::").append(IDLUtil.javaToIDLName(n));
+ pkgName = (idx == -1) ? "" : pkgName.substring(idx + 1);
+ }
+ idlModuleName = b.toString();
+ }
+ return idlModuleName;
+ }
+
+ // Protected -----------------------------------------------------
+ /**
+ * Convert an integer to a 16-digit hex string.
+ */
+ protected String toHexString(int i) {
+ String s = Integer.toHexString(i).toUpperCase();
+ if (s.length() < 8)
+ return "00000000".substring(0, 8 - s.length()) + s;
+ else
+ return s;
+ }
+
+ /**
+ * Convert a long to a 16-digit hex string.
+ */
+ protected String toHexString(long l) {
+ String s = Long.toHexString(l).toUpperCase();
+ if (s.length() < 16)
+ return "0000000000000000".substring(0, 16 - s.length()) + s;
+ else
+ return s;
+ }
+
+ /**
+ * Check if a method is an accessor.
+ */
+ protected boolean isReadMethod(Method m) {
+ Class returnType = m.getReturnType();
+ if (!m.getName().startsWith("get"))
+ if (!m.getName().startsWith("is") || !(returnType == boolean.class))
+ return false;
+ if (returnType == void.class)
+ return false;
+ if (m.getParameterTypes().length != 0)
+ return false;
+ return hasNonAppExceptions(m);
+ }
+
+ /**
+ * Check if a method is a mutator.
+ */
+ protected boolean isWriteMethod(Method m) {
+ if (!m.getName().startsWith("set"))
+ return false;
+ if (m.getReturnType() != void.class)
+ return false;
+ if (m.getParameterTypes().length != 1)
+ return false;
+ return hasNonAppExceptions(m);
+ }
+
+ private static boolean isCheckedException(Class type) {
+ /*
+ * Is a checked exception
+ */
+ if (!Throwable.class.isAssignableFrom(type))
+ return false;
+
+ if (Error.class.isAssignableFrom(type))
+ return false;
+
+ if (RuntimeException.class.isAssignableFrom(type))
+ return false;
+ return true;
+ }
+
+ /**
+ * Check if a method throws anything checked other than
+ * java.rmi.RemoteException and its subclasses.
+ */
+ protected boolean hasNonAppExceptions(Method m) {
+ Class[] ex = m.getExceptionTypes();
+ for (int i = 0; i < ex.length; ++i) {
+ if (!isCheckedException(ex[i]))
+ continue;
+ if (!RemoteException.class.isAssignableFrom(ex[i]))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Analyse the fields of the class. This will fill in the
+ * <code>fields</code> and <code>f_flags</code> arrays.
+ */
+ protected void parseFields() {
+ fields = javaClass.getDeclaredFields();
+ f_flags = new byte[fields.length];
+ for (int i = 0; i < fields.length; ++i) {
+ int mods = fields[i].getModifiers();
+ if (Modifier.isFinal(mods) && Modifier.isStatic(mods) && Modifier.isPublic(mods))
+ f_flags[i] |= F_CONSTANT;
+ }
+ }
+
+ /**
+ * Analyse the interfaces of the class. This will fill in the
+ * <code>interfaces</code> array.
+ */
+ protected void parseInterfaces() {
+ Class[] intfs = javaClass.getInterfaces();
+ ArrayList a = new ArrayList();
+ ArrayList b = new ArrayList();
+ for (int i = 0; i < intfs.length; ++i) {
+ // Ignore java.rmi.Remote
+ if (intfs[i] == java.rmi.Remote.class)
+ continue;
+ // Ignore java.io.Serializable
+ if (intfs[i] == java.io.Serializable.class)
+ continue;
+ // Ignore java.io.Externalizable
+ if (intfs[i] == java.io.Externalizable.class)
+ continue;
+ if (!Java2IDLUtil.isAbstractValueType(intfs[i])) {
+ a.add(InterfaceType.getInterfaceType(intfs[i]));
+ } else {
+ b.add(ValueType.getValueType(intfs[i]));
+ }
+ }
+ interfaces = new InterfaceType[a.size()];
+ interfaces = (InterfaceType[])a.toArray(interfaces);
+ abstractBaseValuetypes = new ValueType[b.size()];
+ abstractBaseValuetypes = (ValueType[])b.toArray(abstractBaseValuetypes);
+ }
+
+ /**
+ * Analyse the methods of the class. This will fill in the
+ * <code>methods</code> and <code>m_flags</code> arrays.
+ */
+ protected void parseMethods() {
+ // The dynamic stub and skeleton strategy generation mechanism
+ // requires the inclusion of inherited methods in the type of
+ // remote interfaces. To speed things up, inherited methods are
+ // not considered in the type of a class or non-remote interface.
+ if (javaClass.isInterface() && java.rmi.Remote.class.isAssignableFrom(javaClass))
+ methods = javaClass.getMethods();
+ else
+ methods = javaClass.getDeclaredMethods();
+ m_flags = new byte[methods.length];
+ setters = new int[methods.length];
+ // Find read-write properties
+ for (int i = 0; i < methods.length; ++i)
+ setters[i] = -1; // no mutator here
+ for (int i = 0; i < methods.length; ++i) {
+ if (isReadMethod(methods[i]) && (m_flags[i] & M_READ) == 0) {
+ String attrName = attributeReadName(methods[i].getName());
+ Class iReturn = methods[i].getReturnType();
+ for (int j = i + 1; j < methods.length; ++j) {
+ if (isWriteMethod(methods[j]) && (m_flags[j] & M_WRITE) == 0
+ && attrName.equals(attributeWriteName(methods[j].getName()))) {
+ Class[] jParams = methods[j].getParameterTypes();
+ if (jParams.length == 1 && jParams[0] == iReturn) {
+ m_flags[i] |= M_READ;
+ m_flags[j] |= M_WRITE;
+ setters[i] = j;
+ break;
+ }
+ }
+ }
+ } else if (isWriteMethod(methods[i]) && (m_flags[i] & M_WRITE) == 0) {
+ String attrName = attributeWriteName(methods[i].getName());
+ Class[] iParams = methods[i].getParameterTypes();
+ for (int j = i + 1; j < methods.length; ++j) {
+ if (isReadMethod(methods[j]) && (m_flags[j] & M_READ) == 0
+ && attrName.equals(attributeReadName(methods[j].getName()))) {
+ Class jReturn = methods[j].getReturnType();
+ if (iParams.length == 1 && iParams[0] == jReturn) {
+ m_flags[i] |= M_WRITE;
+ m_flags[j] |= M_READ;
+ setters[j] = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+ // Find read-only properties
+ for (int i = 0; i < methods.length; ++i)
+ if ((m_flags[i] & (M_READ | M_WRITE)) == 0 && isReadMethod(methods[i]))
+ m_flags[i] |= M_READONLY;
+ // Check for overloaded and inherited methods
+ for (int i = 0; i < methods.length; ++i) {
+ if ((m_flags[i] & (M_READ | M_WRITE | M_READONLY)) == 0) {
+ String iName = methods[i].getName();
+ for (int j = i + 1; j < methods.length; ++j) {
+ if (iName.equals(methods[j].getName())) {
+ m_flags[i] |= M_OVERLOADED;
+ m_flags[j] |= M_OVERLOADED;
+ }
+ }
+ }
+ if (methods[i].getDeclaringClass() != javaClass)
+ m_flags[i] |= M_INHERITED;
+ }
+ }
+
+ /**
+ * Convert an attribute read method name in Java format to an attribute name
+ * in Java format.
+ */
+ protected String attributeReadName(String name) {
+ if (name.startsWith("get"))
+ name = name.substring(3);
+ else if (name.startsWith("is"))
+ name = name.substring(2);
+ else
+ throw new IllegalArgumentException("Not an accessor: " + name);
+ return name;
+ }
+
+ /**
+ * Convert an attribute write method name in Java format to an attribute
+ * name in Java format.
+ */
+ protected String attributeWriteName(String name) {
+ if (name.startsWith("set"))
+ name = name.substring(3);
+ else
+ throw new IllegalArgumentException("Not an accessor: " + name);
+ return name;
+ }
+
+ /**
+ * Analyse constants. This will fill in the <code>constants</code> array.
+ */
+ protected void parseConstants() {
+ ArrayList a = new ArrayList();
+ for (int i = 0; i < fields.length; ++i) {
+ if ((f_flags[i] & F_CONSTANT) == 0)
+ continue;
+ Class type = fields[i].getType();
+ // Only map primitives and java.lang.String
+ if (!type.isPrimitive() && type != java.lang.String.class) {
+ // It is an RMI/IIOP violation for interfaces.
+ if (javaClass.isInterface())
+ throw new IDLViolationException("Field \"" + fields[i].getName()
+ + "\" of interface \""
+ + javaClass.getName()
+ + "\" is a constant, but not of one "
+ + "of the primitive types, or String.", "1.2.3");
+ continue;
+ }
+ String name = fields[i].getName();
+ Object value;
+ try {
+ value = fields[i].get(null);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex.toString());
+ }
+ a.add(new ConstantType(name, type, value));
+ }
+ constants = new ConstantType[a.size()];
+ constants = (ConstantType[])a.toArray(constants);
+ }
+
+ /**
+ * Analyse attributes. This will fill in the <code>attributes</code>
+ * array.
+ */
+ protected void parseAttributes() {
+ ArrayList a = new ArrayList();
+ for (int i = 0; i < methods.length; ++i) {
+ // if ((m_flags[i]&M_INHERITED) != 0)
+ // continue;
+ if ((m_flags[i] & (M_READ | M_READONLY)) != 0) {
+ // Read method of an attribute.
+ String name = attributeReadName(methods[i].getName());
+ if ((m_flags[i] & M_READONLY) != 0)
+ a.add(new AttributeType(name, methods[i]));
+ else
+ a.add(new AttributeType(name, methods[i], methods[setters[i]]));
+ }
+ }
+
+ attributes = new AttributeType[a.size()];
+ attributes = (AttributeType[])a.toArray(attributes);
+ }
+
+ /**
+ * Analyse operations. This will fill in the <code>operations</code>
+ * array. This implementation just creates an empty array; override in
+ * subclasses for a real type.
+ */
+ protected void parseOperations() {
+ operations = new OperationType[0];
+ }
+
+ /**
+ * Fixup overloaded operation names. As specified in section 1.3.2.6.
+ */
+ protected void fixupOverloadedOperationNames() {
+ for (int i = 0; i < methods.length; ++i) {
+ if ((m_flags[i] & M_OVERLOADED) == 0)
+ continue;
+ // Find the operation
+ OperationType oa = null;
+ for (int opIdx = 0; oa == null && opIdx < operations.length; ++opIdx)
+ if (operations[opIdx].getMethod().equals(methods[i]))
+ oa = operations[opIdx];
+ if (oa == null)
+ continue; // This method is not mapped.
+ // Calculate new IDL name
+ ParameterType[] parms = oa.getParameters();
+ StringBuffer b = new StringBuffer(oa.getIDLName());
+ if (parms.length == 0)
+ b.append("__");
+ for (int j = 0; j < parms.length; ++j) {
+ String s = parms[j].getTypeIDLName();
+ if (s.startsWith("::"))
+ s = s.substring(2);
+ b.append('_');
+ while (!"".equals(s)) {
+ int idx = s.indexOf("::");
+ b.append('_');
+ if (idx == -1) {
+ b.append(s);
+ s = "";
+ } else {
+ b.append(s.substring(0, idx));
+ s = s.substring(idx + 2);
+ }
+ }
+ }
+ // Set new IDL name
+ oa.setIDLName(b.toString());
+ }
+ }
+
+ /**
+ * Fixup names differing only in case. As specified in section 1.3.2.7.
+ */
+ protected void fixupCaseNames() {
+ ArrayList entries = getContainedEntries();
+ boolean[] clash = new boolean[entries.size()];
+ String[] upperNames = new String[entries.size()];
+ for (int i = 0; i < entries.size(); ++i) {
+ IDLType aa = (IDLType)entries.get(i);
+ clash[i] = false;
+ upperNames[i] = aa.getIDLName().toUpperCase();
+ for (int j = 0; j < i; ++j) {
+ if (upperNames[i].equals(upperNames[j])) {
+ clash[i] = true;
+ clash[j] = true;
+ }
+ }
+ }
+ for (int i = 0; i < entries.size(); ++i) {
+ if (!clash[i])
+ continue;
+ IDLType aa = (IDLType)entries.get(i);
+ boolean noUpper = true;
+ String name = aa.getIDLName();
+ StringBuffer b = new StringBuffer(name);
+ b.append('_');
+ for (int j = 0; j < name.length(); ++j) {
+ if (!Character.isUpperCase(name.charAt(j)))
+ continue;
+ if (noUpper)
+ noUpper = false;
+ else
+ b.append('_');
+ b.append(j);
+ }
+ aa.setIDLName(b.toString());
+ }
+ }
+
+ /**
+ * Return a list of all the entries contained here.
+ *
+ * @param entries The list of entries contained here. Entries in this list
+ * must be subclasses of <code>AbstractType</code>.
+ */
+ abstract protected ArrayList getContainedEntries();
+
+ /**
+ * Return the class hash code, as specified in "The Common Object Request
+ * Broker: Architecture and Specification" (01-02-33), section 10.6.2.
+ */
+ protected void calculateClassHashCode() {
+ // The simple cases
+ if (javaClass.isInterface())
+ classHashCode = 0;
+ else if (!Serializable.class.isAssignableFrom(javaClass))
+ classHashCode = 0;
+ else if (Externalizable.class.isAssignableFrom(javaClass))
+ classHashCode = 1;
+ else
+ // Go ask Util class for the hash code
+ classHashCode = IDLUtil.getClassHashCode(javaClass);
+ }
+
+ /**
+ * Escape non-ISO characters for an IR name.
+ */
+ protected String escapeIRName(String name) {
+ StringBuffer b = new StringBuffer();
+ for (int i = 0; i < name.length(); ++i) {
+ char c = name.charAt(i);
+ if (c < 256)
+ b.append(c);
+ else
+ b.append("\\U").append(toHexString((int)c));
+ }
+ return b.toString();
+ }
+
+ /**
+ * Return the IR global ID of the given class or interface. This is
+ * described in section 1.3.5.7. The returned string is in the RMI hashed
+ * format, like "RMI:java.util.Hashtable:C03324C0EA357270:13BB0F25214AE4B8".
+ */
+ protected void calculateRepositoryId() {
+ if (javaClass.isArray() || javaClass.isPrimitive())
+ throw new IllegalArgumentException("Not a class or interface.");
+ if (javaClass.isInterface() && org.omg.CORBA.Object.class.isAssignableFrom(javaClass)
+ && org.omg.CORBA.portable.IDLEntity.class.isAssignableFrom(javaClass)) {
+ StringBuffer b = new StringBuffer("IDL:");
+ b.append(javaClass.getPackage().getName().replace('.', '/'));
+ b.append('/');
+ String base = javaClass.getName();
+ base = base.substring(base.lastIndexOf('.') + 1);
+ b.append(base).append(":1.0");
+ repositoryId = b.toString();
+ } else {
+ StringBuffer b = new StringBuffer("RMI:");
+ b.append(escapeIRName(javaClass.getName()));
+ memberPrefix = b.toString() + ".";
+ String hashStr = toHexString(classHashCode);
+ b.append(':').append(hashStr);
+ ObjectStreamClass osClass = ObjectStreamClass.lookup(javaClass);
+ if (osClass != null) {
+ long serialVersionUID = osClass.getSerialVersionUID();
+ String SVUID = toHexString(serialVersionUID);
+ if (classHashCode != serialVersionUID)
+ b.append(':').append(SVUID);
+ memberPostfix = ":" + hashStr + ":" + SVUID;
+ } else
+ memberPostfix = ":" + hashStr;
+ repositoryId = b.toString();
+ }
+ }
+
+ /**
+ * A simple aggregate of work-in-progress, and the thread doing the work.
+ */
+ private static class Task {
+ ContainerType type;
+ Thread thread;
+
+ Task(ContainerType type, Thread thread) {
+ this.type = type;
+ this.thread = thread;
+ }
+ }
+
+ /**
+ * Instances of this class cache the most complex types. The types cached
+ * are:
+ * <ul>
+ * <li><code>InterfaceType</code> for interfaces.</li>
+ * <li><code>ValueType</code> for value types.</li>
+ * <li><code>ExceptionType</code> for exceptions.</li>
+ * </ul>
+ * Besides caching work already done, this caches work in progress, as we
+ * need to know about this to handle cyclic graphs of parses. When a thread
+ * re-enters the <code>getType()</code> metohd, an unfinished type will be
+ * returned if the same thread is already working on this type.
+ */
+ protected static class WorkCache {
+ /**
+ * The type constructor of our type class. This constructor takes a
+ * single argument of type <code>Class</code>.
+ */
+ private Constructor constructor;
+
+ /**
+ * This maps the classes of completely done parses to soft references of
+ * their type.
+ */
+ private Map workDone;
+
+ /**
+ * This maps the classes of parses in progress to their type.
+ */
+ private Map workInProgress;
+
+ /**
+ * Create a new work cache manager.
+ *
+ * @param containerType The class of the type type we cache here.
+ */
+ WorkCache(Class containerType) {
+ // Find the constructor and initializer.
+ try {
+ constructor = containerType.getDeclaredConstructor(new Class[] {Class.class});
+ } catch (NoSuchMethodException ex) {
+ throw new IllegalArgumentException("Bad Class: " + ex.toString());
+ }
+ workDone = new WeakHashMap();
+ workInProgress = new HashMap();
+ }
+
+ /**
+ * Returns an type. If the calling thread is currently doing an type of
+ * this class, an unfinished type is returned.
+ */
+ ContainerType getType(Class cls) {
+ ContainerType ret;
+ synchronized (this) {
+ ret = lookupDone(cls);
+ if (ret != null)
+ return ret;
+ // is it work-in-progress?
+ Task inProgress = (Task)workInProgress.get(cls);
+ if (inProgress != null) {
+ if (inProgress.thread == Thread.currentThread())
+ return inProgress.type; // return unfinished
+ // Do not wait for the other thread: We may deadlock
+ // Double work is better than deadlock...
+ }
+ ret = createTask(cls);
+ }
+ // Do the work
+ parse(cls, ret);
+ // We did it
+ synchronized (this) {
+ workInProgress.remove(cls);
+ workDone.put(cls, new SoftReference(ret));
+ notifyAll();
+ }
+ return ret;
+ }
+
+ /**
+ * Lookup an type in the fully done map.
+ */
+ private ContainerType lookupDone(Class cls) {
+ SoftReference ref = (SoftReference)workDone.get(cls);
+ if (ref == null)
+ return null;
+ ContainerType ret = (ContainerType)ref.get();
+ if (ret == null)
+ workDone.remove(cls); // clear map entry if soft ref. was
+ // cleared.
+ return ret;
+ }
+
+ /**
+ * Create new work-in-progress.
+ */
+ private ContainerType createTask(Class cls) {
+ try {
+ ContainerType type = (ContainerType)constructor.newInstance(new Object[] {cls});
+ workInProgress.put(cls, new Task(type, Thread.currentThread()));
+ return type;
+ } catch (Exception ex) {
+ // Ignore it
+ return null;
+ }
+ }
+
+ private void parse(Class cls, ContainerType ret) {
+ try {
+ ret.parse();
+ } finally {
+ synchronized (this) {
+ workInProgress.remove(cls);
+ }
+ }
+ }
+
+ }
+
+}