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-2006 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.form;
043:
044: import java.util.*;
045: import java.lang.reflect.*;
046: import java.util.logging.Level;
047: import java.util.logging.Logger;
048: import org.netbeans.modules.form.codestructure.*;
049:
050: /**
051: * @author Tomas Pavek
052: */
053:
054: public class CreationDescriptor {
055:
056: // style flags - for finding best creator for a set or properties
057: public static final int CHANGED_ONLY = 1;
058: public static final int PLACE_ALL = 2;
059:
060: public interface Creator {
061:
062: public int getParameterCount();
063:
064: public Class[] getParameterTypes();
065:
066: public Class[] getExceptionTypes();
067:
068: public String[] getPropertyNames();
069:
070: public Object createInstance(FormProperty[] props)
071: throws InstantiationException, IllegalAccessException,
072: IllegalArgumentException, InvocationTargetException;
073:
074: public Object createInstance(Object[] paramValues)
075: throws InstantiationException, IllegalAccessException,
076: IllegalArgumentException, InvocationTargetException;
077:
078: // [this will become useless when we can rely on getCodeOrigin(...)]
079: public String getJavaCreationCode(FormProperty[] props,
080: Class expressionType, String genericTypes);
081:
082: public CodeExpressionOrigin getCodeOrigin(
083: CodeExpression[] params);
084: }
085:
086: private Class describedClass;
087: private List<Creator> creators = new ArrayList<Creator>(10);
088: private Object[] defaultParams;
089: private Creator defaultCreator;
090:
091: private static final Class[] emptyTypes = {};
092: private static final String[] emptyNames = {};
093: private static final Object[] emptyParams = {};
094:
095: public CreationDescriptor() {
096: }
097:
098: public CreationDescriptor(Class descClass,
099: Class[][] constrParamTypes, String[][] constrPropNames,
100: Object[] defParams) throws NoSuchMethodException, // if some constructor is not found
101: IllegalArgumentException {
102: addConstructorCreators(descClass, constrParamTypes,
103: constrPropNames, defParams);
104: }
105:
106: CreationDescriptor(Class factoryClass, Class descClass,
107: String methodName, Class[][] constrParamTypes,
108: String[][] constrPropNames,
109: CreationFactory.PropertyParameters[] propertyParameters,
110: Object[] defParams) throws NoSuchMethodException, // if some method is not found
111: IllegalArgumentException {
112: addMethodCreators(factoryClass, descClass, methodName,
113: constrParamTypes, constrPropNames, propertyParameters,
114: defParams);
115: }
116:
117: public void addConstructorCreators(Class descClass,
118: Class[][] constrParamTypes, String[][] constrPropNames,
119: Object[] defParams) throws NoSuchMethodException // if some constructor is not found
120: {
121: if (getDescribedClass() == null) {
122: setDescribedClass(descClass);
123: } else if (getDescribedClass() != descClass) {
124: throw new IllegalArgumentException();
125: }
126: if (constrParamTypes != null && constrParamTypes.length > 0) {
127:
128: for (int i = 0; i < constrParamTypes.length; i++)
129: creators.add(new ConstructorCreator(describedClass,
130: constrParamTypes[i], constrPropNames[i]));
131: }
132:
133: defaultParams = defParams == null ? emptyParams : defParams;
134: }
135:
136: void addMethodCreators(Class factoryClass, Class descClass,
137: String methodName, Class[][] constrParamTypes,
138: String[][] constrPropNames,
139: CreationFactory.PropertyParameters[] propertyParameters,
140: Object[] defParams) throws NoSuchMethodException // if some method is not found
141: {
142: if (getDescribedClass() == null) {
143: setDescribedClass(descClass);
144: } else if (getDescribedClass() != descClass) {
145: throw new IllegalArgumentException();
146: }
147:
148: if (constrParamTypes != null && constrParamTypes.length > 0) {
149:
150: CreationFactory.Property2ParametersMapper[] properties;
151: for (int i = 0; i < constrParamTypes.length; i++) {
152:
153: properties = new CreationFactory.Property2ParametersMapper[constrParamTypes[i].length];
154: for (int j = 0; j < constrParamTypes[i].length; j++) {
155: properties[j] = new CreationFactory.Property2ParametersMapper(
156: constrParamTypes[i][j],
157: constrPropNames[i][j]);
158: if (propertyParameters != null
159: && propertyParameters.length > 0) {
160: for (int ppi = 0; ppi < propertyParameters.length; ppi++) {
161: if (propertyParameters[ppi]
162: .getPropertyName().equals(
163: constrPropNames[i][j])) {
164: properties[j]
165: .setPropertyParameters(propertyParameters[ppi]);
166: }
167: }
168: }
169: }
170:
171: creators.add(new MethodCreator(factoryClass,
172: describedClass, methodName, properties));
173:
174: }
175:
176: }
177:
178: defaultParams = defParams == null ? emptyParams : defParams;
179: }
180:
181: public void addCreator(Creator creator, Object[] defaultParams) {
182: creators.add(creator);
183: this .defaultParams = defaultParams;
184: }
185:
186: protected void setDescribedClass(Class descClass)
187: throws IllegalArgumentException {
188: if (describedClass == null) {
189: describedClass = descClass;
190: }
191: }
192:
193: public CreationDescriptor(Class descClass) {
194: // throws NoSuchMethodException // if public empty constructor doesn't exist
195: describedClass = descClass;
196:
197: try {
198: ConstructorCreator creator = new ConstructorCreator(
199: describedClass, emptyTypes, emptyNames);
200: creators.add(creator);
201: } catch (NoSuchMethodException ex) { // ignore
202: Logger.getLogger(CreationDescriptor.class.getName()).log(
203: Level.INFO,
204: "[WARNING] No default constructor for "
205: + descClass.getName(), ex); // NOI18N
206: }
207:
208: defaultParams = emptyParams;
209: }
210:
211: // ---------
212:
213: public Class getDescribedClass() {
214: return describedClass;
215: }
216:
217: /**
218: * This method allows sub-classes to return name of the
219: * described class without the need to load the class itself.
220: *
221: * @return name of the described class e.g. (<code>getDescribedClass().getName()</code>).
222: */
223: public String getDescribedClassName() {
224: return getDescribedClass().getName();
225: }
226:
227: public Creator[] getCreators() {
228: return creators.toArray(new Creator[creators.size()]);
229: }
230:
231: public Creator findBestCreator(FormProperty[] properties, int style) {
232: if (creators == null)
233: return null;
234:
235: Creator[] allCreators = getCreators();
236: int[] evals = CreationFactory.evaluateCreators(allCreators,
237: properties, (style & CHANGED_ONLY) != 0);
238: int best = CreationFactory.getBestCreator(allCreators,
239: properties, evals, (style & PLACE_ALL) != 0);
240: return allCreators[best];
241: }
242:
243: public Object createDefaultInstance()
244: throws InstantiationException, IllegalAccessException,
245: IllegalArgumentException, InvocationTargetException,
246: NoSuchMethodException {
247: return getDefaultCreator().createInstance(defaultParams);
248: }
249:
250: private Creator getDefaultCreator() throws NoSuchMethodException {
251: if (defaultCreator == null) {
252: defaultCreator = findDefaultCreator();
253: }
254: return defaultCreator;
255: }
256:
257: // ----------
258:
259: // finds first constructor that matches defaultConstrParams
260: private Creator findDefaultCreator() throws NoSuchMethodException {
261: for (Iterator<Creator> it = creators.iterator(); it.hasNext();) {
262:
263: Creator creator = it.next();
264: Class[] paramTypes = creator.getParameterTypes();
265:
266: if (paramTypes.length == defaultParams.length) {
267: int ii;
268: for (ii = 0; ii < paramTypes.length; ii++) {
269: Class cls = paramTypes[ii];
270: Object param = defaultParams[ii];
271:
272: if (cls.isPrimitive()) {
273: if (param == null
274: || (param instanceof Integer && cls != Integer.TYPE)
275: || (param instanceof Boolean && cls != Boolean.TYPE)
276: || (param instanceof Double && cls != Double.TYPE)
277: || (param instanceof Long && cls != Long.TYPE)
278: || (param instanceof Float && cls != Float.TYPE)
279: || (param instanceof Short && cls != Short.TYPE)
280: || (param instanceof Byte && cls != Byte.TYPE)
281: || (param instanceof Character && cls != Character.TYPE))
282: break;
283: } else if (param != null && !cls.isInstance(param))
284: break;
285: }
286: if (ii == paramTypes.length) {
287: return creator;
288: }
289: }
290: }
291: throw new NoSuchMethodException();
292: }
293:
294: // ----------
295:
296: static class ConstructorCreator implements Creator {
297: private Class theClass;
298: private Constructor constructor;
299: // private Class[] constructorParamTypes;
300: private String[] constructorPropNames;
301:
302: ConstructorCreator(Class cls, Class[] paramTypes,
303: String[] propNames) throws NoSuchMethodException {
304: if (paramTypes == null)
305: paramTypes = emptyTypes;
306: if (propNames == null)
307: propNames = emptyNames;
308: if (paramTypes.length != propNames.length)
309: throw new IllegalArgumentException();
310:
311: constructor = cls.getConstructor(paramTypes);
312: theClass = cls;
313: // constructorParamTypes = paramTypes;
314: constructorPropNames = propNames;
315: }
316:
317: public final int getParameterCount() {
318: return constructorPropNames.length; //constructorParamTypes.length;
319: }
320:
321: public final Class[] getParameterTypes() {
322: return constructor.getParameterTypes(); //constructorParamTypes;
323: }
324:
325: public final Class[] getExceptionTypes() {
326: return constructor.getExceptionTypes();
327: }
328:
329: public final String[] getPropertyNames() {
330: return constructorPropNames;
331: }
332:
333: public Object createInstance(FormProperty[] props)
334: throws InstantiationException, IllegalAccessException,
335: IllegalArgumentException, InvocationTargetException {
336: Object[] paramValues = new Object[constructorPropNames.length];
337:
338: for (int i = 0; i < constructorPropNames.length; i++) {
339: FormProperty prop = CreationFactory.findProperty(
340: constructorPropNames[i], props);
341: if (prop == null)
342: return null; // should not happen
343:
344: try {
345: paramValues[i] = prop.getRealValue();
346: } catch (Exception ex) { // unlikely to happen
347: InstantiationException iex = new InstantiationException();
348: iex.initCause(ex);
349: throw (iex);
350: }
351: }
352:
353: return constructor.newInstance(paramValues);
354: }
355:
356: public Object createInstance(Object[] paramValues)
357: throws InstantiationException, IllegalAccessException,
358: IllegalArgumentException, InvocationTargetException {
359: return constructor.newInstance(paramValues);
360: }
361:
362: public String getJavaCreationCode(FormProperty[] props,
363: Class expressionType, String genericTypes) {
364: StringBuffer buf = new StringBuffer();
365: buf.append("new "); // NOI18N
366: buf.append(theClass.getCanonicalName());
367: if (genericTypes != null) {
368: buf.append(genericTypes);
369: }
370: buf.append("("); // NOI18N
371:
372: for (int i = 0; i < constructorPropNames.length; i++) {
373: FormProperty prop = CreationFactory.findProperty(
374: constructorPropNames[i], props);
375: if (prop == null)
376: return null; // should not happen
377:
378: buf.append(prop.getJavaInitializationString());
379: if (i + 1 < constructorPropNames.length)
380: buf.append(", "); // NOI18N
381: }
382:
383: buf.append(")"); // NOI18N
384: return buf.toString();
385: }
386:
387: public CodeExpressionOrigin getCodeOrigin(
388: CodeExpression[] params) {
389: return CodeStructure.createOrigin(constructor, params);
390: }
391: }
392:
393: static class MethodCreator implements Creator {
394: private Class factoryClass;
395: private Class describedClass;
396: private Method method;
397: private CreationFactory.Property2ParametersMapper[] properties;
398: private String[] propertyNames;
399:
400: MethodCreator(Class factoryClass, Class describedClass,
401: String methodName,
402: CreationFactory.Property2ParametersMapper[] properties)
403: throws NoSuchMethodException {
404:
405: List<Class> paramTypesList = new ArrayList<Class>();
406: propertyNames = new String[properties.length];
407:
408: for (int i = 0; i < properties.length; i++) {
409: for (int j = 0; j < properties[i].getPropertyTypes().length; j++) {
410: paramTypesList
411: .add(properties[i].getPropertyTypes()[j]);
412: }
413: propertyNames[i] = properties[i].getPropertyName();
414: }
415:
416: Class[] paramTypes = paramTypesList
417: .toArray(new Class[paramTypesList.size()]);
418:
419: method = factoryClass.getMethod(methodName, paramTypes);
420:
421: this .factoryClass = factoryClass;
422: this .describedClass = describedClass;
423: this .properties = properties;
424:
425: }
426:
427: public final int getParameterCount() {
428: return propertyNames.length;
429: }
430:
431: public final Class[] getParameterTypes() {
432: return method.getParameterTypes();
433: }
434:
435: public final Class[] getExceptionTypes() {
436: return method.getExceptionTypes();
437: }
438:
439: public final String[] getPropertyNames() {
440: return propertyNames;
441: }
442:
443: public Object createInstance(FormProperty[] props)
444: throws InstantiationException, IllegalAccessException,
445: IllegalArgumentException, InvocationTargetException {
446:
447: List<Object> paramValuesList = new ArrayList<Object>();
448: for (int i = 0; i < properties.length; i++) {
449: FormProperty prop = CreationFactory.findProperty(
450: properties[i].getPropertyName(), props);
451: if (prop == null)
452: return null; // should not happen
453:
454: Object[] propertyParameters = properties[i]
455: .getPropertyParametersValues(prop);
456: for (int j = 0; j < propertyParameters.length; j++) {
457: paramValuesList.add(propertyParameters[j]);
458: }
459: }
460:
461: Object[] paramValues = paramValuesList
462: .toArray(new Object[paramValuesList.size()]);
463:
464: Object ret = method.invoke(null, paramValues);
465: if (ret.getClass() != describedClass) {
466: throw new IllegalArgumentException();
467: }
468: return ret;
469: }
470:
471: public Object createInstance(Object[] paramValues)
472: throws InstantiationException, IllegalAccessException,
473: IllegalArgumentException, InvocationTargetException {
474:
475: Object ret = method.invoke(null, paramValues);
476: if (ret.getClass() != describedClass) {
477: throw new IllegalArgumentException();
478: }
479: return ret;
480: }
481:
482: public String getJavaCreationCode(FormProperty[] props,
483: Class expressionType, String genericTypes) {
484: StringBuffer buf = new StringBuffer();
485: if (expressionType == null)
486: expressionType = describedClass;
487: if (!expressionType
488: .isAssignableFrom(method.getReturnType())) { // Issue 71220
489: buf.append('(').append(expressionType.getName())
490: .append(')');
491: }
492: buf.append(factoryClass.getName()); // NOI18N
493: buf.append("."); // NOI18N
494: buf.append(method.getName());
495: buf.append("("); // NOI18N
496:
497: for (int i = 0; i < properties.length; i++) {
498:
499: String name = properties[i].getPropertyName();
500: FormProperty prop = CreationFactory.findProperty(name,
501: props);
502:
503: if (prop == null)
504: return null; // should not happen
505:
506: buf.append(properties[i].getJavaParametersString(prop));
507:
508: if (i + 1 < properties.length)
509: buf.append(", "); // NOI18N
510: }
511:
512: buf.append(")"); // NOI18N
513: return buf.toString();
514: }
515:
516: public CodeExpressionOrigin getCodeOrigin(
517: CodeExpression[] params) {
518: // nobody cares ...
519: return null;
520: }
521: }
522:
523: }
|