001: /*
002: Copyright (c) 2004-2005, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.binding.model;
030:
031: import java.util.HashMap;
032:
033: /**
034: * Utilities for working with class, field, or method information.
035: *
036: * @author Dennis M. Sosnoski
037: */
038: public class ClassUtils {
039: /** Map for primitive type signature variants. */
040: private static HashMap s_variantMap = new HashMap();
041:
042: /** Map for signatures corresponding to class names. */
043: private static HashMap s_signatureMap = new HashMap();
044:
045: static {
046:
047: // initialize primitive type variants
048: s_variantMap.put("boolean", new String[] { "Z" });
049: s_variantMap.put("byte", new String[] { "B", "S", "I" });
050: s_variantMap.put("char", new String[] { "C", "I" });
051: s_variantMap.put("double", new String[] { "D" });
052: s_variantMap.put("float", new String[] { "F" });
053: s_variantMap.put("int", new String[] { "I" });
054: s_variantMap.put("long", new String[] { "J" });
055: s_variantMap.put("short", new String[] { "S", "I" });
056: s_variantMap.put("void", new String[] { "V" });
057:
058: // initialize signatures for primitive types
059: s_signatureMap.put("boolean", "Z");
060: s_signatureMap.put("byte", "B");
061: s_signatureMap.put("char", "C");
062: s_signatureMap.put("double", "D");
063: s_signatureMap.put("float", "F");
064: s_signatureMap.put("int", "I");
065: s_signatureMap.put("long", "J");
066: s_signatureMap.put("short", "S");
067: s_signatureMap.put("void", "V");
068: }
069:
070: /**
071: * Check if type name is a primitive.
072: *
073: * @param type
074: * @return <code>true</code> if a primitive, <code>false</code> if not
075: */
076: public static boolean isPrimitive(String type) {
077: return s_variantMap.get(type) != null;
078: }
079:
080: /**
081: * Get virtual method by fully qualified name. This splits the class
082: * name from the method name, finds the class, and then tries to find a
083: * matching method name in that class or a superclass.
084: *
085: * @param name fully qualified class and method name
086: * @param sigs possible method signatures
087: * @param vctx validation context (used for class lookup)
088: * @return information for the method, or <code>null</code> if not found
089: */
090: public static IClassItem findVirtualMethod(String name,
091: String[] sigs, ValidationContext vctx) {
092:
093: // get the class containing the method
094: int split = name.lastIndexOf('.');
095: String cname = name.substring(0, split);
096: String mname = name.substring(split + 1);
097: IClass iclas = vctx.getClassInfo(cname);
098: if (iclas != null) {
099:
100: // find the method in class or superclass
101: for (int i = 0; i < sigs.length; i++) {
102: IClassItem method = iclas.getMethod(mname, sigs[i]);
103: if (method != null) {
104: return method;
105: }
106: }
107: }
108: return null;
109: }
110:
111: /**
112: * Get static method by fully qualified name. This splits the class
113: * name from the method name, finds the class, and then tries to find a
114: * matching method name in that class.
115: *
116: * @param name fully qualified class and method name
117: * @param sigs possible method signatures
118: * @param vctx validation context (used for class lookup)
119: * @return information for the method, or <code>null</code> if not found
120: */
121: public static IClassItem findStaticMethod(String name,
122: String[] sigs, ValidationContext vctx) {
123:
124: // get the class containing the method
125: int split = name.lastIndexOf('.');
126: String cname = name.substring(0, split);
127: String mname = name.substring(split + 1);
128: IClass iclas = vctx.getClassInfo(cname);
129: if (iclas != null) {
130:
131: // find the method in class or superclass
132: for (int i = 0; i < sigs.length; i++) {
133: IClassItem method = iclas.getStaticMethod(mname,
134: sigs[i]);
135: if (method != null) {
136: return method;
137: }
138: }
139: }
140: return null;
141: }
142:
143: /**
144: * Get all variant signatures for a fully qualified class name. The
145: * returned array gives all signatures (for interfaces or classes) which
146: * instances of the class can match.
147: *
148: * @param name fully qualified class name
149: * @param vctx validation context (used for class lookup)
150: * @return possible signature variations for instances of the class
151: */
152: public static String[] getSignatureVariants(String name,
153: ValidationContext vctx) {
154: Object obj = s_variantMap.get(name);
155: if (obj == null) {
156: IClass iclas = vctx.getRequiredClassInfo(name);
157: return iclas.getInstanceSigs();
158: } else {
159: return (String[]) obj;
160: }
161: }
162:
163: /**
164: * Gets the signature string corresponding to a type. The base for the type
165: * may be a primitive or class name, and may include trailing array
166: * brackets.
167: *
168: * @param type type name
169: * @return signature string for type
170: */
171: public static String getSignature(String type) {
172:
173: //. check if already built signature for this type
174: String sig = (String) s_signatureMap.get(type);
175: if (sig == null) {
176:
177: // check if this is an array type
178: int dim = 0;
179: int split = type.indexOf('[');
180: if (split >= 0) {
181:
182: // count pairs of array brackets
183: int mark = split;
184: while ((type.length() - mark) >= 2) {
185: if (type.charAt(mark) == '['
186: || type.charAt(mark + 1) == ']') {
187: dim++;
188: mark += 2;
189: } else {
190: throw new IllegalArgumentException(
191: "Invalid type name " + type);
192: }
193: }
194:
195: // make sure only bracket pairs at end
196: if (mark < type.length()) {
197: throw new IllegalArgumentException(
198: "Invalid type name " + type);
199: }
200:
201: // see if signature for base object type needs to be added
202: String cname = type.substring(0, split);
203: String base = (String) s_signatureMap.get(cname);
204: if (base == null) {
205:
206: // add base type signature to map
207: base = "L" + cname.replace('.', '/') + ';';
208: s_signatureMap.put(cname, base);
209:
210: }
211:
212: // prepend appropriate number of
213: StringBuffer buff = new StringBuffer(dim
214: + base.length());
215: for (int i = 0; i < dim; i++) {
216: buff.append('[');
217: }
218: buff.append(base);
219: sig = buff.toString();
220:
221: } else {
222:
223: // define signature for ordinary object type
224: sig = "L" + type.replace('.', '/') + ';';
225:
226: }
227:
228: // add signature definition to map
229: s_signatureMap.put(type, sig);
230: }
231:
232: // return signature for type
233: return sig;
234: }
235:
236: /**
237: * Check if a value of one type can be directly assigned to another type.
238: * This is basically the equivalent of the instanceof operator, but with
239: * application to primitive types as well as object types.
240: *
241: * @param from fully qualified class name of initial type
242: * @param to fully qualified class name of assignment type
243: * @param vctx validation context (used for class lookup)
244: * @return <code>true</code> if assignable, <code>false</code> if not
245: */
246: public static boolean isAssignable(String from, String to,
247: ValidationContext vctx) {
248:
249: // always assignable if the two are the same
250: if (from.equals(to)) {
251: return true;
252: } else {
253:
254: // try direct lookup for primitive types
255: Object fobj = s_variantMap.get(from);
256: Object tobj = s_variantMap.get(to);
257: if (fobj == null && tobj == null) {
258:
259: // find the actual class information
260: IClass fclas = vctx.getRequiredClassInfo(from);
261: IClass tclas = vctx.getRequiredClassInfo(to);
262:
263: // assignable if from type has to as a possible signature
264: String[] sigs = fclas.getInstanceSigs();
265: String match = tclas.getSignature();
266: for (int i = 0; i < sigs.length; i++) {
267: if (match.equals(sigs[i])) {
268: return true;
269: }
270: }
271: return false;
272:
273: } else if (fobj != null && tobj != null) {
274:
275: // assignable if from type has to as a possible signature
276: String[] fsigs = (String[]) fobj;
277: String[] tsigs = (String[]) tobj;
278: if (tsigs.length == 1) {
279: for (int i = 0; i < fsigs.length; i++) {
280: if (fsigs[i] == tsigs[0]) {
281: return true;
282: }
283: }
284: }
285: return false;
286:
287: } else {
288:
289: // primitive and object types never assignable
290: return false;
291:
292: }
293: }
294: }
295: }
|