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 Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036: package com.sun.tools.xjc.reader;
037:
038: import java.util.ArrayList;
039: import java.util.Collection;
040: import java.util.Comparator;
041: import java.util.Iterator;
042: import java.util.List;
043: import java.util.Set;
044: import java.util.TreeSet;
045:
046: import com.sun.codemodel.JClass;
047: import com.sun.codemodel.JClassAlreadyExistsException;
048: import com.sun.codemodel.JCodeModel;
049: import com.sun.codemodel.JDefinedClass;
050: import com.sun.codemodel.JType;
051: import com.sun.tools.xjc.ErrorReceiver;
052:
053: import org.xml.sax.Locator;
054: import org.xml.sax.SAXParseException;
055:
056: /**
057: * Type-related utility methods.
058: *
059: * @author
060: * <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a>
061: */
062: public class TypeUtil {
063:
064: /**
065: * Computes the common base type of two types.
066: *
067: * @param types
068: * set of {@link JType} objects.
069: */
070: public static JType getCommonBaseType(JCodeModel codeModel,
071: Collection<? extends JType> types) {
072: return getCommonBaseType(codeModel, types
073: .toArray(new JType[types.size()]));
074: }
075:
076: /**
077: * Computes the common base type of types.
078: *
079: * TODO: this is a very interesting problem. Since one type has possibly
080: * multiple base types, it's not an easy problem.
081: * The current implementation is very naive.
082: *
083: * To make the result deterministic across differente JVMs, we have to
084: * use a Set whose ordering is deterministic.
085: */
086: public static JType getCommonBaseType(JCodeModel codeModel,
087: JType... t) {
088: // first, eliminate duplicates.
089: Set<JType> uniqueTypes = new TreeSet<JType>(typeComparator);
090: for (JType type : t)
091: uniqueTypes.add(type);
092:
093: // if this yields only one type. return now.
094: // this is the only case where we can return a primitive type
095: // from this method
096: if (uniqueTypes.size() == 1)
097: return uniqueTypes.iterator().next();
098:
099: // assertion failed. nullType can be used only under a very special circumstance
100: assert !uniqueTypes.isEmpty();
101:
102: // the null type doesn't need to be taken into account.
103: uniqueTypes.remove(codeModel.NULL);
104:
105: // box all the types and compute the intersection of all types
106: Set<JClass> s = null;
107:
108: for (JType type : uniqueTypes) {
109: JClass cls = type.boxify();
110:
111: if (s == null)
112: s = getAssignableTypes(cls);
113: else
114: s.retainAll(getAssignableTypes(cls));
115: }
116:
117: // any JClass can be casted to Object, so make sure it's always there
118: s.add(codeModel.ref(Object.class));
119:
120: // refine 's' by removing "lower" types.
121: // for example, if we have both java.lang.Object and
122: // java.io.InputStream, then we don't want to use java.lang.Object.
123:
124: JClass[] raw = s.toArray(new JClass[s.size()]);
125: s.clear();
126:
127: for (int i = 0; i < raw.length; i++) { // for each raw[i]
128: int j;
129: for (j = 0; j < raw.length; j++) { // see if raw[j] "includes" raw[i]
130: if (i == j)
131: continue;
132:
133: if (raw[i].isAssignableFrom(raw[j]))
134: break; // raw[j] is derived from raw[i], hence j includes i.
135: }
136:
137: if (j == raw.length)
138: // no other type inclueds raw[i]. remember this value.
139: s.add(raw[i]);
140: }
141:
142: assert !s.isEmpty(); // since at least java.lang.Object has to be there
143:
144: // we now pick the candidate for the return type
145: JClass result = pickOne(s);
146:
147: // finally, sometimes this method is used to compute the base type of types like
148: // JAXBElement<A>, JAXBElement<B>, and JAXBElement<C>.
149: // for those inputs, at this point result=JAXBElement.
150: //
151: // here, we'll try to figure out the parameterization
152: // so that we can return JAXBElement<? extends D> instead of just "JAXBElement".
153: if (result.isParameterized())
154: return result;
155:
156: // for each uniqueType we store the list of base type parameterization
157: List<List<JClass>> parameters = new ArrayList<List<JClass>>(
158: uniqueTypes.size());
159: int paramLen = -1;
160:
161: for (JType type : uniqueTypes) {
162: JClass cls = type.boxify();
163: JClass bp = cls.getBaseClass(result);
164: // if there's no parameterization in the base type,
165: // we won't do any better than <?>. Thus no point in trying to figure out the parameterization.
166: // just return the base type.
167: if (bp.equals(result))
168: return result;
169:
170: assert bp.isParameterized();
171: List<JClass> tp = bp.getTypeParameters();
172: parameters.add(tp);
173:
174: assert paramLen == -1 || paramLen == tp.size();
175: // since 'bp' always is a parameterized version of 'result', it should always
176: // have the same number of parameters.
177: paramLen = tp.size();
178: }
179:
180: List<JClass> paramResult = new ArrayList<JClass>();
181: List<JClass> argList = new ArrayList<JClass>(parameters.size());
182: // for each type parameter compute the common base type
183: for (int i = 0; i < paramLen; i++) {
184: argList.clear();
185: for (List<JClass> list : parameters)
186: argList.add(list.get(i));
187:
188: // compute the lower bound.
189: JClass bound = (JClass) getCommonBaseType(codeModel,
190: argList);
191: boolean allSame = true;
192: for (JClass a : argList)
193: allSame &= a.equals(bound);
194: if (!allSame)
195: bound = bound.wildcard();
196:
197: paramResult.add(bound);
198: }
199:
200: return result.narrow(paramResult);
201: }
202:
203: private static JClass pickOne(Set<JClass> s) {
204: // we may have more than one candidates at this point.
205: // any user-defined generated types should have
206: // precedence over system-defined existing types.
207: //
208: // so try to return such a type if any.
209: for (JClass c : s)
210: if (c instanceof JDefinedClass)
211: return c;
212:
213: // we can do more if we like. for example,
214: // we can avoid types in the RI runtime.
215: // but for now, just return the first one.
216: return s.iterator().next();
217: }
218:
219: private static Set<JClass> getAssignableTypes(JClass t) {
220: Set<JClass> r = new TreeSet<JClass>(typeComparator);
221: getAssignableTypes(t, r);
222: return r;
223: }
224:
225: /**
226: * Returns the set of all classes/interfaces that a given type
227: * implements/extends, including itself.
228: *
229: * For example, if you pass java.io.FilterInputStream, then the returned
230: * set will contain java.lang.Object, java.lang.InputStream, and
231: * java.lang.FilterInputStream.
232: */
233: private static void getAssignableTypes(JClass t, Set<JClass> s) {
234: if (!s.add(t))
235: return;
236:
237: // add its raw type
238: s.add(t.erasure());
239:
240: // if this type is added for the first time,
241: // recursively process the super class.
242: JClass _super = t._extends();
243: if (_super != null)
244: getAssignableTypes(_super , s);
245:
246: // recursively process all implemented interfaces
247: Iterator<JClass> itr = t._implements ();
248: while (itr.hasNext())
249: getAssignableTypes(itr.next(), s);
250: }
251:
252: /**
253: * Obtains a {@link JType} object for the string representation
254: * of a type.
255: */
256: public static JType getType(JCodeModel codeModel, String typeName,
257: ErrorReceiver errorHandler, Locator errorSource) {
258:
259: try {
260: return codeModel.parseType(typeName);
261: } catch (ClassNotFoundException ee) {
262:
263: // make it a warning
264: errorHandler.warning(new SAXParseException(
265: Messages.ERR_CLASS_NOT_FOUND.format(typeName),
266: errorSource));
267:
268: // recover by assuming that it's a class that derives from Object
269: return codeModel.directClass(typeName);
270: }
271: }
272:
273: /**
274: * Compares {@link JType} objects by their names.
275: */
276: private static final Comparator<JType> typeComparator = new Comparator<JType>() {
277: public int compare(JType t1, JType t2) {
278: return t1.fullName().compareTo(t2.fullName());
279: }
280: };
281: }
|