0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package org.netbeans.modules.visualweb.insync.beans;
0042:
0043: import java.lang.reflect.Modifier;
0044: import org.netbeans.modules.visualweb.insync.java.EventMethod;
0045: import org.netbeans.modules.visualweb.insync.java.JavaClass;
0046: import org.netbeans.modules.visualweb.insync.java.Method;
0047: import org.netbeans.modules.visualweb.insync.java.Statement;
0048: import org.netbeans.modules.visualweb.insync.models.FacesModel;
0049: import java.beans.BeanInfo;
0050: import java.beans.Introspector;
0051: import java.beans.MethodDescriptor;
0052: import java.io.PrintWriter;
0053: import java.lang.reflect.Array;
0054: import java.math.BigDecimal;
0055: import java.util.ArrayList;
0056: import java.util.HashMap;
0057: import java.util.Iterator;
0058: import java.util.List;
0059: import java.util.Map;
0060:
0061: import org.openide.ErrorManager;
0062: import org.openide.util.Lookup;
0063: import org.netbeans.modules.visualweb.extension.openide.util.Trace;
0064:
0065: import com.sun.rave.designtime.Position;
0066: import org.netbeans.modules.visualweb.insync.Model;
0067: import org.netbeans.modules.visualweb.insync.ParserAnnotation;
0068: import org.netbeans.modules.visualweb.insync.UndoEvent;
0069: import org.netbeans.modules.visualweb.insync.Unit;
0070: import org.netbeans.modules.visualweb.insync.java.JavaUnit;
0071: import org.netbeans.modules.visualweb.insync.java.ClassUtil;
0072:
0073: /**
0074: * An abstract compilation Unit that represents a single outer (or 'this' or 'root') JavaBean class
0075: * under construction. Within this Unit will be instance Beans, as well as event handling methods.
0076: *
0077: * @author cquinn
0078: */
0079: public class BeansUnit implements Unit {
0080:
0081: protected final List<Bean> beans = new ArrayList<Bean>();
0082: protected final Map nameCounters = new HashMap(); // type-mapped integer counters
0083:
0084: protected JavaUnit junit; // underlying java source unit
0085: protected ClassLoader classLoader; // classloader to use for loading bean and beaninfo classes
0086:
0087: protected String packageName; // package this unit resides in
0088:
0089: protected JavaClass javaClass;
0090:
0091: protected ParserAnnotation error;
0092:
0093: protected Model model; // Not sure this should be here but I need it for some code. revisit. EAT TODO XXX
0094:
0095: // We need to force a sync in case I was constructed with units that we're already synced
0096: // If my sub units are already synced, I will not realize I myself need to be sync'ed. This flag
0097: // is true once at least one sync has been completed.
0098: protected boolean syncedSinceConstructed;
0099:
0100: private boolean isPageBean = false;
0101:
0102: // Hold onto my scanner
0103: protected BeanStructureScanner beanStructureScanner;
0104:
0105: private String baseClassName;
0106:
0107: protected List<Bean> beansToAdd = new ArrayList<Bean>();
0108: protected List<Bean> beansToRemove = new ArrayList<Bean>();
0109:
0110: //--------------------------------------------------------------------------------- Construction
0111:
0112: /**
0113: * Set up the beans design-time flags
0114: */
0115: {
0116: java.beans.Beans.setDesignTime(true);
0117: java.beans.Beans.setGuiAvailable(true);
0118: }
0119:
0120: /**
0121: * Construct an BeansUnit from an existing source file
0122: *
0123: * @param junit Underlying Java Unit
0124: * @param cl Project-wide classloader
0125: * @param packageName Package name for this class
0126: */
0127: public BeansUnit(JavaUnit junit, ClassLoader cl,
0128: String packageName, Model model) {
0129: this .junit = junit;
0130: this .packageName = packageName;
0131: this .model = model;
0132: setClassLoader(cl);
0133: //Trace.enableTraceCategory("insync.beans");
0134: this .syncedSinceConstructed = false;
0135: }
0136:
0137: /*
0138: * @see org.netbeans.modules.visualweb.insync.Unit#destroy()
0139: */
0140: public void destroy() {
0141: junit = null;
0142: beans.clear();
0143: nameCounters.clear();
0144: packageName = null;
0145: beanStructureScanner = null;
0146: javaClass = null;
0147: }
0148:
0149: /*
0150: * @see org.netbeans.modules.visualweb.insync.Unit#getState()
0151: */
0152: public State getState() {
0153: return junit.getState();
0154: }
0155:
0156: public boolean isPageBean() {
0157: return isPageBean;
0158: }
0159:
0160: public ParserAnnotation[] getErrors() {
0161: ParserAnnotation[] annotations = (junit == null) ? ParserAnnotation.EMPTY_ARRAY
0162: : junit.getErrors();
0163: if (error != null) {
0164: System
0165: .arraycopy(
0166: annotations,
0167: 0,
0168: annotations = new ParserAnnotation[annotations.length + 1],
0169: 0, annotations.length - 1);
0170: annotations[annotations.length - 1] = error;
0171: }
0172: return annotations;
0173: }
0174:
0175: /**
0176: * @return
0177: */
0178: public ClassLoader getClassLoader() {
0179: return classLoader;
0180: }
0181:
0182: /**
0183: * @param cl
0184: */
0185: public void setClassLoader(ClassLoader cl) {
0186: classLoader = cl != null ? cl : (ClassLoader) Lookup
0187: .getDefault().lookup(ClassLoader.class);
0188: }
0189:
0190: //----------------------------------------------------------------------------------- Unit Input
0191:
0192: /*
0193: * @see org.netbeans.modules.visualweb.insync.Unit#readLock()
0194: */
0195: public void readLock() {
0196: junit.readLock();
0197: }
0198:
0199: /*
0200: * @see org.netbeans.modules.visualweb.insync.Unit#readUnlock()
0201: */
0202: public void readUnlock() {
0203: junit.readUnlock();
0204: }
0205:
0206: /**
0207: * Read the underlying java model and rebuild ourselves if it had changed or we are new.
0208: * @see org.netbeans.modules.visualweb.insync.Unit#sync()
0209: */
0210: public boolean sync() {
0211: boolean synced = syncSubUnits();
0212: //if (!synced && javaClass != null)
0213: // return false; // do nothing if read was a no-op and we are already inited
0214: if ((syncedSinceConstructed && !synced)
0215: || getState().isBusted())
0216: return false;
0217: error = null;
0218: scan();
0219: if (getState().isBusted())
0220: return false;
0221: beans.clear();
0222: bind();
0223: syncedSinceConstructed = true;
0224: return true;
0225: }
0226:
0227: /**
0228: * Since a unit can be composed of other units, this is where a subclass should add code
0229: * to sync there sub units. In order for my sync to proceed, I must ensure that my sub
0230: * units are synced successfully. Once they are sync'ed, I can go ahead and sync myself.
0231: * This method provides the capability of a subclass to add on additional sub-units and have
0232: * them synced when I am asked to sync.
0233: * NOTE: Make sure that if ANY of the child units did a sync, this method must return true
0234: * as I must assume something has changed in that case and continue the sync process.
0235: * @return
0236: */
0237: protected boolean syncSubUnits() {
0238: return junit.sync();
0239: }
0240:
0241: /**
0242: * Scan our underlying document to find or create our tracked items
0243: */
0244: protected void scan() {
0245: // If the JavaUnit does not have a class defined in it, then I will re-use the same scanner
0246: // so that a similar class is re-created.
0247: javaClass = getJavaUnit().getJavaClass();
0248: if (javaClass != null) {
0249: for (int i = 0; i < FacesModel.managedBeanNames.length; i++) {
0250: if (javaClass
0251: .isSubTypeOf(FacesModel.managedBeanNames[i])) {
0252: isPageBean = FacesModel.managedBeanIsPage[i];
0253: baseClassName = FacesModel.managedBeanNames[i];
0254: break;
0255: }
0256: }
0257:
0258: beanStructureScanner = getNewBeanStructureScanner();
0259: }
0260: // Handle the case where the JavaUnit never had a public class in it to begin with
0261: if (beanStructureScanner == null) {
0262: error = new ParserAnnotation("Must have a class defined.",
0263: getJavaUnit().getFileObject(), 1, 1);
0264: // TODO Should most likely add state to BeansUnit, but since error is source related we should be ok
0265: getJavaUnit().setBusted();
0266: return;
0267: }
0268: beanStructureScanner.scan();
0269: }
0270:
0271: /**
0272: * Return my currently active scanner, if a sync has been performed or is not in progress
0273: * then the result will be null.
0274: */
0275: public BeanStructureScanner getBeanStructureScanner() {
0276: return beanStructureScanner;
0277: }
0278:
0279: /**
0280: * Bind beans & their properties, events and parents
0281: */
0282: protected void bind() {
0283: bindBeans();
0284: List<Statement> stmts = getPropertiesInitStatements();
0285: bindProperties(stmts);
0286: bindEventSets(stmts);
0287: bindBeanParents();
0288: }
0289:
0290: //---------------------------------------------------------------------------------- Unit Output
0291:
0292: /*
0293: * @see org.netbeans.modules.visualweb.insync.Unit#writeLock(org.netbeans.modules.visualweb.insync.UndoEvent)
0294: */
0295: public void writeLock(UndoEvent event) {
0296: junit.writeLock(event);
0297: }
0298:
0299: /*
0300: * @see org.netbeans.modules.visualweb.insync.Unit#writeUnlock(org.netbeans.modules.visualweb.insync.UndoEvent)
0301: */
0302: public boolean writeUnlock(UndoEvent event) {
0303: boolean unLocked = junit.writeUnlock(event);
0304: if (unLocked) {
0305: flush(event);
0306: }
0307: return unLocked;
0308: }
0309:
0310: /*
0311: * Writes the java code changes
0312: */
0313: private void flush(UndoEvent event) {
0314: junit.writeLock(event);
0315: try {
0316: if (beansToRemove.size() > 0) {
0317: javaClass.removeBeans(beansToRemove);
0318: beansToRemove.clear();
0319: }
0320:
0321: if (beansToAdd.size() > 0) {
0322: javaClass.addBeans(beansToAdd);
0323: beansToAdd.clear();
0324: }
0325:
0326: for (Bean b : beans) {
0327: if (b.unit.getPropertiesInitMethod() != null
0328: && b.isInserted()) {
0329: Method m = b.unit.getPropertiesInitMethod();
0330: m.addPropertySetStatements(b);
0331: m.addEventSetStatements(b);
0332: }
0333: }
0334: } finally {
0335: junit.writeUnlock(event);
0336: }
0337: }
0338:
0339: /*
0340: * @see org.netbeans.modules.visualweb.insync.Unit#isWriteLocked()
0341: */
0342: public boolean isWriteLocked() {
0343: return junit.isWriteLocked();
0344: }
0345:
0346: //----------------------------------------------------------------------------------------- Guts
0347:
0348: /**
0349: * @param bean
0350: * @param type
0351: * @return
0352: */
0353: protected Bean firstBeanOfType(Bean bean, Class type) {
0354: if (type.isAssignableFrom(bean.getBeanInfo()
0355: .getBeanDescriptor().getBeanClass()))
0356: return bean;
0357: Bean[] kids = bean.getChildren();
0358: return kids != null ? firstBeanOfType(kids, type) : null;
0359: }
0360:
0361: /**
0362: * @param beans
0363: * @param type
0364: * @return
0365: */
0366: protected Bean firstBeanOfType(Bean[] beans, Class type) {
0367: for (int i = 0; i < beans.length; i++) {
0368: Bean kb = firstBeanOfType(beans[i], type);
0369: if (kb != null)
0370: return kb;
0371: }
0372: return null;
0373: }
0374:
0375: /**
0376: * Update the names counter for a given type to be at least as large as a specific ordinal value
0377: */
0378: protected void updateName(String type, int index) {
0379: //Use short name for type to avoid name collision
0380: int dot = type.lastIndexOf('.');
0381: if (dot >= 0)
0382: type = type.substring(dot + 1);
0383: Integer counter = (Integer) nameCounters.get(type);
0384: if (counter == null || counter.intValue() < index) {
0385: nameCounters.put(type, new Integer(index));
0386: assert Trace.trace("insync.beans", "BU.updateName type:"
0387: + type + " index:" + index); //NOI18N
0388: }
0389: }
0390:
0391: /**
0392: * Examine a given type and name to adjust the max ordinal used.
0393: */
0394: protected void scanName(String type, String name) {
0395: String baseName = Naming.varName(type);
0396: if (name.startsWith(baseName)) {
0397: int tailPos = baseName.length();
0398: String tail = name.substring(tailPos);
0399: try {
0400: int index = Integer.parseInt(tail);
0401: updateName(type, index);
0402: } catch (NumberFormatException e) {
0403: // ignore--not using a generated index-based name
0404: }
0405: }
0406: }
0407:
0408: /**
0409: * Retrieve the next generated name for a type.
0410: * @param type The type name of type to gen name for
0411: * @return The next generated name for the type.
0412: */
0413: protected String nextNameForType(String type) {
0414: //Use short name for type to avoid name collision
0415: int dot = type.lastIndexOf('.');
0416: if (dot >= 0)
0417: type = type.substring(dot + 1);
0418: Integer counter = (Integer) nameCounters.get(type);
0419: int index = counter == null ? 1 : counter.intValue() + 1;
0420: String name = null;
0421: do {
0422: name = Naming.varName(type) + Integer.toString(index);
0423: if (javaClass.getField(name) == null) {
0424: nameCounters.put(type, new Integer(index));
0425: assert Trace.trace("insync.beans",
0426: "BU.nextNameForType type:" + type + //NOI18N
0427: " index:" + index + " name:" + name); //NOI18N
0428: return name;
0429: }
0430: index++;
0431: } while (index <= 9999);
0432: return Naming.varName(type) + "_SuffixOverflow"; //NOI18N;
0433: }
0434:
0435: private boolean isBeanNameAvailable(Bean[] beans, String name,
0436: Bean bean) {
0437: for (int i = 0; i < beans.length; i++)
0438: if (name.equals(beans[i].getName()) && beans[i] != bean)
0439: return false;
0440: return true;
0441: }
0442:
0443: /**
0444: * @param name
0445: * @return whether or not a given name is available for use as a bean name
0446: */
0447: protected boolean isBeanNameAvailable(String name, Bean bean) {
0448: return isBeanNameAvailable(getBeans(), name, bean);
0449: }
0450:
0451: /**
0452: * Retrieve the next available name given a base
0453: * @param base The base name to use.
0454: * @param bean The bean that wants the name--used to ignore for autonumbering
0455: * @param alwaysNumber true to always include a number suffix, false to first attempt to use base
0456: * @return The next available name from base, returns base itself if no collisions
0457: */
0458: protected String nextAvailableName(String base, Bean bean,
0459: boolean alwaysNumber) {
0460: int suffix = 1;
0461: String name = alwaysNumber ? base + Integer.toString(suffix++)
0462: : base;
0463: base = Naming.getBaseName(base);
0464: Bean[] beans = getBeans();
0465: do {
0466: if (isBeanNameAvailable(beans, name, bean)
0467: && (javaClass.getField(name) == null))
0468: return name;
0469: name = base + Integer.toString(suffix++);
0470: } while (suffix <= 9999);
0471: return base + "_SuffixOverflow"; //NOI18N
0472: }
0473:
0474: /**
0475: * Return a new Bean instance bound to an existing field, getter & setter
0476: */
0477: protected Bean newBoundBean(BeanInfo beanInfo, String name,
0478: List<String> typeNames) {
0479: return new Bean(this , beanInfo, name, typeNames);
0480: }
0481:
0482: /**
0483: *
0484: */
0485: public boolean canCreateBean(BeanInfo bi, Bean parent) {
0486: // can't create any parented beans
0487: return parent == null && bi != null;
0488: }
0489:
0490: /**
0491: * Return a brand new created Bean instance
0492: * @param pos TODO
0493: */
0494: protected Bean newCreatedBean(BeanInfo beanInfo, Bean parent,
0495: String name, String facet, Position pos) {
0496: Bean b = new Bean(this , beanInfo, name);
0497: if (b != null) {
0498: int index = pos != null ? pos.getIndex() : -1;
0499: beansToAdd.add(b);
0500: if (parent != null) { // add this child to parent
0501: beans.add(b); // add at end, this pos doesn't matter
0502: parent.addChild(b, pos);
0503: } else {
0504: if (index > 0 && index <= beans.size())
0505: beans.add(index, b);
0506: else
0507: beans.add(b);
0508: }
0509: }
0510: return b;
0511: }
0512:
0513: /**
0514: * Scan all the fields of the host class and attempt to bind each to a new bean, updating our
0515: * beans list as we go.
0516: * Run a second parent-child wiring pass
0517: */
0518: protected void bindBeans() {
0519: HashMap<String, List<String>> props = javaClass
0520: .getPropertiesNameAndTypes();
0521: for (String key : props.keySet()) {
0522: Bean bean = getBean(key);
0523: if (bean == null) {
0524: bean = bindBean(key, props.get(key));
0525: if (bean != null) {
0526: beans.add(bean);
0527: }
0528: }
0529: if (bean != null) {
0530: bean.setInserted(true);
0531: }
0532: }
0533: }
0534:
0535: /**
0536: * Run a pass over the beans to see if they need to do parent-child wiring
0537: */
0538: protected void bindBeanParents() {
0539: for (Iterator i = beans.iterator(); i.hasNext();) {
0540: Bean b = (Bean) i.next();
0541: Bean parent = b.bindParent();
0542: if (parent != null)
0543: parent.addChild(b, null);
0544: }
0545: }
0546:
0547: protected Bean bindBean(String name, List<String> typeNames) {
0548: // Scan all type/name pairs for later name generation
0549: scanName(typeNames.get(0), name);
0550: // make sure we can obtain the bean's beaninfo
0551: BeanInfo bi = getBeanInfo(typeNames.get(0));
0552: if (bi == null) {
0553: return null;
0554: }
0555: return newBoundBean(bi, name, typeNames.subList(1, typeNames
0556: .size()));
0557: }
0558:
0559: /**
0560: * @param s
0561: * @return
0562: */
0563: protected Property newBoundProperty(Statement stmt) {
0564: return Property.newBoundInstance(this , stmt);
0565: }
0566:
0567: /**
0568: *
0569: */
0570: protected void bindProperties(List<Statement> stmts) {
0571: for (Statement stmt : stmts) {
0572: Property p = newBoundProperty(stmt);
0573: if (p != null) {
0574: Bean b = p.bean;
0575: b.properties.add(p);
0576: } else {
0577: assert Trace.trace("insync.beans",
0578: "BU.bindProperties: Stmnt was NOT a property setter:"
0579: + stmt); //NOI18N
0580: }
0581: }
0582: }
0583:
0584: /**
0585: * Overridable hook to allow subclasses an opportunity to create different kinds of bound event
0586: * sets.
0587: *
0588: * @param s
0589: * @return
0590: */
0591: protected EventSet newBoundEventSet(Statement stmt) {
0592: return EventSet.newBoundInstance(this , stmt);
0593: }
0594:
0595: /**
0596: * Scan the init block statements and create matching EventSets and register with their beans
0597: */
0598: protected void bindEventSets(List<Statement> stmts) {
0599: for (Statement stmt : stmts) {
0600: EventSet es = newBoundEventSet(stmt);
0601: if (es != null) {
0602: es.bean.eventSets.add(es);
0603: } else {
0604: assert Trace.trace("insync.beans",
0605: "BU.bindEventSets: Stmnt was NOT an event adder:"
0606: + stmt); //NOI18N
0607: }
0608: }
0609: }
0610:
0611: //------------------------------------------------------------------------------------ Accessors
0612:
0613: /**
0614: *
0615: */
0616: /**
0617: * @return
0618: */
0619: public String getThisPackageName() {
0620: return packageName; // same as: junit.getPackage().getName()
0621: }
0622:
0623: /**
0624: *
0625: */
0626: /**
0627: * @return
0628: */
0629: public String getThisClassName() {
0630: if (javaClass == null)
0631: return null;
0632: return javaClass.getShortName();
0633: }
0634:
0635: /**
0636: *
0637: */
0638: /**
0639: * @return
0640: */
0641: public String getBeanName() {
0642: return getThisClassName();
0643: }
0644:
0645: /**
0646: *
0647: */
0648: /**
0649: * @return
0650: */
0651: public JavaClass getThisClass() {
0652: return javaClass;
0653: }
0654:
0655: /**
0656: * To get the base bean class
0657: */
0658: /**
0659: * @return the base bean class if the bean is a known managed bean
0660: */
0661: public Class getBaseBeanClass() {
0662: try {
0663: return ClassUtil.getClass(getBaseBeanClassName(),
0664: classLoader);
0665: } catch (ClassNotFoundException e) {
0666: }
0667: return null;
0668: }
0669:
0670: /**
0671: * @return the base bean class name
0672: */
0673: public String getBaseBeanClassName() {
0674: return baseClassName;
0675: }
0676:
0677: /**
0678: * @return
0679: */
0680: public List<Statement> getPropertiesInitStatements() {
0681: return getBeanStructureScanner().getPropertiesInitStatements();
0682: }
0683:
0684: /**
0685: * @return
0686: */
0687: public Method getPropertiesInitMethod() {
0688: return getBeanStructureScanner().getPropertiesInitMethod();
0689: }
0690:
0691: /**
0692: * @return
0693: */
0694: public Method getCleanupMethod() {
0695: return getBeanStructureScanner().getDestroyMethod();
0696: }
0697:
0698: /**
0699: *
0700: */
0701: /**
0702: * @return
0703: */
0704: public JavaUnit getJavaUnit() {
0705: return junit;
0706: }
0707:
0708: /**
0709: *
0710: */
0711: /**
0712: * @return
0713: */
0714: public Bean[] getBeans() {
0715: return (Bean[]) beans.toArray(Bean.EMPTY_ARRAY);
0716: }
0717:
0718: /**
0719: * Get the root DOM element for the bean tree if applicable
0720: */
0721: /**
0722: * @return
0723: */
0724: public org.w3c.dom.Element getRootElement() {
0725: return null;
0726: }
0727:
0728: /**
0729: * Get the live instance (if any) for the root live container
0730: * @return the live root instance
0731: */
0732: public Object getRootInstance() {
0733: return null;
0734: }
0735:
0736: /**
0737: *
0738: */
0739: /**
0740: * @param name
0741: * @return
0742: */
0743: public Bean getBean(String name) {
0744: for (Iterator i = beans.iterator(); i.hasNext();) {
0745: Bean c = (Bean) i.next();
0746: if (c.getName().equals(name))
0747: return c;
0748: }
0749: return null;
0750: }
0751:
0752: /**
0753: * Use the junit method to enure that we have an import for this type so that
0754: * the identifier can use its short form. By default explicit imports are ensured
0755: *
0756: * @param type fully-qualified type name
0757: */
0758: public void ensureImportForType(String type) {
0759: getBeanStructureScanner().ensureImportForType(type);
0760: }
0761:
0762: /**
0763: * Create and add a new bean to an existing parent.
0764: *
0765: * @param beanInfo The definition of the bean to create
0766: * @param parent The parent for the new bean
0767: * @param name The instance name for the new bean
0768: * @param facet The (optional) facet to tag this child within the parent
0769: * @return The newly created bean
0770: */
0771: public final Bean addBean(BeanInfo beanInfo, Bean parent,
0772: String name, String facet, Position pos) {
0773: String type = beanInfo.getBeanDescriptor().getBeanClass()
0774: .getName();
0775: name = name != null ? nextAvailableName(name, null, true)
0776: : nextNameForType(type);
0777:
0778: Bean b = newCreatedBean(beanInfo, parent, name, facet, pos);
0779:
0780: // ensure a package import for the bean's type if it has a Java source entry (is renamable)
0781: //if (b.canSetName())
0782: // ensureImportForType(type);
0783:
0784: return b;
0785: }
0786:
0787: /**
0788: * Create binding bean in java source. This should be called
0789: * only to add the binding for a component.
0790: *
0791: * @param name The instance name for the new bean
0792: * @return The newly created bean
0793: */
0794: public final void addBindingBean(String name) {
0795: Bean b = getBean(name);
0796: if (b != null) {
0797: beansToAdd.add(b);
0798: }
0799: }
0800:
0801: /**
0802: * Remove binding bean in java source. This should be called
0803: * only to remove the binding for a component.
0804: *
0805: * @param name The instance name for the new bean
0806: */
0807: public final void removeBindingBean(String name) {
0808: Bean b = getBean(name);
0809: if (b != null) {
0810: beansToRemove.add(b);
0811: }
0812: }
0813:
0814: /**
0815: *
0816: */
0817: /**
0818: * @param bean
0819: * @param newparent
0820: * @param pos
0821: */
0822: public void moveBean(Bean bean, Bean newparent, Position pos) {
0823: Bean oldparent = bean.getParent();
0824: oldparent.removeChild(bean);
0825: newparent.addChild(bean, pos);
0826: }
0827:
0828: /**
0829: *
0830: */
0831: /**
0832: * @param bean
0833: */
0834: public final void removeBean(Bean bean) {
0835: beans.remove(bean); // remove from unit list
0836: Bean parent = bean.getParent();
0837: if (parent != null)
0838: parent.removeChild(bean); // remove from parent list
0839: beansToRemove.add(bean);
0840: bean.removeEntry();
0841: if (!bean.isInserted() && beansToAdd.contains(bean)) {
0842: //It may exist in the beansToAdd list if the removal happens before
0843: //the bean is inserted into java source
0844: beansToAdd.remove(bean);
0845: }
0846: }
0847:
0848: /**
0849: * Ensures that a cross-reference accessor to a sibling bean is in place. Accessor method is of
0850: * the form:
0851: * public <type> get<Mname>() {
0852: * return (<type>) getBean("<bname>");
0853: * }
0854: *
0855: * @param bname
0856: * @param type
0857: */
0858: public void addXRefAccessor(String bname, String type) {
0859: getBeanStructureScanner().addXRefAccessor(bname, type);
0860: }
0861:
0862: //-------------------------------------------------------------------------------- Event Methods
0863:
0864: /**
0865: * Find an (event) method with a given name
0866: *
0867: * @param name
0868: * @return
0869: */
0870: public Method getEventMethod(String name, MethodDescriptor md) {
0871: Class[] pts = md.getMethod().getParameterTypes();
0872: assert Trace.trace("insync.beans", "BU.getEventMethod name:"
0873: + name + " null"); //NOI18N
0874: return javaClass.getMethod(name, pts);
0875: }
0876:
0877: /**
0878: * Find the initializer method for the page
0879: *
0880: * @param name
0881: * @return
0882: */
0883: public Method getInitializerMethod() {
0884: return javaClass.getMethod("init", new Class[0]);
0885: }
0886:
0887: /**
0888: * Add an event method with a given name and event type, and return type
0889: *
0890: * @param md
0891: * @param name
0892: * @return
0893: */
0894: public boolean hasEventMethod(MethodDescriptor md, String name) {
0895: Class[] pts = md.getMethod().getParameterTypes();
0896: return javaClass.getMethod(name, pts) != null;
0897: }
0898:
0899: /**
0900: * Add as needed an event method with a given name and event type, and return type. Do nothing
0901: * if the method is already present.
0902: *
0903: * @param md The MethodDescriptor that identifies the method signature+return.
0904: * @param name The name of the metod to find or create.
0905: * @return The existing or newly created method.
0906: */
0907: public EventMethod ensureEventMethod(MethodDescriptor md,
0908: String name, String defaultBody, String[] parameterNames,
0909: String[] requiredImports) {
0910: return getBeanStructureScanner().ensureEventMethod(md, name,
0911: defaultBody, parameterNames, requiredImports);
0912: }
0913:
0914: //-------------------------------------------------------------------------------------- Utility
0915:
0916: /**
0917: * Instantiate a bean (or any object really) given its Class. Handles primitives and arrays by
0918: * creating default values for them.
0919: *
0920: * @param cls Class to instantiate.
0921: * @return The new instance of the given Class.
0922: */
0923: public Object instantiateBean(Class cls) {
0924: // intercept Basic and Primitive types & just hand-construct them
0925: // Also, intercept classes that lack a null constructor
0926: if (cls == Boolean.TYPE || cls == Boolean.class)
0927: return Boolean.FALSE;
0928: else if (cls == Byte.TYPE || cls == Byte.class)
0929: return new Byte((byte) 0);
0930: else if (cls == Character.TYPE || cls == Character.class)
0931: return new Character('\000');
0932: else if (cls == Double.TYPE || cls == Double.class)
0933: return new Double(0);
0934: else if (cls == Float.TYPE || cls == Float.class)
0935: return new Float(0);
0936: else if (cls == Integer.TYPE || cls == Integer.class)
0937: return new Integer(0);
0938: else if (cls == Long.TYPE || cls == Long.class)
0939: return new Long(0);
0940: else if (cls == Short.TYPE || cls == Short.class)
0941: return new Short((short) 0);
0942: else if (cls == BigDecimal.class)
0943: return new BigDecimal("0");
0944:
0945: try {
0946: if (cls.isArray()) {
0947: return Array.newInstance(cls.getComponentType(), 0);
0948: } else {
0949: if (!cls.isInterface()
0950: && !Modifier.isAbstract(cls.getModifiers())) {
0951: return cls.newInstance();
0952: }
0953: }
0954: } catch (Exception e) {
0955: // EAT: TODO XXX
0956: // Look into what we can do to handle beans that do not have a default constructor ?
0957: // Is there something in the Beans spec that could help ?
0958: ErrorManager.getDefault().notify(
0959: ErrorManager.INFORMATIONAL, e);
0960: }
0961: return null;
0962: }
0963:
0964: /**
0965: * Get the actual class of a bean or supporting object, given its type. The type may be a
0966: * primitive and/or it may be an array.
0967: *
0968: * @param type The type retrieve the Class for.
0969: * @return The Class that represents the given type.
0970: * @throws ClassNotFoundException
0971: */
0972: public Class getBeanClass(String type)
0973: throws ClassNotFoundException {
0974: return ClassUtil.getClass(type, classLoader);
0975: }
0976:
0977: /**
0978: * Get the BeanInfo given a class for a bean.
0979: *
0980: * @param cls The Class of the bean.
0981: * @return The BeanInfo.
0982: */
0983: public static BeanInfo getBeanInfo(Class cls,
0984: ClassLoader classLoader) {
0985: ClassLoader oldContextClassLoader = Thread.currentThread()
0986: .getContextClassLoader();
0987: try {
0988: Thread.currentThread().setContextClassLoader(classLoader);
0989: try {
0990: if (cls == null)
0991: return null;
0992: return Introspector.getBeanInfo(cls,
0993: Introspector.USE_ALL_BEANINFO);
0994: } catch (Exception e) {
0995: e.printStackTrace();
0996: assert Trace.trace("insync.beans", "Caught " + e
0997: + " in BU.getBeanInfo for class:"
0998: + cls.getName()); //NOI18N
0999: } catch (NoClassDefFoundError e) {
1000: throw e;
1001: }
1002: return null;
1003: } finally {
1004: Thread.currentThread().setContextClassLoader(
1005: oldContextClassLoader);
1006: }
1007: }
1008:
1009: /**
1010: * Get the BeanInfo for a bean by type using this unit's class loader.
1011: *
1012: * @param type The fully-qualified bean type name
1013: * @return The BeanInfo, or null if not found.
1014: */
1015: public BeanInfo getBeanInfo(String type) {
1016: ClassLoader oldContextClassLoader = Thread.currentThread()
1017: .getContextClassLoader();
1018: try {
1019: Thread.currentThread().setContextClassLoader(
1020: getClassLoader());
1021: try {
1022: return getBeanInfo(getBeanClass(type), getClassLoader());
1023: } catch (Exception e) {
1024: //System.err.println("Caught " + e + " in BU.getBeanInfo for type:" + type); //NOI18N
1025: assert Trace.trace("insync.beans", "Caught " + e
1026: + " in BU.getBeanInfo for type:" + type); //NOI18N
1027: }
1028: return null;
1029: } finally {
1030: Thread.currentThread().setContextClassLoader(
1031: oldContextClassLoader);
1032: }
1033: }
1034:
1035: //--------------------------------------------------------------------------------------- Output
1036:
1037: /*
1038: * @see org.netbeans.modules.visualweb.insync.Unit#dumpTo(java.io.PrintWriter)
1039: */
1040: public void dumpTo(PrintWriter w) {
1041: w.println("BeansUnit pkg:" + packageName + " name:"
1042: + junit.getName() + " beans:"); //NOI18N
1043: Bean[] beans = getBeans();
1044: for (int bi = 0; bi < beans.length; bi++) {
1045: w.println(" Bean: " + beans[bi].getName()); //NOI18N
1046: Property[] props = beans[bi].getProperties();
1047: for (int pi = 0; pi < props.length; pi++)
1048: w.println(" PropSetting name:" + props[pi].getName()
1049: + " valSrc:" + //NOI18N
1050: props[pi].getValueSource());
1051: }
1052: }
1053:
1054: /*
1055: * @see java.lang.Object#toString()
1056: */
1057: public String toString() {
1058: StringBuffer sb = new StringBuffer(30);
1059: sb.append("[BeansUnit pkg:" + getThisPackageName() + " cls:"
1060: + getThisClassName() + //NOI18N
1061: " name:" + junit.getName() + " beans:"); //NOI18N
1062: Bean[] beans = getBeans();
1063: for (int i = 0; i < beans.length; i++)
1064: sb.append(beans[i].toString());
1065: sb.append("]"); //NOI18N
1066: return sb.toString();
1067: }
1068:
1069: public Model getModel() {
1070: return model;
1071: }
1072:
1073: /**
1074: * Must guarantee to never return null;
1075: *
1076: * @return
1077: */
1078: protected BeanStructureScanner getNewBeanStructureScanner() {
1079: return new BeanStructureScanner(this);
1080: }
1081:
1082: }
|