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: package org.netbeans.modules.form;
042:
043: import com.sun.source.tree.AnnotationTree;
044: import com.sun.source.tree.ClassTree;
045: import com.sun.source.tree.MethodTree;
046: import com.sun.source.tree.ModifiersTree;
047: import com.sun.source.tree.Tree;
048: import com.sun.source.tree.VariableTree;
049: import com.sun.source.util.SourcePositions;
050: import com.sun.source.util.TreePath;
051: import com.sun.source.util.TreeScanner;
052: import java.beans.Introspector;
053: import java.io.IOException;
054: import java.util.ArrayList;
055: import java.util.Collections;
056: import java.util.List;
057: import java.util.logging.Level;
058: import java.util.logging.Logger;
059: import javax.lang.model.element.Element;
060: import javax.lang.model.element.ElementKind;
061: import javax.lang.model.element.ExecutableElement;
062: import javax.lang.model.element.TypeElement;
063: import javax.lang.model.type.TypeKind;
064: import javax.lang.model.type.TypeMirror;
065: import javax.swing.text.Position;
066: import org.netbeans.api.editor.guards.SimpleSection;
067: import org.netbeans.api.java.classpath.ClassPath;
068: import org.netbeans.api.java.source.CancellableTask;
069: import org.netbeans.api.java.source.CompilationController;
070: import org.netbeans.api.java.source.JavaSource;
071: import org.netbeans.api.java.source.TreeMaker;
072: import org.netbeans.api.java.source.WorkingCopy;
073: import org.openide.filesystems.FileObject;
074:
075: /**
076: *
077: * Provides information about the forms java source file.
078: *
079: * @author Tomas Stupka
080: */
081: public class FormJavaSource {
082:
083: private final FormDataObject formDataObject;
084: private List<String> fields = null;
085: private static final String[] PROPERTY_PREFIXES = new String[] {
086: "get", // NOI18N
087: "is" }; // NOI18N
088:
089: public FormJavaSource(FormDataObject formDataObject) {
090: this .formDataObject = formDataObject;
091: }
092:
093: public void refresh() {
094: this .fields = Collections.<String> emptyList();
095: runUserActionTask(new CancellableTask<CompilationController>() {
096: public void cancel() {
097: }
098:
099: public void run(CompilationController controller)
100: throws Exception {
101: controller.toPhase(JavaSource.Phase.PARSED);
102: FormJavaSource.this .fields = getFieldNames(controller);
103: }
104: });
105:
106: }
107:
108: private void runUserActionTask(
109: CancellableTask<CompilationController> task) {
110: FileObject javaFileObject = formDataObject.getPrimaryFile();
111: JavaSource js = JavaSource.forFileObject(javaFileObject);
112: if (js != null) {
113: try {
114: js.runUserActionTask(task, true);
115: } catch (IOException ex) {
116: Logger.getLogger(this .getClass().getName()).log(
117: Level.SEVERE, null, ex);
118: }
119: }
120: }
121:
122: private void runModificationTask(CancellableTask<WorkingCopy> task) {
123: FileObject javaFileObject = formDataObject.getPrimaryFile();
124: JavaSource js = JavaSource.forFileObject(javaFileObject);
125: if (js != null) {
126: try {
127: js.runModificationTask(task).commit();
128: } catch (IOException ex) {
129: Logger.getLogger(this .getClass().getName()).log(
130: Level.SEVERE, null, ex);
131: }
132: }
133: }
134:
135: public boolean containsField(String name, boolean refresh) {
136: if (refresh) {
137: refresh();
138: }
139: return fields != null && fields.contains(name);
140: }
141:
142: private ClassTree findClassTree(CompilationController controller) {
143: String fileName = formDataObject.getPrimaryFile().getName();
144:
145: for (Tree t : controller.getCompilationUnit().getTypeDecls()) {
146: if (t.getKind() == Tree.Kind.CLASS
147: && fileName.equals(((ClassTree) t).getSimpleName()
148: .toString())) {
149: return (ClassTree) t;
150: }
151: }
152: return null;
153: }
154:
155: private static TypeKind primitiveClassToTypeKind(Class clazz) {
156: TypeKind kind = null;
157: if (clazz == char.class) {
158: kind = TypeKind.CHAR;
159: } else if (clazz == boolean.class) {
160: kind = TypeKind.BOOLEAN;
161: } else if (clazz == int.class) {
162: kind = TypeKind.INT;
163: } else if (clazz == long.class) {
164: kind = TypeKind.LONG;
165: } else if (clazz == byte.class) {
166: kind = TypeKind.BYTE;
167: } else if (clazz == short.class) {
168: kind = TypeKind.SHORT;
169: } else if (clazz == float.class) {
170: kind = TypeKind.FLOAT;
171: } else if (clazz == double.class) {
172: kind = TypeKind.DOUBLE;
173: }
174: return kind;
175: }
176:
177: private List<String> findMethodsByReturnType(
178: CompilationController controller, TypeElement celem,
179: Class returnType) {
180: List<String> methods = new ArrayList<String>();
181: TypeMirror type;
182: if (returnType.isPrimitive()) {
183: TypeKind kind = primitiveClassToTypeKind(returnType);
184: type = controller.getTypes().getPrimitiveType(kind);
185: } else {
186: String returnTypeName = returnType.getCanonicalName();
187: TypeElement returnTypeElm = controller.getElements()
188: .getTypeElement(returnTypeName);
189: type = returnTypeElm.asType();
190: }
191: for (Element el : celem.getEnclosedElements()) {
192: if (el.getKind() == ElementKind.METHOD) {
193: ExecutableElement method = (ExecutableElement) el;
194: TypeMirror methodRT = method.getReturnType();
195: if (controller.getTypes().isAssignable(type, methodRT)) {
196: methods.add(method.getSimpleName().toString());
197: }
198: }
199: }
200: return methods;
201: }
202:
203: /**
204: * Returns names for all methods with the specified return type
205: *
206: * @param returnType return type
207: * @return names of all methods with the given return type.
208: */
209: public String[] getMethodNames(final Class returnType) {
210: final Object[] result = new Object[1];
211:
212: runUserActionTask(new CancellableTask<CompilationController>() {
213: public void cancel() {
214: }
215:
216: public void run(CompilationController controller)
217: throws Exception {
218: controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
219:
220: ClassTree ct = findClassTree(controller);
221: if (ct != null) {
222: TreePath cpath = controller.getTrees().getPath(
223: controller.getCompilationUnit(), ct);
224: TypeElement celem = (TypeElement) controller
225: .getTrees().getElement(cpath);
226: List<String> names = findMethodsByReturnType(
227: controller, celem, returnType);
228: result[0] = toArray(names);
229: }
230:
231: }
232: });
233:
234: return result[0] == null ? new String[0] : (String[]) result[0];
235: }
236:
237: public String getAnnotationCode(final String methodName,
238: final Position startPosition, final Position endPosition,
239: final boolean removeAnnotations) {
240: final StringBuilder sb = new StringBuilder();
241: runModificationTask(new CancellableTask<WorkingCopy>() {
242: public void cancel() {
243: }
244:
245: public void run(WorkingCopy wc) throws Exception {
246: wc.toPhase(JavaSource.Phase.RESOLVED);
247: ClassTree ct = findClassTree(wc);
248: int start = startPosition.getOffset();
249: int end = endPosition.getOffset();
250: if (ct != null) {
251: SourcePositions sp = wc.getTrees()
252: .getSourcePositions();
253: for (Tree member : ct.getMembers()) {
254: if (Tree.Kind.METHOD == member.getKind()) {
255: MethodTree method = (MethodTree) member;
256: if (methodName.equals(method.getName()
257: .toString())) {
258: long methodStart = sp
259: .getStartPosition(wc
260: .getCompilationUnit(),
261: method);
262: long methodEnd = sp.getEndPosition(wc
263: .getCompilationUnit(), method);
264: if ((methodStart <= end)
265: && (start <= methodEnd)) {
266: for (AnnotationTree annotation : method
267: .getModifiers()
268: .getAnnotations()) {
269: sb
270: .append(
271: annotation
272: .toString())
273: .append('\n');
274: }
275: }
276: if (removeAnnotations) {
277: ModifiersTree oldModifiers = method
278: .getModifiers();
279: TreeMaker make = wc.getTreeMaker();
280: ModifiersTree newModifiers = make
281: .Modifiers(oldModifiers
282: .getFlags());
283: wc.rewrite(oldModifiers,
284: newModifiers);
285: }
286: }
287: }
288: }
289: }
290: }
291: });
292: return (sb.length() == 0) ? null : sb.toString();
293: }
294:
295: /**
296: * Returns names for all methods with the specified return type which
297: * start with the prefixes "is" and "get"
298: *
299: * @param returnType return type.
300: * @return names of methods.
301: */
302: public String[] getPropertyReadMethodNames(Class returnType) {
303: String[] names = getMethodNames(returnType);
304: List<String> result = new ArrayList<String>(names.length);
305: for (String name : names) {
306: if (!FormJavaSource.extractPropertyName(name).equals("")) { // NOI18N
307: // seems to be property method
308: result.add(name);
309: }
310:
311: }
312:
313: return toArray(result);
314:
315: }
316:
317: public static String extractPropertyName(String methodName) {
318: for (int i = 0; i < PROPERTY_PREFIXES.length; i++) {
319: if (methodName.startsWith(PROPERTY_PREFIXES[i])
320: && methodName.length() > PROPERTY_PREFIXES[i]
321: .length()) {
322: return Introspector.decapitalize(methodName
323: .substring(PROPERTY_PREFIXES[i].length()));
324: }
325: }
326: return ""; // NOI18N
327: }
328:
329: private List<String> getFieldNames(
330: final CompilationController controller) {
331: SimpleSection variablesSection = formDataObject
332: .getFormEditorSupport().getVariablesSection();
333:
334: if (variablesSection == null) {
335: return null;
336: }
337:
338: final int genVariablesStartOffset = variablesSection
339: .getStartPosition().getOffset();
340: final int genVariablesEndOffset = variablesSection
341: .getEndPosition().getOffset();
342:
343: final SourcePositions positions = controller.getTrees()
344: .getSourcePositions();
345:
346: TreeScanner<Void, List<String>> scan = new TreeScanner<Void, List<String>>() {
347: @Override
348: public Void visitClass(ClassTree node, List<String> p) {
349: long startOffset = positions.getStartPosition(
350: controller.getCompilationUnit(), node);
351: long endOffset = positions.getEndPosition(controller
352: .getCompilationUnit(), node);
353: if (genVariablesStartOffset > startOffset
354: && genVariablesEndOffset < endOffset) {
355: for (Tree tree : node.getMembers()) {
356: if (tree.getKind() == Tree.Kind.VARIABLE) {
357: testVariable((VariableTree) tree, p);
358: }
359: }
360: }
361: return null;
362: }
363:
364: private void testVariable(VariableTree node, List<String> p) {
365: long startOffset = positions.getStartPosition(
366: controller.getCompilationUnit(), node);
367: if (startOffset >= genVariablesEndOffset
368: || startOffset <= genVariablesStartOffset) {
369: p.add(node.getName().toString());
370: }
371: }
372: };
373:
374: List<String> fieldNames = new ArrayList<String>();
375: scan.scan(controller.getCompilationUnit(), fieldNames);
376:
377: return fieldNames;
378: }
379:
380: private static String[] toArray(List<String> list) {
381: return list.toArray(new String[list.size()]);
382: }
383:
384: public static boolean isInDefaultPackage(FormModel formModel) {
385: FileObject fdo = FormEditor.getFormDataObject(formModel)
386: .getPrimaryFile();
387: ClassPath cp = ClassPath.getClassPath(fdo, ClassPath.SOURCE);
388: String name = cp.getResourceName(fdo);
389: return name.indexOf('/') < 0;
390: }
391:
392: }
|