0001: package tide.editor.completions;
0002:
0003: import tide.editor.*;
0004: import tide.utils.*;
0005: import tide.syntaxtree.*;
0006: import tide.sources.*;
0007: import tide.classsyntax.*;
0008: import javaparser.*;
0009: import snow.texteditor.*;
0010: import javax.swing.*;
0011: import javax.swing.event.*;
0012: import javax.swing.text.*;
0013: import java.util.*;
0014: import java.util.regex.*;
0015: import java.awt.EventQueue;
0016: import java.awt.Point;
0017: import java.awt.Rectangle;
0018: import java.awt.event.*;
0019:
0020: /** Some delegated inserts called from the EditorDocumentFilter.
0021: * may popup some completion proposal dialogs in certain cases.
0022: * (on CTRL+space, CTRL+T, dot, ampersand, ...)
0023: *
0024: * TODO: wait some 200ms and only popup if nothing else was typed...
0025: * TODO: ctrl+space ignores already typed items (for attributes)
0026: */
0027: public class CompletionManager {
0028: final EditorPanel editor;
0029:
0030: public final boolean allowLazyInexactSearch = false; // maybe later, but should show an hint !
0031:
0032: // only contains the arguments completions dialogs (not modal)
0033: final ArrayList<CompletionDialog> openedDialogs = new ArrayList<CompletionDialog>();
0034:
0035: public CompletionManager(EditorPanel editor) {
0036: this .editor = editor;
0037: }
0038:
0039: /** Called when changing source in the editor panel.
0040: */
0041: public void closeOpenedDialogs() {
0042: for (int i = openedDialogs.size() - 1; i >= 0; i--) //reverse
0043: {
0044: CompletionDialog cd = openedDialogs.get(i);
0045: cd.cancelDialog();
0046: openedDialogs.remove(cd);
0047: }
0048: }
0049:
0050: /** called when "esc" pressed in the editor
0051: */
0052: public void closeLastCompletionDialog() {
0053: int n = openedDialogs.size();
0054: if (n > 0) {
0055: CompletionDialog cd = openedDialogs.get(n - 1);
0056: cd.cancelDialog();
0057: openedDialogs.remove(cd);
0058: }
0059: }
0060:
0061: /** Shows all types in the project and libs
0062: * (TODO: look at name starting with typed text fragment before position if any)
0063: */
0064: public void controlTPressed(FileItem editedSource,
0065: SimpleDocument doc, int posInSource, boolean projectOnly,
0066: final String initialTextForBrowseMode) {
0067: Rectangle posPt; // = new Rectangle(0,0,10,10);
0068: try {
0069: posPt = editor.getTextPane().modelToView(posInSource);
0070: Point pts = editor.getTextPane().getLocationOnScreen();
0071: ImportCompletionDialog acd = new ImportCompletionDialog(
0072: true, MainEditorFrame.instance,
0073: (int) (pts.getX() + posPt.getX()), (int) (pts
0074: .getY()
0075: + posPt.getY() + editor.getTextPane()
0076: .getFont().getSize()), editor.getTextPane()
0077: .getDocument(), posInSource, projectOnly,
0078: initialTextForBrowseMode);
0079:
0080: if (!acd.getWasCancelled()
0081: && initialTextForBrowseMode == null) {
0082: String sel = acd.getSelectionAndClear();
0083: doc.insertString(sel, posInSource);
0084: }
0085: } catch (Exception e) {
0086: e.printStackTrace();
0087: }
0088: }
0089:
0090: public void dotWithoutCompletion(FileItem editedSource,
0091: SimpleDocument doc, int posInSource) {
0092: if (editedSource == null)
0093: return;
0094: if (!editedSource.isEditable())
0095: return;
0096: try {
0097: doc.insertString(".", posInSource);
0098: } catch (Exception e) {
0099: e.printStackTrace();
0100: }
0101: }
0102:
0103: /** Completion with local variables.
0104: * (like "this" plus local variables) + types starting with
0105: * TODO: better use scopes (let CTRL+ space trigger other completions... ??)
0106: */
0107: public void controlSpacePressed(final FileItem editedSource,
0108: final SimpleDocument doc, final int posInSource) {
0109: // TODO: with the new parser
0110: SimplifiedSyntaxTree2 sst = editedSource
0111: .getSimplifiedSyntaxTreeIfAlreadyMade();
0112: if (sst == null)
0113: return;
0114:
0115: // can be used when already typing some incomplete ID
0116: final ParsedID pid = SyntaxUtils.getJavaIdentifierBefore(doc,
0117: posInSource, "this"); // [Feb2008] to really enforce "this" + dot
0118: final SingleClassLoader scl = SingleClassLoader
0119: .createSingleClassLoader();
0120: final IDChain resolver = new IDChain(pid, editedSource, doc,
0121: posInSource, ".", scl);
0122:
0123: final int[] lineCol = DocumentUtils.getLineColumnNumbers(doc,
0124: posInSource);
0125: //TypeInterface deepestTypeAtPos = sst.getDeepestTypeAt(lineCol[0]+1, lineCol[1]+1, true);
0126: RAWParserTreeNode tokenAtOrAfterPosition = TreeUtils
0127: .getTokenNodeAtOrAfterPosition(
0128: sst.getRawParserResult(), lineCol[0],
0129: lineCol[1]);
0130: List<Parameter> params = new ArrayList<Parameter>();
0131: TreeUtils.collectLocalVariablesBefore(tokenAtOrAfterPosition,
0132: lineCol[0], params);
0133: List<AttrCompletionItem> av = new ArrayList<AttrCompletionItem>();
0134: Set<String> alreadyAddedNames = new HashSet<String>();
0135: for (Parameter p : params) {
0136: if (!alreadyAddedNames.contains(p.name)) {
0137: av.add(AttrCompletionItem.createParameter(p.name,
0138: p.type));
0139: alreadyAddedNames.add(p.name);
0140: }
0141: }
0142:
0143: // [Jan2007]: look at eventual classes starting with that name !
0144: //
0145: String start = pid.identifierChain;
0146: if (start.length() > 0) // && Character.isUpperCase(start.charAt(0)))
0147: {
0148: MainEditorFrame.debugOut("invalid resolver, looking for "
0149: + start);
0150: List<FileItem> hits = TypeLocator
0151: .searchSimpleNamesStartingWith(start);
0152: for (FileItem fi : hits) {
0153: av.add(AttrCompletionItem.createNameCompletionForClass(
0154: fi, start));
0155: }
0156: }
0157:
0158: // [Feb2007]: add the members of this class.
0159: // [Feb2008]: use the real type at caret. the one ot "this."
0160: // [Feb2008]: put the code in a method, to share with CtrlSpace action
0161: String resolvedClassName = editedSource.getJavaName();
0162: if (resolver != null
0163: && resolver.getLastChainComponent() != null) {
0164: resolvedClassName = resolveClassName(editedSource, resolver
0165: .getLastChainComponent(), scl);
0166: }
0167:
0168: //System.out.println("Add attrs for "+resolvedClassName);
0169: if (resolvedClassName != null) {
0170: boolean onlyStatic = false;
0171: av.addAll(ClassSyntaxManager.getAttributes(scl,
0172: resolvedClassName, null, true, true, true,
0173: onlyStatic));
0174: }
0175:
0176: try {
0177: Rectangle posPt = editor.getTextPane().modelToView(
0178: posInSource);
0179: Point pts = editor.getTextPane().getLocationOnScreen();
0180:
0181: //TODO: look at class name and edited class name to decide if package scope enabled...
0182:
0183: final AttributeCompletionDialog acd = new AttributeCompletionDialog(
0184: MainEditorFrame.instance, (int) (pts.getX() + posPt
0185: .getX()),
0186: (int) (pts.getY() + posPt.getY() + editor
0187: .getTextPane().getFont().getSize()), doc,
0188: posInSource, "Local variables completion (+ "
0189: + resolvedClassName + ")", false); // show also private...
0190:
0191: acd.setCompletionItems(av);
0192: acd.addFactoryBarForCTRLSpaceCompletion();
0193: acd.setVisible(true); // MODAL !
0194:
0195: String specialCompletionPrepend = acd
0196: .getSpecialCompletionPrepend();
0197: int rewind = acd.caretRewind;
0198:
0199: doc.insertString(acd.getSelectionAndClear(), posInSource);
0200:
0201: if (specialCompletionPrepend != null
0202: && specialCompletionPrepend.length() > 0) {
0203: doc.insertString(specialCompletionPrepend, posInSource);
0204: }
0205:
0206: if (rewind != 0) {
0207: editor.getTextPane().setCaretPosition(
0208: editor.getTextPane().getCaretPosition()
0209: - rewind);
0210: }
0211: } catch (Exception e) {
0212: e.printStackTrace();
0213: }
0214: }
0215:
0216: /** when @ is pressed, propose some javadoc OR annotations
0217: @return the replaced text
0218: */
0219: public String ampersandPressed(DocumentFilter.FilterBypass fb,
0220: int offset, int length, AttributeSet attrs)
0221: throws Exception {
0222: Document doc = editor.getTextPane().getDocument();
0223: boolean inComment = false;
0224: boolean inString = false;
0225: //Vector<String>[] args = null;
0226: String preceedingTokenOfInterrest = "";
0227: try {
0228: String txtUpto = doc.getText(0, offset);
0229: inString = SyntaxUtils.isInString(txtUpto, offset);
0230: if (inString) {
0231: fb.replace(offset, 0, "@", attrs);
0232: return "@";
0233: }
0234: inComment = SyntaxUtils.isInComment(txtUpto, offset);
0235: if (txtUpto.endsWith("{"))
0236: preceedingTokenOfInterrest = "{";
0237: if (txtUpto.endsWith("//"))
0238: preceedingTokenOfInterrest = "//"; //JML
0239: } catch (RuntimeException ignore) {
0240: }
0241:
0242: try {
0243: Rectangle posPt = editor.getTextPane().modelToView(offset);
0244: Point pts = editor.getTextPane().getLocationOnScreen();
0245:
0246: AmpersandCompletionDialog acd = new AmpersandCompletionDialog(
0247: inComment, MainEditorFrame.instance, (int) (pts
0248: .getX() + posPt.getX()), (int) (pts.getY()
0249: + posPt.getY() + editor.getTextPane()
0250: .getFont().getSize()), editor.getTextPane()
0251: .getDocument(), offset,
0252: preceedingTokenOfInterrest);
0253:
0254: String repl = "";
0255: if (!acd.getWasCancelled()) {
0256: repl = acd.getSelection();
0257: } else {
0258: repl = "@";
0259: }
0260:
0261: fb.replace(offset, length, repl, attrs);
0262: return repl;
0263:
0264: } catch (Exception e) {
0265: throw e;
0266: }
0267:
0268: //return "";
0269:
0270: }
0271:
0272: /** Either shows the methods "xxx(..." or the constructors "Xxx(..."
0273: */
0274: public String openingParenthesisPressed(FileItem editedSource,
0275: DocumentFilter.FilterBypass fb, int offset, int length,
0276: AttributeSet attrs) throws Exception {
0277:
0278: if (isInStringOrComment(offset)) {
0279: try {
0280: fb.replace(offset, length, "(", attrs);
0281: } catch (Exception e) {
0282: e.printStackTrace();
0283: }
0284: return "(";
0285: }
0286:
0287: SimpleDocument doc = editor.getDocument();
0288:
0289: final SingleClassLoader scl = SingleClassLoader
0290: .createSingleClassLoader();
0291:
0292: // detect the identifier before offset
0293: // this is trimmed and without spaces. ex: System.out.println
0294: // or a().re[][] , with empty parenthesis and brackets...
0295: ParsedID pid = SyntaxUtils.getJavaIdentifierBefore(doc, offset,
0296: null);
0297: final IDChain resolver = new IDChain(pid, editedSource, doc,
0298: offset, "(", scl);
0299:
0300: String id = pid.identifierChain;
0301: MainEditorFrame
0302: .debugOut("\n===== Arguments resolver =====:\nid>>> "
0303: + resolver + " <<<");
0304:
0305: String repl = "(";
0306:
0307: if ("@".equals(pid.precedingItem)) {
0308:
0309: repl = "(";
0310: try {
0311: // just replace...
0312: fb.replace(offset, length, repl, attrs);
0313:
0314: Class clazz = resolver.getClassForType(resolver
0315: .getLastChainComponent());
0316: if (clazz != null) {
0317: Rectangle posPt = editor.getTextPane().modelToView(
0318: offset);
0319: Point pts = editor.getTextPane()
0320: .getLocationOnScreen();
0321:
0322: final AnnotationParamsCompletion acd = new AnnotationParamsCompletion(
0323: clazz,
0324: "Annotation arguments completion for "
0325: + clazz.getName(),
0326: MainEditorFrame.instance,
0327: (int) (pts.getX() + posPt.getX()) - 50,
0328: (int) (pts.getY() + posPt.getY() + editor
0329: .getTextPane().getFont().getSize()) + 5,
0330: this , offset);
0331: openedDialogs.add(acd);
0332: }
0333:
0334: } catch (Exception e) {
0335: e.printStackTrace();
0336: }
0337: return repl;
0338: }
0339:
0340: // String className = null;
0341: // FileItem foundType = null;
0342:
0343: /* boolean staticOnly = false;
0344: boolean includePrivate = false;
0345: boolean includeProtected = false;
0346: boolean includePackageScope = false;
0347: boolean constructorMode = false;*/
0348:
0349: if (resolver != null && resolver.isValid()) {
0350: final IDChainElement elt = resolver.getLastChainComponent();
0351:
0352: /*
0353: className = id;
0354: foundType = locateTypeUsingImports(null, className, editedSource.getPackageName(), allowLazyInexactSearch);
0355:
0356: includePrivate = foundType.getJavaName().equals( editedSource.getJavaName());
0357: includeProtected = includePrivate; // TODO:
0358: includePackageScope = foundType.getPackageName().equals( editedSource.getPackageName());
0359: */
0360: Rectangle posPt = editor.getTextPane().modelToView(offset);
0361: Point pts = editor.getTextPane().getLocationOnScreen();
0362:
0363: //TODO: look at class name and edited class name to decide if package scope enabled...
0364:
0365: //System.out.println("acd showing !");
0366: final ArgumentsCompletionDialog acd = new ArgumentsCompletionDialog(
0367: "Arguments completion", MainEditorFrame.instance,
0368: (int) (pts.getX() + posPt.getX()) - 50, (int) (pts
0369: .getY()
0370: + posPt.getY() + editor.getTextPane()
0371: .getFont().getSize()) + 5, this , offset);
0372: openedDialogs.add(acd);
0373:
0374: // not modal !
0375: editor.getTextPane().requestFocus();
0376:
0377: final Thread t = new Thread() {
0378: public void run() {
0379: List<AttrCompletionItem> av = null;
0380:
0381: if (elt.getKind() == ElementKind.Constructor) {
0382: MainEditorFrame
0383: .debugOut("\n=====Constructor completion, "
0384: + elt.getName());
0385: MainEditorFrame.debugOut("type="
0386: + elt.getTypeMaybeResolved());
0387: final String className = elt
0388: .getTypeMaybeResolved();
0389:
0390: if (className != null) {
0391: final String tp = elt.getTypeParameters();
0392: final StringBuilder paramsMapperText = new StringBuilder();
0393: av = ClassSyntaxManager
0394: .getConstructorsArgs(scl,
0395: className, tp, true, true,
0396: true, // show all...
0397: false, // not static only
0398: paramsMapperText);
0399: EventQueue.invokeLater(new Runnable() {
0400: public void run() {
0401: acd.setTitle("Constructor of "
0402: + className
0403: + (tp != null ? " <"
0404: + paramsMapperText
0405: + ">" : ""));
0406: }
0407: });
0408:
0409: }
0410: } else if (elt.getKind() == ElementKind.Method) {
0411: //System.out.println("method completion");
0412: // null ??
0413: //System.out.println("method completion res type "+elt.resolvedType);
0414:
0415: final String className;
0416: String classParameters = null;
0417: if (resolver.chain.size() > 1) {
0418: // the last is the method => the preceeding is the class
0419: IDChainElement idce = resolver.chain
0420: .get(resolver.chain.size() - 2);
0421: className = idce.getTypeMaybeResolved();
0422: classParameters = idce.getTypeParameters();
0423: } else {
0424: // ??? direct call from the method (TODO: look at enclosing class)
0425: className = resolver.source.getJavaName();
0426: }
0427:
0428: if (className != null) {
0429: final String title = "Method "
0430: + elt.getName()
0431: + " in "
0432: + className
0433: + (classParameters != null ? " <"
0434: + classParameters + ">"
0435: : "");
0436: EventQueue.invokeLater(new Runnable() {
0437: public void run() {
0438: acd.setTitle(title);
0439: }
0440: });
0441:
0442: av = ClassSyntaxManager.getMethodsArgs(scl,
0443: className, elt.getName(),
0444: classParameters, true, true, true,
0445: false);
0446: }
0447: } else if (elt.getKind() == ElementKind.Class) {
0448: MainEditorFrame
0449: .debugOut("Unknown type, no parent completion for class element="
0450: + elt);
0451: MainEditorFrame
0452: .debugOut("details: " + resolver);
0453: } else {
0454: MainEditorFrame
0455: .debugOut("Unknown type, no parent completion for type="
0456: + elt.getKind());
0457: MainEditorFrame
0458: .debugOut("details: " + resolver);
0459: }
0460:
0461: if (av != null && av.size() > 0) {
0462: acd.setCompletionItems(av);
0463: } else {
0464: EventQueue.invokeLater(new Runnable() {
0465: public void run() {
0466: MainEditorFrame
0467: .debugOut("cancel arg compl dialog");
0468: acd.cancelDialog();
0469: }
0470: });
0471: }
0472: }
0473: };
0474: t.setName("AttributesCompletion:names");
0475: t.setPriority(Thread.NORM_PRIORITY - 1);
0476: t.start();
0477:
0478: } else {
0479: System.out.println("opening parenthesis was no resolved");
0480: }
0481:
0482: fb.replace(offset, length, "(", attrs);
0483: return "(";
0484: }
0485:
0486: /** space pressed.
0487: * => import completion if import preceeds.
0488: */
0489: public String spacePressed(DocumentFilter.FilterBypass fb,
0490: int offset, int length, AttributeSet attrs)
0491: throws Exception {
0492: // look if "import" preceed the offset
0493: String replacement = "";
0494: boolean importFound = false;
0495: if (offset > 6) {
0496: try {
0497: String txt = editor.getDocument()
0498: .getText(offset - 6, 6);
0499: //System.out.println(""+txt);
0500: if (txt.equals("import"))
0501: importFound = true;
0502: } catch (Exception e) {
0503: e.printStackTrace();
0504: }
0505: }
0506:
0507: if (offset > 13) {
0508: try {
0509: String txt = editor.getDocument().getText(offset - 13,
0510: 13);
0511: //System.out.println(""+txt);
0512: if (txt.equals("import static"))
0513: importFound = true;
0514: } catch (Exception e) {
0515: e.printStackTrace();
0516: }
0517: }
0518:
0519: if (importFound) {
0520: // abort if in comment of in litteral
0521: if (isInStringOrComment(offset)) {
0522: replacement = " ";
0523: fb.replace(offset, length, replacement, attrs);
0524: return replacement;
0525: }
0526:
0527: }
0528:
0529: if (!importFound) {
0530: try {
0531: // simple replace.
0532: replacement = " ";
0533: fb.replace(offset, length, replacement, attrs);
0534: } catch (Exception e) {
0535: e.printStackTrace();
0536: }
0537: } else {
0538: Rectangle posPt; // = new Rectangle(0,0,10,10);
0539: try {
0540: posPt = editor.getTextPane().modelToView(offset);
0541: Point pts = editor.getTextPane().getLocationOnScreen();
0542: ImportCompletionDialog acd = new ImportCompletionDialog(
0543: false, MainEditorFrame.instance, (int) (pts
0544: .getX() + posPt.getX()), (int) (pts
0545: .getY()
0546: + posPt.getY() + editor.getTextPane()
0547: .getFont().getSize()), editor
0548: .getTextPane().getDocument(), offset,
0549: false, null);
0550:
0551: replacement = "";
0552: if (!acd.getWasCancelled()) {
0553: replacement = " " + acd.getSelectionAndClear();
0554: } else {
0555: acd.clear();
0556: replacement = " ";
0557: }
0558:
0559: fb.replace(offset, length, replacement, attrs);
0560:
0561: } catch (Exception e) {
0562: e.printStackTrace();
0563: }
0564: }
0565: return replacement;
0566: }
0567:
0568: /** Also used from the editor.
0569: */
0570: public FileItem locateType(FileItem editedSource,
0571: String javaNameToLocate, boolean _allowLazyInexactSearch) {
0572: return locateTypeUsingImports(editedSource, javaNameToLocate,
0573: _allowLazyInexactSearch);
0574: }
0575:
0576: /** null if no sst available
0577: */
0578: public List<FileItem> locateEventualMissingImport(
0579: FileItem editedSource, String javaNameToLocate) {
0580: if (editedSource.getParserResultIfAlreadyMade() != null) {
0581: if (editedSource.getParserResultIfAlreadyMade().compilationUnit != null) {
0582: return locateMissingImport(editedSource
0583: .getParserResultIfAlreadyMade(),
0584: javaNameToLocate, editedSource.getPackageName());
0585: }
0586: }
0587:
0588: // OLD
0589: @SuppressWarnings("deprecation")
0590: SimplifiedSyntaxTree2 sst = editedSource
0591: .getSimplifiedSyntaxTreeIfAlreadyMade();
0592: if (sst == null)
0593: return null;
0594: ParserTreeNode importsNode = sst.importsNode;
0595: return locateMissingImport(importsNode, javaNameToLocate,
0596: editedSource.getPackageName());
0597: }
0598:
0599: /** Suitable for a type in the short form in the source code (from which the import node comes)
0600: * or an absolute type name.
0601: */
0602: private FileItem locateTypeUsingImports(final FileItem source,
0603: final String javaNameToLocate,
0604: final boolean _allowLazyInexactSearch) {
0605: if (javaNameToLocate.indexOf('$') > 0) {
0606: // TODO: internal class encountered !!
0607: }
0608:
0609: // use first the imports (ordered) to search for the name, first try absolute matching !
0610: FileItem it = TypeLocator.locateUsingImports(source,
0611: javaNameToLocate);
0612: if (it != null)
0613: return it;
0614:
0615: // lazy search
0616: if (_allowLazyInexactSearch) {
0617: it = TypeLocator.searchTypeForName(javaNameToLocate, true,
0618: true);
0619: }
0620:
0621: return it;
0622: }
0623:
0624: /* Suitable for a type in the short form in the source code (from which the import node comes)
0625: * or an absolute type name.
0626: *
0627: private FileItem locateTypeUsingImports(final ParserResult pr, String javaNameToLocate, String sourcePackage, boolean _allowLazyInexactSearch)
0628: {
0629: if(javaNameToLocate.indexOf('$')>0)
0630: {
0631: // TODO: internal class encountered !!
0632: }
0633:
0634: FileItem it = null;
0635: if(pr!=null && pr.compilationUnit!=null)
0636: {
0637: // use first the imports (ordered) to search for the name, first try absolute matching !
0638: it = TypeLocator.locateUsingImports(pr, javaNameToLocate, sourcePackage);
0639: if(it!=null) return it;
0640: }
0641:
0642: // lazy search
0643: if(_allowLazyInexactSearch)
0644: {
0645: it = TypeLocator.searchTypeForName(javaNameToLocate, true, true);
0646: }
0647:
0648: return it;
0649: }*/
0650:
0651: /** null if no importsNode given
0652: */
0653: @Deprecated
0654: private List<FileItem> locateMissingImport(
0655: ParserTreeNode importsNode, String javaNameToLocate,
0656: String sourcePackage) {
0657: if (importsNode != null) {
0658: // use first the imports (ordered) to search for the name, first try absolute matching !
0659: return TypeLocator.locateMissingImport(importsNode,
0660: javaNameToLocate, sourcePackage);
0661: }
0662:
0663: return null;
0664: }
0665:
0666: /** null if no importsNode given
0667: */
0668: private List<FileItem> locateMissingImport(final ParserResult pr,
0669: final String javaNameToLocate, final String sourcePackage) {
0670: if (pr != null && pr.compilationUnit != null) {
0671: // use first the imports (ordered) to search for the name, first try absolute matching !
0672: return TypeLocator.locateMissingImport(pr,
0673: javaNameToLocate, sourcePackage);
0674: }
0675:
0676: return null;
0677: }
0678:
0679: private boolean isInStringOrComment(int offset) {
0680: final SimpleDocument doc = editor.getDocument();
0681: try {
0682: String txtUpto = doc.getText(0, offset);
0683: if (SyntaxUtils.isInString(txtUpto, offset))
0684: return true;
0685:
0686: if (SyntaxUtils.isInComment(txtUpto, offset))
0687: return true;
0688: } catch (Exception e) {
0689: }
0690:
0691: return false;
0692: }
0693:
0694: /** For the completion based on the reflection, the full class name is all what we need.
0695: *
0696: * @return null if not resolved or primitive type.
0697: */
0698: public String resolveClassName(final FileItem editedSource,
0699: final IDChainElement ide, final SingleClassLoader scl) {
0700: if (ide.getArrayDepth() > 0) {
0701: // array completion => ".length" and object
0702: return "java.lang.Object";
0703: } else if (ide.isPrimitiveType) {
0704: // no completions for "int", "double", ...
0705: return null;
0706: }
0707:
0708: // try first with the class (most accurate way)
0709: if (ide.reflectedObject != null) {
0710: if (ide.reflectedObject instanceof Class) {
0711: return ((Class) ide.reflectedObject).getName();
0712: } else {
0713: System.out.println("UNKN::"
0714: + ide.reflectedObject.getClass());
0715: }
0716: }
0717:
0718: FileItem resolvedType = ide.resolvedType_.fitem;
0719: if (resolvedType != null) {
0720: return resolvedType.getJavaName();
0721: }
0722:
0723: // Special case for inner classes: if a $ is present, it is resolved !!
0724: String typeName = ide.getTypeMaybeResolved();
0725:
0726: if (typeName != null && typeName.indexOf('$') > 0) {
0727: return typeName;
0728: }
0729:
0730: MainEditorFrame.debugOut("Try to locate type " + typeName);
0731:
0732: if (typeName != null) {
0733: resolvedType = locateTypeUsingImports(editedSource,
0734: typeName, allowLazyInexactSearch);
0735: if (resolvedType != null) {
0736: return resolvedType.getJavaName();
0737: } else {
0738: //MainEditorFrame.debugOut("CM::no type found for class "+typeName);
0739: if (ide.reflectedObject == null) {
0740: //System.out.println("ide.reflectedObject="+ide.reflectedObject);
0741: // [Feb2008]
0742: return typeName;
0743: }
0744: }
0745: }
0746:
0747: return null;
0748: }
0749:
0750: /** "." completion, complete class names (static), constructors (if new preceeds) and field and methods names.
0751: * One of the most *tuned* methods to give nice results. Don't shows local variables, because adter a "dot" they can't come.
0752: *
0753: * javax.swing.JFrame#
0754: * a#
0755: * a.b.c#
0756: * "abc"#
0757: * new Point2D#
0758: * new Throwable().printStackTrace();
0759: * a[3][5]#
0760: *
0761: * @return the replacement that was selected
0762: */
0763: public String pointPressed(final FileItem editedSource,
0764: final DocumentFilter.FilterBypass fb, final int offset,
0765: final int length, final AttributeSet attrs)
0766: throws Exception {
0767:
0768: final SimpleDocument doc = editor.getDocument();
0769:
0770: // 0) check if dot pressed in comment or litteral (don't complete if so)
0771: //
0772:
0773: if (isInStringOrComment(offset)) {
0774: try {
0775: fb.replace(offset, length, ".", attrs);
0776: return ".";
0777: } catch (Exception e) {
0778: e.printStackTrace();
0779: }
0780: }
0781:
0782: final SingleClassLoader scl = SingleClassLoader
0783: .createSingleClassLoader();
0784: // detect the identifier before offset
0785: // this is trimmed and without spaces. ex: System.out.println
0786: // or a().re[][] , with empty parenthesis and brackets...
0787: final ParsedID pid = SyntaxUtils.getJavaIdentifierBefore(doc,
0788: offset, null);
0789: final IDChain resolver = new IDChain(pid, editedSource, doc,
0790: offset, ".", scl);
0791:
0792: String id = pid.identifierChain;
0793: MainEditorFrame.debugOut("\n=== ID resolver: " + resolver
0794: + "\n");
0795:
0796: String repl = ".";
0797:
0798: // this is what we need, "java.lang.String" is resolved, "String" is not.
0799: // we can reach "more" with a string as with a FileItem when
0800: String resolvedClassName = null;
0801:
0802: boolean staticOnly = false;
0803: boolean includePrivate = false;
0804: boolean includeProtected = false;
0805: boolean includePackageScope = false;
0806: boolean constructorMode = (pid != null && pid.precedingItem
0807: .equals("new"));
0808:
0809: String typeParameters = null;
0810:
0811: // 2) exact parser based type search
0812: boolean isArrayCompletion = false;
0813: final IDChainElement ide;
0814: if (resolver != null && resolver.isValid()
0815: && resolver.getLastChainComponent() != null) {
0816: // we have a valid chain => locate the type !
0817:
0818: ide = resolver.getLastChainComponent();
0819: typeParameters = ide.getTypeParameters(); // for example String in List<String>
0820:
0821: // we can be more precise than above
0822: if (ide.getKind() == ElementKind.Constructor) {
0823: // YES ! seems strange, but it really is so ! as in new "Throwable()." after the dot, we have methods and fields !
0824: constructorMode = false;
0825: } else if (ide.getKind() == ElementKind.Class) {
0826: staticOnly = !ide.isClassCast;
0827: }
0828:
0829: if (ide.getArrayDepth() > 0) {
0830: // array completion => ".length" and object
0831: isArrayCompletion = true;
0832: resolvedClassName = "java.lang.Object";
0833: } else if (ide.isPrimitiveType) {
0834: // no completions for "int", "double", ...
0835: fb.replace(offset, length, ".", attrs);
0836: return ".";
0837: }
0838:
0839: // [Feb2008]: put the code in a method, to share with CtrlSpace action
0840: resolvedClassName = resolveClassName(editedSource, ide, scl);
0841:
0842: FileItem resolvedType = ide.resolvedType_.fitem;
0843:
0844: String fullIDName = resolver.pid.identifierChain;
0845: if (fullIDName.endsWith("this")
0846: || fullIDName.endsWith("super")) {
0847: includePrivate = true;
0848: includeProtected = true;
0849: includePackageScope = true;
0850: }
0851: } else {
0852: ide = null;
0853: }
0854:
0855: String specialCompletionPrepend = null;
0856: int rewind = 0;
0857: if (resolvedClassName != null) {
0858: // may be null ! (for inner classes)
0859: FileItem fileItemForClass = null;
0860: Class resolvedClass = null;
0861: String resolvedPackageName = null;
0862:
0863: if (ide != null
0864: && ide.getKind() == ElementKind.PackageNameFragment) {
0865: fileItemForClass = ide.resolvedType_.fitem;
0866: } else {
0867: if (ide != null && ide.resolvedType_.isResolved()) {
0868: fileItemForClass = ide.resolvedType_.fitem;
0869: } else {
0870: fileItemForClass = TypeLocator
0871: .locateQuick(resolvedClassName);
0872: }
0873:
0874: try {
0875: resolvedClass = scl.loadClass(resolvedClassName);
0876: } catch (Exception e) {
0877: e.printStackTrace();
0878: } catch (Error e) {
0879: e.printStackTrace();
0880: }
0881:
0882: if (fileItemForClass != null) {
0883: resolvedPackageName = fileItemForClass
0884: .getPackageName();
0885: } else if (resolvedClass != null) {
0886: resolvedPackageName = ClassUtils
0887: .getPackageName(resolvedClass);
0888: if (resolvedPackageName == null) {
0889: System.out.println("Null package name for "
0890: + resolvedClass);
0891: }
0892: }
0893: }
0894:
0895: // todo: look in the extends and implements !!!
0896: if (!includePrivate) {
0897: includePrivate = resolvedClassName.equals(editedSource
0898: .getJavaName());
0899: }
0900: if (!includeProtected) {
0901: includeProtected = includePrivate; // TODO:
0902: }
0903: if (!includePackageScope) {
0904: if (resolvedPackageName != null) {
0905: includePackageScope = resolvedPackageName
0906: .equals(editedSource.getPackageName());
0907: } else {
0908: includePackageScope = true;
0909: }
0910: }
0911:
0912: final String ftypeParameters = typeParameters;
0913:
0914: Rectangle posPt = editor.getTextPane().modelToView(offset);
0915: Point pts = editor.getTextPane().getLocationOnScreen();
0916:
0917: //TODO: look at class name and edited class name to decide if package scope enabled...
0918: boolean onlyPublic = false;
0919: if (ide != null) {
0920: if (ide.resolvedType_.isResolved()) {
0921: if (!SyntaxUtils.getPackageName(
0922: ide.resolvedType_.getJavaName()).equals(
0923: SyntaxUtils.getPackageName(editedSource
0924: .getJavaName()))) {
0925: onlyPublic = true;
0926: //System.out.println("Only public because "+ide.resolvedType_.getJavaName()+" != "+editedSource.getJavaName());
0927: //snow.utils.gui.FileChooserFilter$SearchHit != snow.utils.gui.FileChooserFilter
0928:
0929: }
0930: }
0931: }
0932:
0933: final AttributeCompletionDialog acd = new AttributeCompletionDialog(
0934: MainEditorFrame.instance, (int) (pts.getX() + posPt
0935: .getX()),
0936: (int) (pts.getY() + posPt.getY() + editor
0937: .getTextPane().getFont().getSize()), doc,
0938: offset, isArrayCompletion ? "Array completion"
0939: : resolvedClassName
0940: + (ftypeParameters != null ? " <"
0941: + ftypeParameters + ">"
0942: : ""), //TODO add "extends XXX" if so
0943: onlyPublic);
0944:
0945: final FileItem ffoundType = fileItemForClass;
0946: final String fresolvedClassName = resolvedClassName;
0947: final boolean fincludePrivate = includePrivate;
0948: final boolean fincludeProtected = includeProtected;
0949: final boolean fincludePackageScope = includePackageScope;
0950: final boolean fstaticOnly = staticOnly;
0951: final boolean fisArrayCompletion = isArrayCompletion;
0952: final boolean fconstructorMode = constructorMode;
0953:
0954: acd.addFactoryBarForPointCompletion();
0955:
0956: Thread t = new Thread() {
0957: public void run() {
0958: List<AttrCompletionItem> av = null;
0959:
0960: if (fisArrayCompletion) {
0961: // [April2007]
0962: av = ClassSyntaxManager.getAttributes(scl,
0963: "java.lang.Object", "", false, false,
0964: false, false);
0965: av.add(0, AttrCompletionItem.arrayLengthItem());
0966:
0967: } else if (ffoundType != null
0968: && ffoundType.isDirectory()) {
0969: // package completion
0970: final String packageName = ffoundType
0971: .getPackageName();
0972: // Also find other jars starting with that name... "javax" may be present in several jars...
0973: // look in all libs !! [March2007]
0974: List<FileItem> packs = TypeLocator
0975: .locateAllPackages(packageName);
0976:
0977: av = new ArrayList<AttrCompletionItem>();
0978:
0979: for (FileItem pi : packs) {
0980: for (int i = 0; i < pi.getChildCount(); i++) {
0981: FileItem fi = (FileItem) pi
0982: .getChildAt(i);
0983: if (fi.isDirectory()) {
0984: av
0985: .add(AttrCompletionItem
0986: .createNameCompletionForPackage(fi));
0987: } else {
0988: av
0989: .add(AttrCompletionItem
0990: .createNameCompletionForClass(fi));
0991: }
0992: // TODO: show only 2 columns in the completion table.
0993: }
0994: }
0995:
0996: EventQueue.invokeLater(new Runnable() {
0997: public void run() {
0998: acd.setTitle("Package " + packageName
0999: + " completion");
1000: }
1001: });
1002:
1003: } else if (fconstructorMode) {
1004: av = ClassSyntaxManager
1005: .getCompletionForConstructors(scl,
1006: fresolvedClassName,
1007: ftypeParameters,
1008: fincludePrivate,
1009: fincludeProtected,
1010: fincludePackageScope,
1011: fstaticOnly);
1012: } else {
1013: av = ClassSyntaxManager.getAttributes(scl,
1014: fresolvedClassName, ftypeParameters,
1015: fincludePrivate, fincludeProtected,
1016: fincludePackageScope, fstaticOnly);
1017: }
1018:
1019: if (av != null && av.size() > 0) {
1020: acd.setCompletionItems(av);
1021: } else {
1022: EventQueue.invokeLater(new Runnable() {
1023: public void run() {
1024: System.out.println("cancel dot dialog");
1025: acd.cancelDialog();
1026: }
1027: });
1028: }
1029: }
1030: };
1031: t.setName("AttributesCompletion:names");
1032: t.setPriority(Thread.NORM_PRIORITY - 1);
1033: t.start();
1034:
1035: acd.setVisible(true); // MODAL !
1036:
1037: repl = "." + acd.getSelectionAndClear();
1038: specialCompletionPrepend = acd
1039: .getSpecialCompletionPrepend();
1040: rewind = acd.caretRewind;
1041: } else {
1042: MainEditorFrame
1043: .debugOut("no type for " + resolvedClassName);
1044: }
1045:
1046: fb.replace(offset, length, repl, attrs);
1047:
1048: if (specialCompletionPrepend != null) {
1049: // before offset
1050: //TODO: search the position of the preceding space.(before identifier)
1051: //DocumentUtils.
1052:
1053: int posStartID = offset - 1;
1054: if (pid != null) {
1055: posStartID = pid.startPositionInDocument;
1056: }
1057:
1058: //System.out.println("Insert "+specialCompletionPrepend+" at "+posStartID);
1059:
1060: fb.replace(posStartID, 0, specialCompletionPrepend, attrs);
1061:
1062: //SPECIAL CASE!! manage ourselves
1063: editor.getCodeStyler().userInsertOccured(offset,
1064: repl.length() - length);
1065: return null;
1066: } else {
1067: if (rewind != 0) {
1068: editor.getTextPane().setCaretPosition(
1069: editor.getTextPane().getCaretPosition()
1070: - rewind);
1071: }
1072: }
1073:
1074: return repl;
1075: }
1076:
1077: }
|