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.java.editor.codegen;
042:
043: import com.sun.source.tree.BlockTree;
044: import com.sun.source.tree.ClassTree;
045: import com.sun.source.tree.ExpressionTree;
046: import com.sun.source.tree.MethodTree;
047: import com.sun.source.tree.Scope;
048: import com.sun.source.tree.StatementTree;
049: import com.sun.source.tree.Tree;
050: import com.sun.source.tree.TypeParameterTree;
051: import com.sun.source.tree.VariableTree;
052: import com.sun.source.util.TreePath;
053: import com.sun.source.util.Trees;
054: import java.awt.Dialog;
055: import java.io.IOException;
056: import java.util.ArrayList;
057: import java.util.Collections;
058: import java.util.EnumSet;
059: import java.util.Iterator;
060: import java.util.LinkedHashMap;
061: import java.util.List;
062: import java.util.Map;
063: import java.util.Set;
064: import javax.lang.model.element.Element;
065: import javax.lang.model.element.ExecutableElement;
066: import javax.lang.model.element.Modifier;
067: import javax.lang.model.element.TypeElement;
068: import javax.lang.model.element.VariableElement;
069: import javax.lang.model.type.DeclaredType;
070: import javax.lang.model.type.ExecutableType;
071: import javax.lang.model.type.TypeKind;
072: import javax.lang.model.type.TypeMirror;
073: import javax.lang.model.util.ElementFilter;
074: import javax.lang.model.util.Elements;
075: import javax.swing.text.JTextComponent;
076: import org.netbeans.api.java.source.Task;
077: import org.netbeans.api.java.source.CompilationController;
078: import org.netbeans.api.java.source.ElementHandle;
079: import org.netbeans.api.java.source.JavaSource;
080: import org.netbeans.api.java.source.ModificationResult;
081: import org.netbeans.api.java.source.TreeMaker;
082: import org.netbeans.api.java.source.WorkingCopy;
083: import org.netbeans.modules.editor.java.Utilities;
084: import org.netbeans.modules.java.editor.codegen.ui.DelegatePanel;
085: import org.netbeans.modules.java.editor.codegen.ui.ElementNode;
086: import org.openide.DialogDescriptor;
087: import org.openide.DialogDisplayer;
088: import org.openide.util.Exceptions;
089: import org.openide.util.NbBundle;
090:
091: /**
092: *
093: * @author Dusan Balek
094: */
095: public class DelegateMethodGenerator implements CodeGenerator {
096:
097: public static class Factory implements CodeGenerator.Factory {
098:
099: private static final String ERROR = "<error>"; //NOI18N
100:
101: public Factory() {
102: }
103:
104: public Iterable<? extends CodeGenerator> create(
105: CompilationController controller, TreePath path)
106: throws IOException {
107: path = Utilities
108: .getPathElementOfKind(Tree.Kind.CLASS, path);
109: if (path == null)
110: return Collections.emptySet();
111: controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
112: Elements elements = controller.getElements();
113: TypeElement typeElement = (TypeElement) controller
114: .getTrees().getElement(path);
115: if (typeElement == null || !typeElement.getKind().isClass())
116: return Collections.emptySet();
117: Trees trees = controller.getTrees();
118: Scope scope = trees.getScope(path);
119: Map<Element, List<ElementNode.Description>> map = new LinkedHashMap<Element, List<ElementNode.Description>>();
120: TypeElement cls;
121: while (scope != null
122: && (cls = scope.getEnclosingClass()) != null) {
123: DeclaredType type = (DeclaredType) cls.asType();
124: for (VariableElement field : ElementFilter
125: .fieldsIn(elements.getAllMembers(cls))) {
126: if (!ERROR.contentEquals(field.getSimpleName())
127: && !field.asType().getKind().isPrimitive()
128: && trees.isAccessible(scope, field, type)) {
129: List<ElementNode.Description> descriptions = map
130: .get(field.getEnclosingElement());
131: if (descriptions == null) {
132: descriptions = new ArrayList<ElementNode.Description>();
133: map.put(field.getEnclosingElement(),
134: descriptions);
135: }
136: descriptions.add(ElementNode.Description
137: .create(field, null, false, false));
138: }
139: }
140: scope = scope.getEnclosingScope();
141: }
142: List<ElementNode.Description> descriptions = new ArrayList<ElementNode.Description>();
143: for (Map.Entry<Element, List<ElementNode.Description>> entry : map
144: .entrySet())
145: descriptions.add(ElementNode.Description.create(entry
146: .getKey(), entry.getValue(), false, false));
147: if (descriptions.isEmpty())
148: return Collections.emptySet();
149: Collections.reverse(descriptions);
150: return Collections.singleton(new DelegateMethodGenerator(
151: ElementNode.Description.create(descriptions)));
152: }
153: }
154:
155: private ElementNode.Description description;
156:
157: /** Creates a new instance of DelegateMethodGenerator */
158: private DelegateMethodGenerator(ElementNode.Description description) {
159: this .description = description;
160: }
161:
162: public String getDisplayName() {
163: return org.openide.util.NbBundle.getMessage(
164: DelegateMethodGenerator.class, "LBL_delegate_method"); //NOI18N
165: }
166:
167: public void invoke(JTextComponent component) {
168: final DelegatePanel panel = new DelegatePanel(component,
169: description);
170: DialogDescriptor dialogDescriptor = GeneratorUtils
171: .createDialogDescriptor(panel, NbBundle.getMessage(
172: ConstructorGenerator.class,
173: "LBL_generate_delegate")); //NOI18N
174: Dialog dialog = DialogDisplayer.getDefault().createDialog(
175: dialogDescriptor);
176: dialog.setVisible(true);
177: if (dialogDescriptor.getValue() == dialogDescriptor
178: .getDefaultValue()) {
179: JavaSource js = JavaSource.forDocument(component
180: .getDocument());
181: if (js != null) {
182: try {
183: final int caretOffset = component
184: .getCaretPosition();
185: ModificationResult mr = js
186: .runModificationTask(new Task<WorkingCopy>() {
187: public void run(WorkingCopy copy)
188: throws IOException {
189: copy
190: .toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
191: TreePath path = copy
192: .getTreeUtilities()
193: .pathFor(caretOffset);
194: path = Utilities
195: .getPathElementOfKind(
196: Tree.Kind.CLASS,
197: path);
198: int idx = GeneratorUtils
199: .findClassMemberIndex(copy,
200: (ClassTree) path
201: .getLeaf(),
202: caretOffset);
203: ElementHandle<? extends Element> handle = panel
204: .getDelegateField();
205: VariableElement delegate = handle != null ? (VariableElement) handle
206: .resolve(copy)
207: : null;
208: ArrayList<ExecutableElement> methods = new ArrayList<ExecutableElement>();
209: for (ElementHandle<? extends Element> elementHandle : panel
210: .getDelegateMethods())
211: methods
212: .add((ExecutableElement) elementHandle
213: .resolve(copy));
214: generateDelegatingMethods(copy,
215: path, delegate, methods,
216: idx);
217: }
218: });
219: GeneratorUtils.guardedCommit(component, mr);
220: } catch (IOException ex) {
221: Exceptions.printStackTrace(ex);
222: }
223: }
224: }
225: }
226:
227: public static ElementNode.Description getAvailableMethods(
228: final JTextComponent component,
229: final ElementHandle<? extends Element> elementHandle) {
230: if (elementHandle.getKind().isField()) {
231: JavaSource js = JavaSource.forDocument(component
232: .getDocument());
233: if (js != null) {
234: try {
235: final int caretOffset = component
236: .getCaretPosition();
237: final ElementNode.Description[] description = new ElementNode.Description[1];
238: js.runUserActionTask(
239: new Task<CompilationController>() {
240:
241: public void run(
242: CompilationController controller)
243: throws IOException {
244: VariableElement field = (VariableElement) elementHandle
245: .resolve(controller);
246: if (field.asType().getKind() == TypeKind.DECLARED) {
247: DeclaredType type = (DeclaredType) field
248: .asType();
249: Trees trees = controller
250: .getTrees();
251: Scope scope = controller
252: .getTreeUtilities()
253: .scopeFor(caretOffset);
254: Map<Element, List<ElementNode.Description>> map = new LinkedHashMap<Element, List<ElementNode.Description>>();
255: for (ExecutableElement method : ElementFilter
256: .methodsIn(controller
257: .getElements()
258: .getAllMembers(
259: (TypeElement) type
260: .asElement()))) {
261: if (trees
262: .isAccessible(
263: scope,
264: method,
265: type)) {
266: List<ElementNode.Description> descriptions = map
267: .get(method
268: .getEnclosingElement());
269: if (descriptions == null) {
270: descriptions = new ArrayList<ElementNode.Description>();
271: map
272: .put(
273: method
274: .getEnclosingElement(),
275: descriptions);
276: }
277: descriptions
278: .add(ElementNode.Description
279: .create(
280: method,
281: null,
282: true,
283: false));
284: }
285: }
286: List<ElementNode.Description> descriptions = new ArrayList<ElementNode.Description>();
287: for (Map.Entry<Element, List<ElementNode.Description>> entry : map
288: .entrySet())
289: descriptions
290: .add(ElementNode.Description
291: .create(
292: entry
293: .getKey(),
294: entry
295: .getValue(),
296: false,
297: false));
298: if (!descriptions.isEmpty())
299: Collections
300: .reverse(descriptions);
301: description[0] = ElementNode.Description
302: .create(descriptions);
303: }
304: }
305: }, true);
306: return description[0];
307: } catch (IOException ex) {
308: Exceptions.printStackTrace(ex);
309: }
310: }
311: }
312: return null;
313: }
314:
315: private static void generateDelegatingMethods(WorkingCopy wc,
316: TreePath path, VariableElement delegate,
317: Iterable<? extends ExecutableElement> methods, int index) {
318: assert path.getLeaf().getKind() == Tree.Kind.CLASS;
319: TypeElement te = (TypeElement) wc.getTrees().getElement(path);
320: if (te != null) {
321: TreeMaker make = wc.getTreeMaker();
322: ClassTree nue = (ClassTree) path.getLeaf();
323: for (ExecutableElement executableElement : methods)
324: nue = make.insertClassMember(nue, index,
325: createDelegatingMethod(wc, delegate,
326: executableElement, (DeclaredType) te
327: .asType()));
328: wc.rewrite(path.getLeaf(), nue);
329: }
330: }
331:
332: private static MethodTree createDelegatingMethod(WorkingCopy wc,
333: VariableElement delegate, ExecutableElement method,
334: DeclaredType type) {
335: TreeMaker make = wc.getTreeMaker();
336: ExecutableType methodType = (ExecutableType) wc.getTypes()
337: .asMemberOf((DeclaredType) delegate.asType(), method);
338: Set<Modifier> mods = new java.util.HashSet<Modifier>(method
339: .getModifiers());
340: mods.remove(Modifier.ABSTRACT);
341: mods.remove(Modifier.NATIVE);
342:
343: boolean useThisToDereference = false;
344: String delegateName = delegate.getSimpleName().toString();
345:
346: for (VariableElement ve : method.getParameters()) {
347: if (delegateName.equals(ve.getSimpleName().toString())) {
348: useThisToDereference = true;
349: break;
350: }
351: }
352:
353: List<VariableTree> params = new ArrayList<VariableTree>();
354: List<ExpressionTree> args = new ArrayList<ExpressionTree>();
355:
356: Iterator<? extends VariableElement> it = method.getParameters()
357: .iterator();
358: Iterator<? extends TypeMirror> tIt = methodType
359: .getParameterTypes().iterator();
360: while (it.hasNext() && tIt.hasNext()) {
361: VariableElement ve = it.next();
362: params.add(make.Variable(make.Modifiers(EnumSet
363: .noneOf(Modifier.class)), ve.getSimpleName(), make
364: .Type(tIt.next()), null));
365: args.add(make.Identifier(ve.getSimpleName()));
366: }
367:
368: ExpressionTree methodSelect = useThisToDereference ? make
369: .MemberSelect(make.Identifier("this"), delegate
370: .getSimpleName()) : make.Identifier(delegate
371: .getSimpleName()); //NOI18N
372: ExpressionTree exp = make.MethodInvocation(Collections
373: .<ExpressionTree> emptyList(), make.MemberSelect(
374: methodSelect, method.getSimpleName()), args);
375: StatementTree stmt = method.getReturnType().getKind() == TypeKind.VOID ? make
376: .ExpressionStatement(exp)
377: : make.Return(exp);
378: BlockTree body = make.Block(Collections.singletonList(stmt),
379: false);
380:
381: List<ExpressionTree> throwsClause = new ArrayList<ExpressionTree>();
382: for (TypeMirror tm : methodType.getThrownTypes())
383: throwsClause.add((ExpressionTree) wc.getTreeMaker()
384: .Type(tm));
385:
386: return make.Method(make.Modifiers(mods),
387: method.getSimpleName(), make.Type(methodType
388: .getReturnType()), Collections
389: .<TypeParameterTree> emptyList(), params,
390: throwsClause, body, null);
391: }
392: }
|