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-2007 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: package org.netbeans.modules.visualweb.insync.beans;
042:
043: import java.util.List;
044: import org.netbeans.modules.visualweb.insync.UndoEvent;
045: import org.netbeans.modules.visualweb.insync.java.EventMethod;
046: import org.netbeans.modules.visualweb.insync.java.JavaClass;
047: import org.netbeans.modules.visualweb.insync.java.JavaUnit;
048: import java.beans.MethodDescriptor;
049: import java.io.File;
050: import org.netbeans.modules.visualweb.insync.java.Method;
051: import org.netbeans.modules.visualweb.insync.java.Statement;
052:
053: import org.openide.util.NbBundle;
054:
055: import org.netbeans.modules.visualweb.extension.openide.util.Trace;
056: import java.lang.reflect.Modifier;
057:
058: /**
059: * Manage the methods, fields and such that should be defined for the bean described by my model.
060: *
061: * @author eric
062: *
063: */
064: public class BeanStructureScanner {
065: public static String CTOR = "<init>";
066: public static String ENSURE_INITBLOCK = "ensureInitBlock";
067: // TODO: add code to read the user preference over explicit and
068: // implicit imports.
069: protected boolean explicitImport = true;
070:
071: protected BeansUnit beansUnit;
072: protected JavaUnit javaUnit;
073:
074: //Method info contains the nature of methods need to be in a managed bean.
075: //Sub-classes may alter the method infos according to requirement
076: protected MethodInfo ctorInfo = new MethodInfo(CTOR,
077: Modifier.PUBLIC, Void.TYPE, "", ENSURE_INITBLOCK);
078: protected MethodInfo propertiesInitInfo = ctorInfo;
079: protected MethodInfo destroyInfo;
080:
081: protected Object propertyRegionInsertPosition;
082:
083: public BeanStructureScanner(BeansUnit unit) {
084: super ();
085: this .beansUnit = unit;
086: this .javaUnit = unit.getJavaUnit();
087: }
088:
089: //Methods that need to be in a managed bean
090: protected MethodInfo[] getMethodInfos() {
091: return new MethodInfo[] { ctorInfo };
092: }
093:
094: /**
095: *
096: */
097: protected void ensureInitBlock(MethodInfo mi) {
098: UndoEvent event = null;
099: try {
100: String eventName = NbBundle.getMessage(
101: BeanStructureScanner.class, "EnsureInitBlock"); //NOI18N
102: event = beansUnit.getModel().writeLock(eventName);
103: if (mi.getMethod().hasInitBlock()) {
104: return;
105: }
106: String bodyText = "// "
107: + NbBundle.getMessage(BeansUnit.class,
108: "COMMENT_InitMethodToDoMarker")
109: + "\n"
110: + //NOI18N
111: "// <editor-fold defaultstate=\"collapsed\" desc=\""
112: + NbBundle.getMessage(BeansUnit.class,
113: "COMMENT_InitDescription")
114: + "\">"
115: + //NOI18N
116: "try {\n"
117: + //NOI18N
118: "} catch (Exception e) {\n"
119: + "log(\"Page1 Initialization Failure\", e);\n"
120: + //NOI18N
121: "throw e instanceof FacesException ? (FacesException) e: new FacesException(e);\n"
122: + //NOI18N
123: "}\n"
124: + //NOI18N
125: "// </editor-fold>\n"
126: + //NOI18N
127: "// "
128: + NbBundle.getMessage(BeanStructureScanner.class,
129: "COMMENT_AdditionalCode"); //NOI18N
130: mi.getMethod().replaceBody(bodyText);
131: } finally {
132: if (event != null) {
133: beansUnit.getModel().writeUnlock(event);
134: }
135: }
136: }
137:
138: /**
139: * Add as needed an event method with a given name and event type, and return type. Do nothing
140: * if the method is already present.
141: *
142: * @param md The MethodDescriptor that identifies the method signature+return.
143: * @param name The name of the metod to find or create.
144: * @param defaultBody The default body to be inserted for the event if one does not
145: * already exist, or null to get a generic comment body
146: * @param parameterNames An array of names to be used for the parameters, or null
147: * to use a default algorithm which will derive names from the types
148: * @param requiredImports An array of classes to be imported, or null to import nothing
149: * @return The existing or newly created method.
150: */
151: public EventMethod ensureEventMethod(MethodDescriptor md,
152: String name, String defaultBody, String[] parameterNames,
153: String[] requiredImports) {
154: Class[] pts = md.getMethod().getParameterTypes();
155: EventMethod method = beansUnit.getThisClass().getEventMethod(
156: name, pts);
157: if (method != null) {
158: return method;
159: }
160: Class retType = md.getMethod().getReturnType();
161: String body = defaultBody;
162: if (defaultBody == null) {
163: body = "// "
164: + NbBundle.getMessage(BeanStructureScanner.class,
165: "COMMENT_EventMethodBody"); //NOI18N
166: }
167: body += "\n\n";
168: if (retType != Void.TYPE) {
169: body = body + "return null;"; //NOI18N
170: }
171:
172: String[] pns = parameterNames;
173: if (pns == null) {
174: pns = Naming.paramNames(pts, md.getParameterDescriptors());
175: }
176:
177: UndoEvent event = null;
178: try {
179: String eventName = NbBundle.getMessage(
180: BeanStructureScanner.class, "EnsureEventMethod"); //NOI18N
181: event = beansUnit.getModel().writeLock(eventName);
182: org.netbeans.modules.visualweb.insync.java.MethodInfo info = new org.netbeans.modules.visualweb.insync.java.MethodInfo(
183: name, retType, Modifier.PUBLIC, pns, pts, body,
184: null);
185:
186: return beansUnit.getThisClass().addEventMethod(info);
187:
188: } finally {
189: if (event != null) {
190: beansUnit.getModel().writeUnlock(event);
191: }
192: }
193: }
194:
195: /**
196: * Use the junit method to enure that we have an import for this type so that
197: * the identifier can use its short form. By default explicit imports are ensured
198: *
199: * @param type fully-qualified type name
200: */
201: public void ensureImportForType(String type) {
202: if (explicitImport) {
203: javaUnit.ensureImport(type);
204: } else {
205: int dot = type.lastIndexOf('.');
206: if (dot > 0)
207: javaUnit.ensureImport(type.substring(0, dot + 1) + "*");
208: }
209: }
210:
211: protected Method ensureMethod(Object location, MethodInfo mi) {
212: Method method = beansUnit.getThisClass().getMethod(
213: mi.getName(), null);
214: if (method != null) {
215: return method;
216: }
217: org.netbeans.modules.visualweb.insync.java.MethodInfo info = new org.netbeans.modules.visualweb.insync.java.MethodInfo(
218: mi.getName(), mi.getReturnType(), mi.getModifiers(),
219: null, null, null, mi.getComment());
220: UndoEvent event = null;
221: try {
222: String eventName = NbBundle.getMessage(
223: BeanStructureScanner.class, "EnsureMethod"); //NOI18N
224: event = beansUnit.getModel().writeLock(eventName);
225: method = beansUnit.getThisClass().addMethod(info);
226: } finally {
227: if (event != null) {
228: beansUnit.getModel().writeUnlock(event);
229: }
230: }
231: return method;
232: }
233:
234: /**
235: * Return the last method added.
236: *
237: * @return
238: */
239: protected void ensureMethods() {
240: Method m = null;
241: MethodInfo[] methodInfos = getMethodInfos();
242: for (int i = 0; i < methodInfos.length; i++) {
243: m = ensureMethod(null, methodInfos[i]);
244: methodInfos[i].setMethod(m);
245: try {
246: String ensureMethodName = methodInfos[i]
247: .getEnsureMethodName();
248: if (ensureMethodName != null) {
249: java.lang.reflect.Method m1 = BeanStructureScanner.class
250: .getDeclaredMethod(ensureMethodName,
251: new Class[] { MethodInfo.class });
252: m1.invoke(this , new Object[] { methodInfos[i] });
253: }
254: } catch (Exception e) {
255: //This should not happen
256: assert Trace.trace("insync.beans", e.getMessage()); //NOI18N
257: }
258: }
259: }
260:
261: /**
262: * TODO: We need to change how this region is created.
263: */
264: protected Object/*VariableElement*/ensurePropertyRegion() {
265: /*//NB6.0
266: JavaClassAdapter javaClass = beansUnit.getThisClass();
267: UndoEvent event = null;
268: try {
269: String eventName = NbBundle.getMessage(BeanStructureScanner.class, "EnsurePropertyRegion"); //NOI18N
270: event = beansUnit.getModel().writeLock(eventName);
271: boolean rollback = true;
272: try {
273: JMIUtils.beginTrans(true);
274: Field ph = javaClass.getField("__placeholder"); //NOI18N
275:
276: // Insert a placeholder field at the top of the class and put a region fold comment above it
277: if (ph == null) {
278: ph = javaClass.addField("__placeholder", Integer.TYPE, null, false); //NOI18N
279: ph.setModifiers(Modifier.PRIVATE);
280: ph.setJavadocText("<editor-fold defaultstate=\"collapsed\" desc=\"" + //NOI18N
281: NbBundle.getMessage(BeanStructureScanner.class, "COMMENT_DefDescription") + "\">"); //NOI18N
282: }
283: rollback = false;
284: return ph;
285: }finally {
286: JMIUtils.endTrans(rollback);
287: }
288: }finally {
289: if(event != null) {
290: beansUnit.getModel().writeUnlock(event);
291: }
292: }
293: //*/
294: return null;
295: }
296:
297: /**
298: *
299: */
300: protected JavaClass ensureThisClass() {
301:
302: /*TODO - Deva
303: // get the expected classname from the filename
304: String cname = thisClassName(getJavaUnit().getName());
305:
306: // scan for class by name. if it is there return it, possibly tweaking it first
307: Clazz[] classes = getJavaUnit().getClazzes();
308: for (int i = 0; i < classes.length; i++) {
309: Clazz cls = classes[i];
310: assert Trace.trace("insync.beans", "BU.findThisClass name:" + cls.getName() + " mods:" //NOI18N
311: + cls.getModifiers());
312:
313: // grab the one with the matching name, making it the only public one if necessary
314: if (cls.getName().equals(cname)) {
315: if (cls.getAccessModifiers() != Modifiers.PUBLIC)
316: cls.setAccessModifiers(Modifiers.PUBLIC);
317: for (int j = 0; j < classes.length; j++)
318: if (j != i && classes[j].getAccessModifiers() == Modifiers.PUBLIC)
319: classes[j].setAccessModifiers(0);
320: return cls;
321: }
322: }
323:
324: // no match--grab the public one & rename it
325: for (int i = 0; i < classes.length; i++) {
326: Clazz cls = classes[i];
327: assert Trace.trace("insync.beans", "BU.findThisClass name:" + cls.getName() + " mods:" //NOI18N
328: + cls.getModifiers());
329:
330: if (cls.getAccessModifiers() == Modifiers.PUBLIC) {
331: cls.setName(cname);
332: return cls;
333: }
334: }
335:
336: // no match and no public class--
337: // create the class definition & add a comment
338: Clazz clazz = getJavaUnit().addClass(null, cname);
339: clazz.setModifiers(Modifiers.PUBLIC);
340: String suggestedSuperclass = getSuggestedThisClassSuperclass();
341: if (suggestedSuperclass != null) {
342: clazz.setSuperclass(suggestedSuperclass);
343: ensureImportForType(suggestedSuperclass);
344: }
345: Comment c = clazz.addComment(Comment.STYLE_DOC);
346: c.setBody(getThisClassComment());
347: c.setPrewhite(LineColumn.make(2, 0));
348: clazz.setPrewhite(LineColumn.make(1, 0));
349:
350: return clazz;
351: **/
352: return null;
353: }
354:
355: /**
356: * Ensures that a cross-reference accessor to a sibling bean is in place. Accessor method is of
357: * the form:
358: * public <type> get<Mname>() {
359: * return (<type>) getBean("<bname>");
360: * }
361: *
362: * @param bname
363: * @param type
364: */
365: public void addXRefAccessor(String bname, String type) {
366: String mname = bname.replaceAll("/", "_");
367: // Identify whether type has a package name, if not then skip creating an accessor for it
368: int index = type.lastIndexOf('.');
369: if (index == -1)
370: return;
371:
372: UndoEvent event = null;
373: try {
374: String eventName = NbBundle.getMessage(
375: BeanStructureScanner.class, "EnsureXrefAccessor"); //NOI18N
376: event = beansUnit.getModel().writeLock(eventName);
377: String simpleName = type;
378: if (beansUnit.getJavaUnit().ensureImport(type)) {
379: simpleName = type.substring(index + 1);
380: }
381: String body = "return (" + simpleName + ")getBean(\""
382: + bname + "\");"; //NOI18N
383: String comment = NbBundle.getMessage(
384: BeanStructureScanner.class,
385: "COMMENT_GetScopedBeanComment"); //NOI18N
386: org.netbeans.modules.visualweb.insync.java.MethodInfo info = new org.netbeans.modules.visualweb.insync.java.MethodInfo(
387: "get" + mname, //NOI18N
388: null, Modifier.PROTECTED, null, null, body, comment);
389: info.setReturnTypeName(type);
390: beansUnit.getThisClass().addMethod(info);
391: } finally {
392: if (event != null) {
393: beansUnit.getModel().writeUnlock(event);
394: }
395: }
396: }
397:
398: /**
399: * Finds a possibly existing cross-reference accessor to a sibling bean
400: */
401: protected Method findXRefAccessor(String name) {
402: return beansUnit.getThisClass().getMethod("get" + name,
403: new Class[] {}); //NOI18N
404: }
405:
406: public String getComment(String id) {
407: return NbBundle.getMessage(BeanStructureScanner.class, id);
408: }
409:
410: public Method getConstructorMethod() {
411: return ctorInfo.getMethod();
412: }
413:
414: public Method getPropertiesInitMethod() {
415: return propertiesInitInfo.getMethod();
416: }
417:
418: /**
419: * @return
420: */
421: public Method getDestroyMethod() {
422: return beansUnit.getThisClass().getMethod(
423: destroyInfo.getName(), new Class[] {});
424: }
425:
426: public List<Statement> getPropertiesInitStatements() {
427: return propertiesInitInfo.getMethod()
428: .getPropertySetStatements();
429: }
430:
431: public JavaUnit getJavaUnit() {
432: return javaUnit;
433: }
434:
435: public String getSuggestedThisClassSuperclass() {
436: return null;
437: }
438:
439: public String getThisClassComment() {
440: return NbBundle.getMessage(BeanStructureScanner.class,
441: "COMMENT_BeanClassComment"); //NOI18N
442: }
443:
444: /**
445: * Removes a possibly existing cross-reference accessor to a sibling bean
446: */
447: /**
448: * @param name
449: */
450: public void removeXRefAccessor(String name) {
451: Method m = findXRefAccessor(name);
452: if (m != null)
453: m.remove();
454: }
455:
456: public void scan() {
457: //clazz = ensureThisClass();
458: ensureMethods();
459: ensurePropertyRegion();
460: }
461:
462: /**
463: *
464: */
465: protected String this ClassName(String filename) {
466: int suffix = filename.lastIndexOf('.');
467: int dir = filename.lastIndexOf(File.separatorChar);
468: int start = (dir >= 0) ? dir + 1 : 0;
469: if (suffix > 0)
470: return filename.substring(start, suffix);
471: return filename.substring(start);
472: }
473:
474: public class MethodInfo {
475: String name, comment, ensureMethodName, exception;
476: Class retType;
477: int modifiers;
478: Method method;
479:
480: public MethodInfo(String name, int modifiers, Class retType,
481: String comment, String ensureMethodName,
482: String exception) {
483: this .retType = retType;
484: this .modifiers = modifiers;
485: this .name = name;
486: this .comment = comment;
487: this .ensureMethodName = ensureMethodName;
488: this .exception = exception;
489: }
490:
491: public MethodInfo(String name, int modifiers, Class retType,
492: String comment, String ensureMethodName) {
493: this (name, modifiers, retType, comment, ensureMethodName,
494: null);
495: }
496:
497: public MethodInfo(String name, int modifiers, Class retType,
498: String comment) {
499: this (name, modifiers, retType, comment, null, null);
500: }
501:
502: public MethodInfo(String name, String comment) {
503: this (name, Modifier.PUBLIC, Void.TYPE, comment, null, null); //NOI18N
504: }
505:
506: public MethodInfo(String name) {
507: this (name, Modifier.PUBLIC, Void.TYPE, "", null, null); //NOI18N
508: }
509:
510: public String getName() {
511: return name;
512: }
513:
514: public int getModifiers() {
515: return modifiers;
516: }
517:
518: public Class getReturnType() {
519: return retType;
520: }
521:
522: public String getExceptionName() {
523: return exception;
524: }
525:
526: public Method getMethod() {
527: return method;
528: }
529:
530: public void setMethod(Method method) {
531: this .method = method;
532: }
533:
534: public void setEnsureMethodName(String ensureMethodName) {
535: this .ensureMethodName = ensureMethodName;
536: }
537:
538: public String getEnsureMethodName() {
539: return ensureMethodName;
540: }
541:
542: public String getComment() {
543: return comment;
544: }
545:
546: public void setComment(String comment) {
547: this.comment = comment;
548: }
549: }
550: }
|