From d928d880fa677dca7d28ea161128a71f950578ff Mon Sep 17 00:00:00 2001 From: Denis Zhdanov Date: Mon, 28 Jun 2010 17:32:55 +0400 Subject: [PATCH 01/19] IDEA-53596 Soft wrap for editors Corrected 'Home' key typing processing in order to move to the first non-white space symbol on a line if caret is not positioned at the first column --- .../intellij/openapi/editor/LogicalPosition.java | 14 +++++--------- .../openapi/editor/actions/EditorActionUtil.java | 5 ++++- .../openapi/editor/impl/SoftWrapModelImpl.java | 2 +- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/platform/platform-api/src/com/intellij/openapi/editor/LogicalPosition.java b/platform/platform-api/src/com/intellij/openapi/editor/LogicalPosition.java index ff62e540b587..3b9f0b34a704 100644 --- a/platform/platform-api/src/com/intellij/openapi/editor/LogicalPosition.java +++ b/platform/platform-api/src/com/intellij/openapi/editor/LogicalPosition.java @@ -26,6 +26,9 @@ import org.jetbrains.annotations.NonNls; * single logical (line; column) pair matches soft wrap-introduced virtual space, i.e. different visual positions * correspond to the same logical position. It's convenient to store exact visual location details within the logical * position in order to relief further {@code 'logical position' -> 'visual position'} mapping. + *

+ * Note: two objects of this class are considered equal if their logical line and column are equal. I.e. all logical positions + * for soft wrap-introduced virtual space and the first document symbol after soft wrap are considered to be equal. * * @see Editor#offsetToLogicalPosition(int) * @see Editor#logicalPositionToOffset(LogicalPosition) @@ -134,18 +137,11 @@ public class LogicalPosition implements Comparable { if (!(o instanceof LogicalPosition)) return false; final LogicalPosition logicalPosition = (LogicalPosition) o; - return column == logicalPosition.column && line == logicalPosition.line && softWrapLines == logicalPosition.softWrapLines - && linesFromActiveSoftWrap == logicalPosition.linesFromActiveSoftWrap - && softWrapColumnDiff == logicalPosition.softWrapColumnDiff && foldedLines == logicalPosition.foldedLines - && foldingColumnDiff == logicalPosition.foldingColumnDiff; + return column == logicalPosition.column && line == logicalPosition.line; } public int hashCode() { - int result = 29 * line + column; - result = result * 29 + softWrapLines; - result = 29 * result + softWrapColumnDiff; - result = 29 * result + foldedLines; - return 29 * result + foldingColumnDiff; + return 29 * line + column; } @NonNls diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java index 841a909719df..f716608bfa52 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/actions/EditorActionUtil.java @@ -233,10 +233,13 @@ public class EditorActionUtil { } else if (logLineToUse == logCaretLine) { int line = currentVisCaret.line; - int column = 0; + int column; if (currentVisCaret.column == 0) { column = findSmartIndentColumn(editor, currentVisCaret.line); } + else { + column = findFirstNonSpaceColumnOnTheLine(editor, currentVisCaret.line); + } caretModel.moveToVisualPosition(new VisualPosition(line, column)); } else { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java index 55333c4a986b..fecf85c1090b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SoftWrapModelImpl.java @@ -279,7 +279,7 @@ public class SoftWrapModelImpl implements SoftWrapModelEx { @NotNull public LogicalPosition adjustLogicalPosition(@NotNull LogicalPosition defaultLogical, @NotNull VisualPosition visual) { - if (myActive > 0 || !isSoftWrappingEnabled() || myWraps.isEmpty()) { + if (myActive > 0 || !isSoftWrappingEnabled() || myWraps.isEmpty() || myEditor.getDocument().getTextLength() <= 0) { return defaultLogical; } From cc98c8f8b78e79908bc3947a473e76d8ce842e9f Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Mon, 28 Jun 2010 17:48:32 +0400 Subject: [PATCH 02/19] Make it easy to override FileDescriptor in action handler --- .../intellij/util/ui/LocalPathCellEditor.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/util/ui/LocalPathCellEditor.java b/platform/platform-impl/src/com/intellij/util/ui/LocalPathCellEditor.java index f46845347a69..a02972337c50 100644 --- a/platform/platform-impl/src/com/intellij/util/ui/LocalPathCellEditor.java +++ b/platform/platform-impl/src/com/intellij/util/ui/LocalPathCellEditor.java @@ -21,8 +21,6 @@ import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.ui.AbstractTableCellEditor; -import com.intellij.util.ui.CellEditorComponentWithBrowseButton; import javax.swing.*; import java.awt.*; @@ -45,12 +43,7 @@ public class LocalPathCellEditor extends AbstractTableCellEditor { public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected, final int row, int column) { ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent e) { - FileChooserDescriptor d = new FileChooserDescriptor(false, true, false, true, false, false); - if (myTitle != null) { - d.setTitle(myTitle); - } - d.setShowFileSystemRoots(true); - + final FileChooserDescriptor d = getFileChooserDescriptor(); VirtualFile initialFile = LocalFileSystem.getInstance().findFileByPath((String)getCellEditorValue()); VirtualFile[] files = FileChooser.chooseFiles(table, d, initialFile); if (files.length == 1 && files[0] != null) { @@ -66,4 +59,13 @@ public class LocalPathCellEditor extends AbstractTableCellEditor { myComponent.getChildComponent().setText((String)value); return myComponent; } + + public FileChooserDescriptor getFileChooserDescriptor() { + FileChooserDescriptor d = new FileChooserDescriptor(false, true, false, true, false, false); + if (myTitle != null) { + d.setTitle(myTitle); + } + d.setShowFileSystemRoots(true); + return d; + } } From 62dbe6e710a788f92b00ff202af67310c8ed54e9 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Mon, 28 Jun 2010 17:49:49 +0400 Subject: [PATCH 03/19] File Descriptor for file extensions --- .../fileChooser/FileTypeDescriptor.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 platform/platform-api/src/com/intellij/openapi/fileChooser/FileTypeDescriptor.java diff --git a/platform/platform-api/src/com/intellij/openapi/fileChooser/FileTypeDescriptor.java b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileTypeDescriptor.java new file mode 100644 index 000000000000..95941fac6b78 --- /dev/null +++ b/platform/platform-api/src/com/intellij/openapi/fileChooser/FileTypeDescriptor.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.fileChooser; + +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * @author Konstantin Bulenkov + */ +public class FileTypeDescriptor extends FileChooserDescriptor { + private final ArrayList ext; + + public FileTypeDescriptor(String title, @NotNull String... extensions) { + super(true, false, false, true, false, false); + assert extensions.length > 0 : "There should be at least one extension"; + ext = new ArrayList(Arrays.asList(extensions)); + setTitle(title); + } + + @Override + public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) { + final String ex = file.getExtension(); + return file.isDirectory() || (ex != null && ext.contains(ex.toLowerCase())); + } + + @Override + public boolean isFileSelectable(VirtualFile file) { + return !file.isDirectory() && isFileVisible(file, true); + } +} From a786cc3bfb4e992013d68e49cfac8e5492cad612 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Mon, 28 Jun 2010 17:51:41 +0400 Subject: [PATCH 04/19] Dom Model Generator --- resources/src/idea/IdeaActions.xml | 1 + xml/dom-impl/dom-impl.iml | 1 + .../util/dom/generator/DGNamespace.java | 31 + .../util/dom/generator/DTDModelLoader.java | 338 ++++++++ .../util/dom/generator/DomGenDialog.java | 65 ++ .../util/dom/generator/DomGenPanel.form | 107 +++ .../util/dom/generator/DomGenPanel.java | 64 ++ .../intellij/util/dom/generator/Emitter.java | 34 + .../util/dom/generator/FieldDesc.java | 75 ++ .../util/dom/generator/FileManager.java | 30 + .../dom/generator/GenerateDomModelAction.java | 34 + .../util/dom/generator/JetBrainsEmitter.java | 766 ++++++++++++++++++ .../com/intellij/util/dom/generator/Main.java | 60 ++ .../dom/generator/MergingFileManager.java | 245 ++++++ .../util/dom/generator/ModelDesc.java | 170 ++++ .../intellij/util/dom/generator/ModelGen.java | 176 ++++ .../util/dom/generator/ModelLoader.java | 25 + .../util/dom/generator/NamespaceDesc.java | 81 ++ .../intellij/util/dom/generator/TypeDesc.java | 52 ++ .../com/intellij/util/dom/generator/Util.java | 222 +++++ .../util/dom/generator/XSDModelLoader.java | 674 +++++++++++++++ 21 files changed, 3251 insertions(+) create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/DGNamespace.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/DTDModelLoader.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/DomGenDialog.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/DomGenPanel.form create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/DomGenPanel.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/Emitter.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/FieldDesc.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/FileManager.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/GenerateDomModelAction.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/JetBrainsEmitter.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/Main.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/MergingFileManager.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/ModelDesc.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/ModelGen.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/ModelLoader.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/NamespaceDesc.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/TypeDesc.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/Util.java create mode 100644 xml/dom-impl/src/com/intellij/util/dom/generator/XSDModelLoader.java diff --git a/resources/src/idea/IdeaActions.xml b/resources/src/idea/IdeaActions.xml index 3afaf6fb2804..7213ffbd919f 100644 --- a/resources/src/idea/IdeaActions.xml +++ b/resources/src/idea/IdeaActions.xml @@ -269,6 +269,7 @@ + diff --git a/xml/dom-impl/dom-impl.iml b/xml/dom-impl/dom-impl.iml index 0e93e87a43dd..3e227d5f94bb 100644 --- a/xml/dom-impl/dom-impl.iml +++ b/xml/dom-impl/dom-impl.iml @@ -18,6 +18,7 @@ + diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/DGNamespace.java b/xml/dom-impl/src/com/intellij/util/dom/generator/DGNamespace.java new file mode 100644 index 000000000000..eff7dc97c59a --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/DGNamespace.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.dom.generator; + +/** + * @author Konstantin Bulenkov + */ +public class DGNamespace { + public final String name; + public boolean skip = false; + public String superClass = null; + public String enums = null; + + + public DGNamespace(String name) { + this.name = name; + } +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/DTDModelLoader.java b/xml/dom-impl/src/com/intellij/util/dom/generator/DTDModelLoader.java new file mode 100644 index 000000000000..2924d5b0661f --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/DTDModelLoader.java @@ -0,0 +1,338 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * XSD/DTD Model generator tool + * + * By Gregory Shrago + * 2002 - 2006 + */ +package com.intellij.util.dom.generator; + +import com.wutka.dtd.*; +import org.apache.xerces.xni.parser.XMLEntityResolver; + +import java.io.File; +import java.util.*; + +/** + * @author Gregory.Shrago + */ +public class DTDModelLoader implements ModelLoader { + private ModelDesc model; + + public void loadModel(ModelDesc model, Collection schemas, XMLEntityResolver resolver) throws Exception { + this.model = model; + for (File dtdFile : schemas) { + String fileName = dtdFile.getPath(); + if (dtdFile.isDirectory() || !fileName.endsWith(".dtd") || fileName.endsWith("datatypes.dtd")) { + Util.log("skipping " + fileName); + continue; + } + Util.log("loading " + fileName+".."); + String ns = fileName.substring(fileName.lastIndexOf(File.separatorChar) + 1); + //loadDTDByXerces(ns, dtdFile, resolver); + + loadDTDByWutka(ns, dtdFile); + + } + } + + private NamespaceDesc ensureNamespaceExists(String ns) { + if (!model.nsdMap.containsKey(ns)) { + Util.log("Adding default ns desc for: " + ns); + NamespaceDesc nsd = new NamespaceDesc(ns, model.nsdMap.get("")); + model.nsdMap.put(ns, nsd); + } + return model.nsdMap.get(ns); + } + + + private void loadDTDByWutka(String ns, File dtdFile) throws Exception { + DTDParser parser = new DTDParser(dtdFile, false); + // Parse the DTD and ask the parser to guess the root element + DTD dtd = parser.parse(true); + checkDTDRootElement(dtd); + processDTD(ns, dtd, model.jtMap, model.nsdMap); + } + + + private void processDTD(String namespace, DTD dtd, Map jtMap, Map nsdMap) { + final NamespaceDesc nsd = ensureNamespaceExists(namespace); + if (nsd.skip) return; + final ArrayList resultQNames = new ArrayList(); + final DTDElement[] elements = new DTDElement[dtd.elements.size()]; + int ptr = 1; + + final HashSet visitedElements = new HashSet(); + elements[0] = dtd.rootElement; + + while (--ptr > -1) { + final DTDElement el = elements[ptr]; + visitedElements.add(el); + final String typeName = model.toJavaTypeName(el.name, namespace); + final String typeQName = model.toJavaQualifiedTypeName(namespace, typeName, false); + if (resultQNames.contains(typeQName)) { + continue; + } else { + resultQNames.add(typeQName); + } + final TypeDesc td = new TypeDesc(el.name, namespace, typeName, TypeDesc.TypeEnum.CLASS); + boolean duplicates = false; + if ((el.content instanceof DTDAny) || (el.content instanceof DTDMixed)) { + FieldDesc fd = new FieldDesc(FieldDesc.SIMPLE, "value", "String", null, "null", false); + fd.realIndex = td.fdMap.size(); + td.fdMap.put(fd.name, fd); + } + for (Object o : el.attributes.keySet()) { + String attrName = (String) o; + DTDAttribute attr = (DTDAttribute) el.attributes.get(attrName); + if (attr.decl == DTDDecl.FIXED || "ID".equals(attr.type)) { + continue; + } + boolean required = attr.decl == DTDDecl.REQUIRED; + FieldDesc fd1 = new FieldDesc(FieldDesc.ATTR, Util.toJavaFieldName(attrName), "String", null, "\"\"", required); + fd1.tagName = attrName; + fd1.documentation = "Attribute " + attrName + ""; + fd1.realIndex = td.fdMap.size(); + duplicates = Util.addToNameMap(td.fdMap, fd1, false) || duplicates; + } + final ArrayList> choiceList = new ArrayList>(); + final LinkedList plist = new LinkedList(); + if (el.content instanceof DTDContainer) { + //if ((el.content instanceof DTDChoice) || (el.content instanceof DTDSequence)) { + plist.add(new Entry(el.content, false, true)); + } + while (!plist.isEmpty()) { + final Entry pentry = plist.removeFirst(); + + final DTDItem p = pentry.p; + + if (p instanceof DTDName) { + final DTDName n = (DTDName) p; + final DTDElement nel = (DTDElement) dtd.elements.get(n.value); + final String pName = n.value; + final FieldDesc fd1 = new FieldDesc(FieldDesc.STR, Util.toJavaFieldName(pName), pName, null, "null", pentry.required && (n.cardinal == DTDCardinal.ONEMANY || n.cardinal == DTDCardinal.NONE)); + fd1.tagName = pName; + if (nel != null) { + fd1.documentation = parseDTDItemDocumentation(dtd, nel, "Type " + nel.name + " documentation"); + } + if (nel == null) { + fd1.type = model.toJavaTypeName(fd1.tagName, namespace); + } else if (nel.content instanceof DTDEmpty || nel.content instanceof DTDAny) { + boolean hasAttrFields = false; + boolean hasTextContents = nel.content instanceof DTDAny; + for (Object o : nel.attributes.values()) { + DTDAttribute attr = (DTDAttribute) o; + if (attr.decl != DTDDecl.FIXED && !"ID".equals(attr.type)) { + hasAttrFields = true; + break; + } + } + if (hasAttrFields || hasTextContents) { + fd1.clType = FieldDesc.OBJ; + fd1.type = model.toJavaTypeName(fd1.tagName, namespace); + fd1.contentQualifiedName = model.toJavaQualifiedTypeName(namespace, fd1.name, false); + fd1.def = "null"; + // next type + if (!visitedElements.contains(nel)) { + elements[ptr++] = nel; + } + } else if (hasTextContents) { + fd1.clType = FieldDesc.STR; + fd1.type = "String"; + fd1.def = "null"; + } else { + fd1.clType = FieldDesc.BOOL; + fd1.type = "boolean"; + fd1.def = "false"; + } + } else if (nel.content instanceof DTDContainer) { + boolean hasAttrFields = false; + boolean hasTextField = false; + if ((nel.content instanceof DTDMixed) && ((DTDMixed) nel.content).getItemsVec().size() == 1) { + hasTextField = true; + for (Object o : nel.attributes.values()) { + final DTDAttribute attr = (DTDAttribute) o; + if (attr.decl != DTDDecl.FIXED && !"ID".equals(attr.type)) { + hasAttrFields = true; + break; + } + } + } + if (hasTextField && !hasAttrFields) { + fd1.clType = FieldDesc.STR; + fd1.type = "String"; + fd1.def = "null"; + } else { + fd1.clType = FieldDesc.OBJ; + fd1.type = model.toJavaTypeName(fd1.tagName, namespace); + fd1.contentQualifiedName = model.toJavaQualifiedTypeName(namespace, fd1.tagName, false); + // next type + if (!visitedElements.contains(nel)) { + elements[ptr++] = nel; + } + } + } else { + fd1.type = "ERROR:Name"; + } + if ((pentry.many || n.cardinal.type >= 2) && fd1.clType != FieldDesc.BOOL) { + fd1.elementType = fd1.type; + fd1.elementName = fd1.name; + fd1.type = "List<" + fd1.elementType + ">"; + fd1.name = Util.pluralize(fd1.name); + fd1.def = "new ArrayList(0)"; + fd1.clType = -fd1.clType; + fd1.comment = "array of " + fd1.elementType; + } + fd1.realIndex = td.fdMap.size(); + duplicates = Util.addToNameMap(td.fdMap, fd1, false) || duplicates; + } else if (p instanceof DTDContainer) { + final DTDContainer cont = (DTDContainer) p; + final boolean isChoice = cont instanceof DTDChoice; + // 0 - NONE, 1 - OPT, 2 - ZEROMANY, 3 - ONEMANY + final boolean required = !isChoice && pentry.required && p.cardinal != DTDCardinal.ZEROMANY && p.cardinal != DTDCardinal.OPTIONAL; + final boolean many = p.cardinal == DTDCardinal.ONEMANY || p.cardinal == DTDCardinal.ZEROMANY; + List l = cont.getItemsVec(); + if (!many && isChoice) { + choiceList.add(l); + } + for (DTDItem aL : l) { + plist.add(new Entry(aL, many, required)); + } + } else { + Util.logerr("unknown item " + p); + } + } + td.duplicates = duplicates; + td.documentation = parseDTDItemDocumentation(dtd, el, "Type " + el.name + " documentation"); + jtMap.put(model.toJavaQualifiedTypeName(namespace, td.name, false), td); + int i = 0; + for (FieldDesc fd : td.fdMap.values()) { + fd.idx = i++; + } + for (List l : choiceList) { + ArrayList clist = new ArrayList(); + LinkedList elist = new LinkedList(); + for (i = 0; i < l.size(); i++) { + elist.add(l.get(i)); + } + while (!elist.isEmpty()) { + DTDItem p = elist.removeFirst(); + if (p instanceof DTDContainer) { + List l2 = ((DTDContainer) p).getItemsVec(); + for (DTDItem aL2 : l2) { + elist.addFirst(aL2); + } + } else if (p instanceof DTDName) { + clist.add(p); + } + } + boolean choiceOpt = true; + FieldDesc[] choice = new FieldDesc[clist.size()]; + for (i = 0; i < choice.length; i++) { + DTDName p = (DTDName) clist.get(i); + String s = Util.toJavaFieldName(p.value); + FieldDesc fd = td.fdMap.get(s); + if (fd == null) { + fd = td.fdMap.get(Util.pluralize(s)); + if (fd == null) { + Util.logerr("uknown choice element: " + s); + continue; + } + } + choice[i] = fd; + choice[i].choice = choice; + if (fd.required) choiceOpt = false; + } + for (i = 0; i < choice.length; i++) { + choice[i].choiceOpt = choiceOpt; + } + } + } + List entList = dtd.getItemsByType(DTDEntity.class); + for (DTDEntity entity : entList) { + String value = entity.value; + if (!value.startsWith("(") || !value.endsWith(")")) { + continue; + } + String typeName = model.toJavaTypeName(entity.name, namespace); + TypeDesc td = new TypeDesc(entity.name, namespace, typeName, TypeDesc.TypeEnum.ENUM); + StringTokenizer st = new StringTokenizer(value, "(|)"); + while (st.hasMoreTokens()) { + final String s = st.nextToken(); + td.fdMap.put(s, new FieldDesc(Util.computeEnumConstantName(s, td.name), s)); + } + td.documentation = parseDTDItemDocumentation(dtd, entity, "Type " + entity.name + " documentation"); + jtMap.put(model.toJavaQualifiedTypeName(namespace, td.name, true), td); + } + } + + private static String parseDTDItemDocumentation(DTD dtd, DTDOutput obj, String title) { + int elementIndex = dtd.items.indexOf(obj); + if (elementIndex < 1) return null; + Object prev = dtd.items.get(elementIndex - 1); + if (!(prev instanceof DTDComment)) return null; + DTDComment comment = (DTDComment) prev; + return title + "\n" + "

\n" + comment.getText() + "\n
"; + } + + static class Entry { + boolean required; + boolean many; + DTDItem p; + + Entry(DTDItem p, boolean many, boolean required) { + this.required = required; + this.many = many; + this.p = p; + } + + Entry parent; + DTDItem it; + Vector choice; + int num; + + Entry(Entry parent, DTDItem it, Vector choice, int num) { + this.parent = parent; + this.it = it; + this.choice = choice; + this.num = num; + } + } + + + private static void checkDTDRootElement(DTD dtd) throws Exception { + if (dtd.rootElement == null) { + StringBuffer sb = new StringBuffer("Empty root: possible elements: "); + HashMap map = new HashMap(dtd.elements); + for (Object o : dtd.elements.values()) { + DTDElement el = (DTDElement) o; + if (el.content instanceof DTDContainer) { + for (Object obj : ((DTDContainer) el.content).getItemsVec()) { + if (obj instanceof DTDName) { + map.remove(((DTDName) obj).value); + } + } + } + } + if (dtd.rootElement != null) return; + sb.append(map.size()).append(map.keySet()); + throw new Exception(sb.toString()); + } + } + +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/DomGenDialog.java b/xml/dom-impl/src/com/intellij/util/dom/generator/DomGenDialog.java new file mode 100644 index 000000000000..13171c357b4f --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/DomGenDialog.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.dom.generator; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; + +import javax.swing.*; +import java.io.File; + +/** + * @author Konstantin Bulenkov + */ +public class DomGenDialog extends DialogWrapper{ + final DomGenPanel panel; + final JComponent comp; + + protected DomGenDialog(Project project) { + super(project); + panel = new DomGenPanel(project); + comp = panel.getComponent(); + init(); + getOKAction().putValue(Action.NAME, "Generate"); + } + + @Override + protected JComponent createCenterPanel() { + return comp; + } + + + @Override + protected void doOKAction() { + final String location = panel.getLocation(); + ModelLoader loader = location.toLowerCase().endsWith(".xsd") ? new XSDModelLoader() : new DTDModelLoader(); + final ModelGen modelGen = new ModelGen(loader); + final NamespaceDesc desc = panel.getNamespaceDescriptor(); + modelGen.setConfig(desc.name, location, desc); + try { + final File output = new File(panel.getOutputDir()); + modelGen.perform(output, new File(location).getParentFile()); + } catch (Exception e) { + e.printStackTrace(System.err); + } + super.doOKAction(); + } + + @Override + protected String getDimensionServiceKey() { + return getClass().getName(); + } +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/DomGenPanel.form b/xml/dom-impl/src/com/intellij/util/dom/generator/DomGenPanel.form new file mode 100644 index 000000000000..41286f50090c --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/DomGenPanel.form @@ -0,0 +1,107 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/DomGenPanel.java b/xml/dom-impl/src/com/intellij/util/dom/generator/DomGenPanel.java new file mode 100644 index 000000000000..708fda848f37 --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/DomGenPanel.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.dom.generator; + +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; +import com.intellij.openapi.fileChooser.FileTypeDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; + +import javax.swing.*; + +/** + * @author Konstantin Bulenkov + */ +public class DomGenPanel { + private JPanel mainPanel; + private JTextField myNamespace; + private JTextField mySuperClass; + private TextFieldWithBrowseButton mySchemaLocation; + private JTextField myPackage; + private TextFieldWithBrowseButton myOutputDir; + private final Project myProject; + + public DomGenPanel(Project project) { + myProject = project; + } + + private void createUIComponents() { + mySchemaLocation = new TextFieldWithBrowseButton(); + final String title = "Choose XSD or DTD schema"; + mySchemaLocation.addBrowseFolderListener(title, "", myProject, new FileTypeDescriptor(title, "xsd", "dtd")); + myOutputDir = new TextFieldWithBrowseButton(); + myOutputDir.addBrowseFolderListener("Select Output For Generated Files", "", myProject, FileChooserDescriptorFactory.getDirectoryChooserDescriptor("Output For Generated Files")); + } + + public JComponent getComponent() { + return mainPanel; + } + + public NamespaceDesc getNamespaceDescriptor() { + return new NamespaceDesc(myNamespace.getText().trim(), myPackage.getText().trim(), mySuperClass.getText().trim(), "", null, null, null, null); + } + + public String getLocation() { + return mySchemaLocation.getText(); + } + + public String getOutputDir() { + return myOutputDir.getText(); + } +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/Emitter.java b/xml/dom-impl/src/com/intellij/util/dom/generator/Emitter.java new file mode 100644 index 000000000000..628eb6be6f8b --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/Emitter.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * XSD/DTD Model generator tool + * + * By Gregory Shrago + * 2002 - 2006 + */ +package com.intellij.util.dom.generator; + +import java.io.File; + +public interface Emitter { + public static final String JDOC_OPEN = "/**"; + public static final String JDOC_CONT = " * "; + public static final String JDOC_CLOSE = " */"; + + + void emit(FileManager fileManager, ModelDesc model, File outputRoot); +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/FieldDesc.java b/xml/dom-impl/src/com/intellij/util/dom/generator/FieldDesc.java new file mode 100644 index 000000000000..3bafea61124a --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/FieldDesc.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * XSD/DTD Model generator tool + * + * By Gregory Shrago + * 2002 - 2006 + */ +package com.intellij.util.dom.generator; + +public class FieldDesc implements Comparable { + final static int STR = 1; + final static int BOOL = 2; + final static int OBJ = 3; + final static int ATTR = 4; + final static int DOUBLE = 5; + final static int SIMPLE = 6; + + public FieldDesc(String name, String def) { + this.name = name; + this.def = def; + } + + public FieldDesc(int clType, String name, String type, String elementType, String def, boolean required) { + this.clType = clType; + this.name = name; + this.type = type; + this.elementType = elementType; + this.def = def; + this.required = required; + } + + int clType = STR; + String name; + String type; + String elementType; + String def; + boolean required; + + int idx; + String tagName; + String elementName; + String comment; + FieldDesc[] choice; + boolean choiceOpt; + + String documentation; + String simpleTypesString; + int duplicateIndex = -1; + int realIndex; + String contentQualifiedName; + + public int compareTo(FieldDesc o) { + return name.compareTo(o.name); + } + + public String toString() { + return "Field: " + name + ";" + type + ";" + elementName + ";" + elementType; + } + +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/FileManager.java b/xml/dom-impl/src/com/intellij/util/dom/generator/FileManager.java new file mode 100644 index 000000000000..18cc44b814b8 --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/FileManager.java @@ -0,0 +1,30 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * XSD/DTD Model generator tool + * + * By Gregory Shrago + * 2002 - 2006 + */ +package com.intellij.util.dom.generator; + +import java.io.File; + +public interface FileManager { + public File releaseOutputFile(File outFile); + public File getOutputFile(File target); +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/GenerateDomModelAction.java b/xml/dom-impl/src/com/intellij/util/dom/generator/GenerateDomModelAction.java new file mode 100644 index 000000000000..ffd90a14cd9c --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/GenerateDomModelAction.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.dom.generator; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.project.Project; + +/** + * @author Konstantin Bulenkov + */ +public class GenerateDomModelAction extends AnAction { + @Override + public void actionPerformed(AnActionEvent e) { + final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext()); + if (project != null) { + new DomGenDialog(project).show(); + } + } +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/JetBrainsEmitter.java b/xml/dom-impl/src/com/intellij/util/dom/generator/JetBrainsEmitter.java new file mode 100644 index 000000000000..fff471adca9c --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/JetBrainsEmitter.java @@ -0,0 +1,766 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * XSD/DTD Model generator tool + * + * By Gregory Shrago + * 2002 - 2006 + */ +package com.intellij.util.dom.generator; + +import java.util.*; +import java.io.File; +import java.io.PrintWriter; +import java.io.FileWriter; +import java.io.IOException; + +/** + * @author Gregory.Shrago + */ +public class JetBrainsEmitter implements Emitter { + static final boolean NOT_COMPARE_MODE = true; // compare mode: skip package decl & all comments/javadoc + static final boolean JB_OFF = false; + static final boolean REPLACE_TYPES_WITH_INTERFACES = true; + + + public void emit(FileManager fileManager, ModelDesc model, File outputRoot) { + final NamespaceDesc nsdDef = model.nsdMap.get(""); + final Set simpleTypes = new TreeSet(); + for (TypeDesc td : model.jtMap.values()) { + generateClass(fileManager, td, model, outputRoot, simpleTypes); + } +// for (Iterator it = nsdMap.values().iterator(); it.hasNext(); ) { +// NamespaceDesc nsd = (NamespaceDesc) it.next(); +// generateSuper(nsd, outputRoot); +// generateHelper(nsd, jtMap, outputRoot); +// } + generateSuper(fileManager, nsdDef, model, outputRoot); + generateHelper(fileManager, nsdDef, model, outputRoot); + + Util.log("SimpleTypes log:"); + for (String s : simpleTypes) { + Util.log(" " + s); + } + } + + public void generateClass(FileManager fileManager, TypeDesc td, ModelDesc model, File outDir, Set simpleTypes) { + final Map jtMap = model.jtMap; + final Map nsdMap = model.nsdMap; + final NamespaceDesc nsd = nsdMap.get(nsdMap.containsKey(td.xsNamespace) ? td.xsNamespace : ""); + final String typeName = td.name; + final String typeQName = model.getNSDPrefix(td) + typeName; + final String pkgName = typeQName.lastIndexOf('.') > -1 ? typeQName.substring(0, typeQName.lastIndexOf('.')) : ""; + + final File outFile = fileManager.getOutputFile(new File(outDir, toJavaFileName(typeQName))); + PrintWriter out = null; + try { + TreeSet externalClasses = new TreeSet(); + if (td.type != TypeDesc.TypeEnum.ENUM) { + if (nsd.imports != null) { + StringTokenizer st = new StringTokenizer(nsd.imports, ";"); + while (st.hasMoreTokens()) { + externalClasses.add(st.nextToken()); + } + } + if (!model.getNSDPrefix("", nsd.superClass, false).equals(model.getNSDPrefix(td))) { + externalClasses.add(model.getNSDPrefix("", nsd.superClass, false) + nsd.superClass.substring(nsd.superClass.lastIndexOf(".") + 1)); + } + if (td.supers != null) { + for (TypeDesc tds : td.supers) { + String pkg1 = model.getNSDPrefix(tds); + String pkg2 = model.getNSDPrefix(td); + if (!pkg1.equals(pkg2)) { + externalClasses.add(model.getNSDPrefix(tds) + tds.name); + } + } + } + for (FieldDesc fd : td.fdMap.values()) { + if (fd.simpleTypesString != null && fd.simpleTypesString.indexOf(":fully-qualified-classType;") != -1) { + externalClasses.add("com.intellij.psi.PsiClass"); + } + if (fd.contentQualifiedName != null && fd.contentQualifiedName.indexOf('.') > 0) { + String pkgNameFD = fd.contentQualifiedName.substring(0, fd.contentQualifiedName.lastIndexOf('.')); + if (!pkgNameFD.equals(pkgName)) { + externalClasses.add(fd.contentQualifiedName); + } + } + if (fd.clType < 0) { + externalClasses.add("java.util.List"); + } + externalClasses.add("org.jetbrains.annotations.NotNull"); + if (fd.required) { + externalClasses.add("com.intellij.util.xml.Required"); + } + if (fd.duplicateIndex >= 0 || fd.clType == FieldDesc.BOOL) { + externalClasses.add("com.intellij.util.xml.SubTag"); + } + if (fd.clType == FieldDesc.ATTR) { + externalClasses.add("com.intellij.util.xml.GenericAttributeValue"); + } else if (Math.abs(fd.clType) != FieldDesc.SIMPLE) { + if (Math.abs(fd.clType) == FieldDesc.OBJ) { + final TypeDesc ftd = jtMap.get(fd.contentQualifiedName); + if (ftd != null && ftd.type == TypeDesc.TypeEnum.ENUM) { + externalClasses.add("com.intellij.util.xml.GenericDomValue"); + } + } else { + externalClasses.add("com.intellij.util.xml.GenericDomValue"); + } + } + } + } + + Util.log("Generating type: " + typeName + ".."); + out = new PrintWriter(new FileWriter(outFile)); + if (NOT_COMPARE_MODE) { + out.println("// Generated on " + new Date()); + out.println("// DTD/Schema : " + nsd.name); + } + out.println(""); + if (NOT_COMPARE_MODE && pkgName != null && pkgName.length() > 0) { + out.println("package " + pkgName + ";"); + } + out.println(); + if (td.type != TypeDesc.TypeEnum.ENUM) { + boolean javaLang = false; + boolean external = false; + for (String s : externalClasses) { + if (s.startsWith("java.")) { + javaLang = true; + continue; + } + external = true; + out.println("import " + s + ";"); + } + if (javaLang) { + if (external) out.println(); + for (String s : externalClasses) { + if (!s.startsWith("java.")) continue; + out.println("import " + s + ";"); + } + } + out.println(); + } + if (td.type == TypeDesc.TypeEnum.ENUM) { + boolean text = false; + for (Map.Entry e : td.fdMap.entrySet()) { + if (!e.getKey().equals(e.getValue().name)) { + text = true; + break; + } + } + if (NOT_COMPARE_MODE) { + out.println(JDOC_OPEN); + out.println(JDOC_CONT + td.xsNamespace + ":" + td.xsName + " enumeration."); + printDocumentation(out, td.documentation, JDOC_CONT); + out.println(JDOC_CLOSE); + } + out.print("public enum " + typeName + (text ? (JB_OFF ? "" : " implements com.intellij.util.xml.NamedEnum") : "")); + out.print(" {"); + boolean first = true; + for (Map.Entry e : td.fdMap.entrySet()) { + String val = e.getKey(); + FieldDesc id = e.getValue(); + if (first) { + first = false; + out.println(""); + } else { + out.println(","); + } + if (text) { + out.print("\t" + id.name + " (\"" + val + "\")"); + } else { + out.print("\t" + id.name); + } + } + if (text) { + out.println(";"); + out.println(); + out.println("\tprivate final String value;"); + out.println("\tprivate " + typeName + "(String value) { this.value = value; }"); + out.println("\tpublic String getValue() { return value; }"); + } + out.println(); + out.println("}"); + return; + } + if (NOT_COMPARE_MODE) { + out.println(JDOC_OPEN); + if (td.type == TypeDesc.TypeEnum.GROUP_INTERFACE) { + out.println(JDOC_CONT + td.xsNamespace + ":" + td.xsName + " model group interface."); + } else { + out.println(JDOC_CONT + td.xsNamespace + ":" + td.xsName + " interface."); + } + printDocumentation(out, td.documentation, JDOC_CONT); + out.println(JDOC_CLOSE); + } + out.print("public interface " + typeName); + if (nsd.superClass != null || (td.supers != null && td.supers.length > 1)) { + boolean comma = false; + if (td.type != TypeDesc.TypeEnum.GROUP_INTERFACE) { + if (nsd.superClass != null) { + out.print(" extends " + nsd.superClass.substring(nsd.superClass.lastIndexOf(".") + 1)); + comma = true; + } + } + if (td.supers != null && td.supers.length > 0) { + if (!comma) out.print(" extends "); + for (TypeDesc aSuper : td.supers) { + if (comma) out.print(", "); + else comma = true; + out.print(aSuper.name); + } + } + } + out.println(" {"); + + FieldDesc[] fields = td.fdMap.values().toArray(new FieldDesc[td.fdMap.size()]); + if (fields.length == 0) { + Util.logwarn("no fields in: " + td.xsName); + } + Arrays.sort(fields, new Comparator() { + public int compare(FieldDesc o1, FieldDesc o2) { + return o1.realIndex - o2.realIndex; + } + }); + /* +// fields +out.println(""); +out.println("\t// fields"); +for (int i = 0; i < fields.length; i++) { + out.print("\t// "); + out.print(fields[i].tagName); + out.print(", required:" + fields[i].required); + out.print(", cltype:" + fields[i].clType); + out.println(""); + String[] doc = parseAnnotationString(fields[i].documentation); + for (int dc=0; dc 0 ? name : field.elementName); + String javaDocTagName = field.clType < 0 ? tagName + " children" : tagName != null ? tagName + " child" : "simple content"; + boolean isAttr = field.clType == FieldDesc.ATTR; + boolean isList = field.clType < 0; + + if (name.equals("class")) { // getClass method prohibited + name = "clazz"; + } + boolean nameChanged = field.tagName != null && !name.equals(isList ? Util.pluralize(Util.toJavaFieldName(field.tagName)) : Util.toJavaFieldName(field.tagName)); + + // annotations + // types replacement + String newType = field.clType < 0 ? elementType : type; + String converterString = null; + if (field.simpleTypesString != null) { + if (field.simpleTypesString.indexOf(":fully-qualified-classType;") != -1) { // localType, remoteType, etc. + newType = "PsiClass"; + //converterString = (JB_OFF ? "//" : "")+"\t@Convert (PsiClassReferenceConverter.class)"; + } else if (field.simpleTypesString.indexOf(":ejb-linkType;") != -1) { + } else if (field.simpleTypesString.indexOf(":ejb-ref-nameType;") != -1) { // jndi-nameType + } else if (field.simpleTypesString.indexOf(":pathType;") != -1) { + } else if (field.simpleTypesString.indexOf(":java-identifierType;") != -1) { + //out.println((JB_OFF ? "//" : "") +"\t@Convert (JavaIdentifierConverter.class)"); + } else if (field.simpleTypesString.indexOf(":QName;") != -1) { + // ??? + } else if (field.simpleTypesString.indexOf(":integer;") != -1) { // BigDecimal + newType = REPLACE_TYPES_WITH_INTERFACES ? "Integer" : "int"; + } else if (field.simpleTypesString.indexOf(":int;") != -1) { + newType = REPLACE_TYPES_WITH_INTERFACES ? "Integer" : "int"; + } else if (field.simpleTypesString.indexOf(":byte;") != -1) { + newType = REPLACE_TYPES_WITH_INTERFACES ? "Byte" : "byte"; + } else if (field.simpleTypesString.indexOf(":short;") != -1) { + newType = REPLACE_TYPES_WITH_INTERFACES ? "Short" : "short"; + } else if (field.simpleTypesString.indexOf(":long;") != -1) { + newType = REPLACE_TYPES_WITH_INTERFACES ? "Long" : "long"; + } else if (field.simpleTypesString.indexOf(":float;") != -1) { + newType = REPLACE_TYPES_WITH_INTERFACES ? "Float" : "float"; + } else if (field.simpleTypesString.indexOf(":double;") != -1) { + newType = REPLACE_TYPES_WITH_INTERFACES ? "Double" : "double"; + } else if (field.simpleTypesString.indexOf(":boolean;") != -1) { // true-falseType + newType = REPLACE_TYPES_WITH_INTERFACES ? "Boolean" : "boolean"; + } + for (int idx = 0; idx != -1;) { + simpleTypes.add(field.simpleTypesString.substring(idx)); + idx = field.simpleTypesString.indexOf(';', idx) + 1; + if (idx == 0) break; + } + } + if (REPLACE_TYPES_WITH_INTERFACES) { + switch (Math.abs(field.clType)) { + case FieldDesc.ATTR: + newType = "GenericAttributeValue<" + newType + ">"; + break; + case FieldDesc.BOOL: + newType = "GenericDomValue"; + break; + case FieldDesc.SIMPLE: + break; + case FieldDesc.STR: + newType = "GenericDomValue<" + newType + ">"; + break; + case FieldDesc.OBJ: { + TypeDesc ftd = jtMap.get(field.contentQualifiedName); + if (ftd != null && ftd.type == TypeDesc.TypeEnum.ENUM) { + newType = "GenericDomValue<" + ftd.name + ">"; + } + break; + } + } + } + if (newType != null && isList) { + elementType = newType; + } else if (newType != null) { + type = newType; + } + if (isList) { + type = "List<" + elementType + ">"; + } + + StringBuffer sbAnnotations = new StringBuffer(); + if (field.clType == FieldDesc.SIMPLE) { + // sbAnnotations.append((JB_OFF ? "//" : "") +"\t@TagValue"); + } else if (isAttr && nameChanged) { + sbAnnotations.append((JB_OFF ? "//" : "") + "\t@com.intellij.util.xml.Attribute (\"").append(tagName).append("\")"); + } else if (isList) { + // sbAnnotations.append((JB_OFF ? "//" : "") +"\t@SubTagList (\"" + tagName + "\")"); + if (nameChanged) { + sbAnnotations.append((JB_OFF ? "//" : "") + "\t@com.intellij.util.xml.SubTag (\"").append(tagName).append("\")"); + } + } else { + if (field.duplicateIndex >= 0) { + sbAnnotations.append((JB_OFF ? "//" : "") + "\t@SubTag (value = \"").append(tagName).append("\", index = ").append(field.duplicateIndex - 1).append(")"); + } else if (field.clType == FieldDesc.BOOL) { + sbAnnotations.append((JB_OFF ? "//" : "") + "\t@SubTag (value = \"").append(tagName).append("\", indicator = true)"); + } else if (!name.equals(field.name)) { + sbAnnotations.append((JB_OFF ? "//" : "") + "\t@com.intellij.util.xml.SubTag (\"").append(tagName).append("\")"); + } + } + if (converterString != null) { + sbAnnotations.append("\n").append(converterString); + } + if (NOT_COMPARE_MODE && td.type != TypeDesc.TypeEnum.GROUP_INTERFACE) { + out.println("\t" + JDOC_OPEN); + final String text; + if (isList) { + text = "the list of " + javaDocTagName; + } else { + text = "the value of the " + javaDocTagName; + } + out.println("\t" + JDOC_CONT + "Returns " + text + "."); + printDocumentation(out, field.documentation, "\t" + JDOC_CONT); + out.println("\t" + JDOC_CONT + "@return " + text + "."); + out.println("\t" + JDOC_CLOSE); + } + out.println((JB_OFF ? "//" : "") + "\t@NotNull"); + if (td.type != TypeDesc.TypeEnum.GROUP_INTERFACE) { + if (sbAnnotations.length() > 0) out.println(sbAnnotations); + if (field.required) { + out.println((JB_OFF ? "//" : "") + "\t@Required"); + } + } + out.print("\t"); + //out.print("public "); + out.print(type); + out.print(" "); + out.print("get"); + out.print(Util.capitalize(name)); + out.println("();"); + + final boolean genAddRemoveInsteadOfSet = true; + if (!genAddRemoveInsteadOfSet || field.clType > 0) { + if (field.clType == FieldDesc.SIMPLE) { + if (NOT_COMPARE_MODE && td.type != TypeDesc.TypeEnum.GROUP_INTERFACE) { + out.println("\t" + JDOC_OPEN); + if (field.clType < 0) { + out.println("\t" + JDOC_CONT + "Sets the list of " + javaDocTagName + "."); + } else { + out.println("\t" + JDOC_CONT + "Sets the value of the " + javaDocTagName + "."); + } + out.println("\t" + JDOC_CONT + "@param " + paramName + " the new value to set"); + out.println("\t" + JDOC_CLOSE); + if (sbAnnotations.length() > 0) out.println(sbAnnotations); + } + out.print("\t"); + //out.print("public "); + out.print("void set"); + out.print(Util.capitalize(name)); + out.print("("); + if (field.required) { + out.print((JB_OFF ? "" : "@NotNull ")); + } + out.print(type); + out.print(" "); + out.print(paramName); + out.println(");"); + } + } else { + if (NOT_COMPARE_MODE && td.type != TypeDesc.TypeEnum.GROUP_INTERFACE) { + out.println("\t" + JDOC_OPEN); + out.println("\t" + JDOC_CONT + "Adds new child to the list of " + javaDocTagName + "."); + out.println("\t" + JDOC_CONT + "@return created child"); + out.println("\t" + JDOC_CLOSE); + if (sbAnnotations.length() > 0) out.println(sbAnnotations); + } + out.print("\t"); + //out.print("public "); + out.print(elementType + " add"); + out.print(Util.capitalize(field.elementName)); + out.println("();"); +/* + if (!td.isGroupInterface) { + out.println("\t" + JDOC_OPEN); + out.println("\t" + JDOC_CONT + "Adds new child to the list of " + javaDocTagName + "."); + out.println("\t" + JDOC_CONT + "@param " + paramName + " the new child to add"); + out.println("\t" + JDOC_CLOSE); + if (sbAnnotations.length() > 0) out.println(sbAnnotations); + } + out.print("\tpublic void add"); + out.print(capitalize(fields[i].elementName)); + out.print("("); + out.print(fields[i].elementType); + out.print(" "); + out.print(paramName); + out.println(");"); + + if (!td.isGroupInterface) { + out.println("\t" + JDOC_OPEN); + out.println("\t" + JDOC_CONT + "Removes the child from the list of " + javaDocTagName + "."); + out.println("\t" + JDOC_CONT + "@param " + paramName + " the new child to remove"); + out.println("\t" + JDOC_CLOSE); + if (sbAnnotations.length() > 0) out.println(sbAnnotations); + } + out.print("\tpublic "); + out.print("void remove"); + out.print(capitalize(fields[i].elementName)); + out.print("("); + out.print(fields[i].elementType); + out.print(" "); + out.print(paramName); + out.println(");"); +*/ + } + + out.println(""); + out.println(""); + } + out.println("}"); + } catch (IOException ex) { + ex.printStackTrace(); + } finally { + try { + out.close(); + } catch (Exception ex) { + } + fileManager.releaseOutputFile(outFile); + } + } + + private void generateSuper(FileManager fileManager, NamespaceDesc nsd, ModelDesc model, File outDir) { + if (nsd.superClass == null || nsd.superClass.length() == 0) return; + final String typeName = nsd.superClass.substring(nsd.superClass.lastIndexOf(".") + 1); + final String typeQName = model.toJavaQualifiedTypeName("", nsd.superClass, false); + String pkgName = typeQName.substring(0, typeQName.lastIndexOf('.')); + File outFile = new File(outDir, toJavaFileName(typeQName)); + outFile = fileManager.getOutputFile(outFile); + PrintWriter out = null; + try { + Util.log("Generating type: " + typeName + ".."); + out = new PrintWriter(new FileWriter(outFile)); + out.println("// Generated on " + new Date()); + out.println("// DTD/Schema : " + nsd.name); + out.println(""); + if (pkgName != null) { + out.println("package " + pkgName + ";"); + } + out.println(""); + out.println(""); + out.println(JDOC_OPEN); + out.println(JDOC_CONT + nsd.name + " base interface."); + out.println(JDOC_CLOSE); + out.print("public interface " + typeName + " "); + out.println("{"); + + + out.println("}"); + } catch (IOException ex) { + ex.printStackTrace(); + } finally { + try { + if (out != null) { + out.close(); + } + } catch (Exception ex) { + } + fileManager.releaseOutputFile(outFile); + } + } + + private void generateHelper(FileManager fileManager, NamespaceDesc nsd, ModelDesc model, File outDir) { + final Map jtMap = model.jtMap; + final Map nsdMap = model.nsdMap; + if (nsd.helperClass == null || nsd.helperClass.length() == 0) return; + ArrayList jtList = new ArrayList(); + for (TypeDesc td : jtMap.values()) { + if (td.type != TypeDesc.TypeEnum.CLASS) continue; +// if (!nsd.name.equals(td.xsNamespace)) { +// continue; +// } + jtList.add(td); + } + if (jtList.size() == 0) return; + + String typeName = nsd.helperClass.substring(nsd.helperClass.lastIndexOf(".") + 1); + final String typeQName = model.toJavaQualifiedTypeName("", nsd.helperClass, false); + String pkgName = typeQName.substring(0, typeQName.lastIndexOf('.')); + File outFile = new File(outDir, toJavaFileName(typeQName)); + outFile = fileManager.getOutputFile(outFile); + PrintWriter out = null; + try { + Util.log("Generating type: " + typeName + ".."); + out = new PrintWriter(new FileWriter(outFile)); + out.println("// Generated on " + new Date()); + out.println("// DTD/Schema : " + nsd.name); + out.println(""); + if (pkgName != null) { + out.println("package " + pkgName + ";"); + } + out.println(""); + out.println(""); + out.println(JDOC_OPEN); + out.println(JDOC_CONT + nsd.name + " helper class."); + out.println(JDOC_CLOSE); + out.print("public class " + typeName + " "); + out.println("{"); + out.println(""); + +// child index method +// class Holder implements Comparable { +// String str; +// int index; +// +// public Holder(String str, int index) { +// this.str = str; +// this.index = index; +// } +// +// public int compareTo(Object o) { +// return str.compareTo(o==null?null:((Holder)o).str); +// } +// }; +// ArrayList holders = new ArrayList(); +// for (Iterator it = jtList.iterator(); it.hasNext();) { +// TypeDesc td = (TypeDesc) it.next(); +// NamespaceDesc tdnsd = (NamespaceDesc) nsdMap.get(td.xsNamespace); +// String qname = toJavaQualifiedTypeName(tdnsd, td.name); +// ArrayList fields = new ArrayList(td.fdMap.values()); +// Collections.sort(fields, new Comparator() { +// public int compare(Object o1, Object o2) { +// return ((FieldDesc) o1).realIndex - ((FieldDesc) o2).realIndex; +// } +// }); +// for (Iterator it2 = fields.iterator(); it2.hasNext();) { +// FieldDesc fd = (FieldDesc) it2.next(); +// Holder h = new Holder(qname+":"+fd.tagName, fd.realIndex); +// holders.add(h); +// } +// } +// Collections.sort(holders); +// out.println("\tprivate static String[] parentChildTags = new String[] {"); +// int idx = 0; +// for (Iterator it = holders.iterator(); it.hasNext(); idx++) { +// Holder holder = (Holder) it.next(); +// if (idx % 4 == 0) out.print("\n\t\t"); +// out.print("\""+holder.str+"\""); +// if (idx < holders.size()-1) out.print(","); +// } +// out.println("\n\t};"); +// out.println("\tprivate static int[] parentChildIndices = new int[] {"); +// idx = 0; +// for (Iterator it = holders.iterator(); it.hasNext(); idx++) { +// Holder holder = (Holder) it.next(); +// if (idx % 24 == 0) out.print("\n\t\t"); +// out.print(holder.index); +// if (idx < holders.size()-1) out.print(","); +// } +// out.println("\n\t};"); +// out.println(); +// out.println("\tpublic static int getChildIndex(String parentTag, String childTag) {"); +// out.println("\t\tint idx = java.util.Arrays.binarySearch(parentChildTags, parentTag+\":\"+childTag);"); +// out.println("\t\treturn idx < 0? -1: parentChildIndices[idx];"); +// out.println("\t}"); + + + out.println("\tprivate interface GetName { String getName(Object o); }"); + out.println("\tprivate static java.util.HashMap nameMap = new java.util.HashMap();"); + out.println("\tstatic {"); + + for (TypeDesc td : jtList) { + ArrayList fields = new ArrayList(td.fdMap.values()); + Collections.sort(fields, new Comparator() { + public int compare(FieldDesc o1, FieldDesc o2) { + return o1.realIndex - o2.realIndex; + } + }); + int guessPriority = 0; + FieldDesc guessedField = null; + for (FieldDesc fd : fields) { + if (fd.clType == FieldDesc.STR || fd.clType == FieldDesc.SIMPLE || fd.clType == FieldDesc.ATTR) { + if (fd.name.equals("name") && guessPriority < 10) { + guessPriority = 10; + guessedField = fd; + } else if (fd.name.endsWith("Name")) { + if ((fd.name.endsWith(Util.decapitalize(td.name + "Name")) || fd.realIndex < 2) && guessPriority < 10) { + guessPriority = 10; + guessedField = fd; + } else if (fd.name.endsWith(Util.decapitalize("DisplayName")) && guessPriority < 5) { + guessPriority = 5; + guessedField = fd; + } else if (guessPriority < 3) { + guessPriority = 3; + guessedField = fd; + } + } else if (fd.name.equals("value") && guessPriority < 1) { + guessPriority = 1; + guessedField = fd; + } + } else + if ((fd.clType == -FieldDesc.OBJ || fd.clType == -FieldDesc.STR) && fd.name.endsWith("displayNames") && guessPriority < 5) { + guessPriority = 5; + guessedField = fd; + } + } + out.println(); + String qname = model.getNSDPrefix(td) + td.name; + String tdNameString = "\"" + toPresentationName(td.name) + "\""; + out.println("\t\tnameMap.put(" + qname + ".class, new GetName() {"); + out.println("\t\t\tpublic String getName(Object o) {"); + if (guessedField != null) { + out.println("\t\t\t\t" + qname + " my = (" + qname + ") o;"); + String getter = "my.get" + Util.capitalize(guessedField.name) + "()"; + if (guessedField.clType > 0) { + out.println("\t\t\t\tString s = o==null? null:" + getter + + (guessedField.clType == FieldDesc.STR || guessedField.clType == FieldDesc.ATTR ? ".getValue();" : ";")); + out.println("\t\t\t\treturn s==null?" + tdNameString + ":s;"); + } else { + out.println("\t\t\t\treturn (o!=null && " + getter + "!=null && " + getter + ".size()>0)?"); + out.println("\t\t\t\t\tgetPresentationName(" + getter + ".get(0), null):" + tdNameString + ";"); + } + } else { + out.println("\t\t\t\treturn " + tdNameString + ";"); + } + out.println("\t\t\t}"); + out.println("\t\t});"); + } + out.println("\t}"); + + out.println("\tpublic static String getPresentationName(Object o, String def) {"); + out.println("\t\tGetName g = o!=null? nameMap.get(o.getClass().getInterfaces()[0]):null;"); + out.println("\t\treturn g != null?g.getName(o):def;"); + out.println("\t}"); + out.println("}"); + } catch (IOException ex) { + ex.printStackTrace(); + } finally { + try { + out.close(); + } catch (Exception ex) { + } + fileManager.releaseOutputFile(outFile); + } + } + + public static void printDocumentation(PrintWriter out, String str, String prefix) { + if (str == null) return; + StringTokenizer st = new StringTokenizer(str, "\n\r"); + while (st.hasMoreTokens()) { + String line = prefix + st.nextToken(); + out.println(line); + } + } + + public static String toPresentationName(String typeName) { + StringBuffer sb = new StringBuffer(typeName.length() + 10); + boolean prevUp = true; + for (int i = 0; i < typeName.length(); i++) { + char c = typeName.charAt(i); + if (Character.isUpperCase(c) && !prevUp) { + sb.append(' '); + } + sb.append(c); + prevUp = Character.isUpperCase(c); + } + return sb.toString(); + } + + private static String toJavaFileName(String typeName) { + return typeName.replace('.', File.separatorChar) + ".java"; + } + + public static String toJavaIdName(String javaFieldName) { + if (Util.RESERVED_NAMES_MAP.containsKey(javaFieldName)) { + javaFieldName += "_"; + } + return javaFieldName; + } + + +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/Main.java b/xml/dom-impl/src/com/intellij/util/dom/generator/Main.java new file mode 100644 index 000000000000..35993e4e95e9 --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/Main.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * XSD/DTD Model generator tool + * + * By Gregory Shrago + * 2002 - 2006 + */ +package com.intellij.util.dom.generator; + +import java.io.File; + +/** + * @author Gregory.Shrago + */ +public class Main { + + + public static void main(String argv[]) throws Exception { + if (argv.length != 4) { + System.out.println("Usage: Main "); + } + String mode = argv[0]; + final ModelLoader loader; + if (mode.equalsIgnoreCase("xsd")) { + loader = new XSDModelLoader(); + } + else if (mode.equalsIgnoreCase("dtd")) { + loader = new DTDModelLoader(); + } + else { + System.out.println("'"+mode+"' format not supported"); + System.exit(-1); + return; + } + final File modelRoot = new File(argv[1]); + final File outputRoot = new File(argv[2]); + final File configXml = new File(argv[3]); + + outputRoot.mkdirs(); + final ModelGen modelGen = new ModelGen(loader); + modelGen.loadConfig(configXml); + modelGen.perform(outputRoot, modelRoot); + } + +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/MergingFileManager.java b/xml/dom-impl/src/com/intellij/util/dom/generator/MergingFileManager.java new file mode 100644 index 000000000000..d544f2ab0be6 --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/MergingFileManager.java @@ -0,0 +1,245 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * XSD/DTD Model generator tool + * + * By Gregory Shrago + * 2002 - 2006 + */ +package com.intellij.util.dom.generator; + +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.TreeSet; + +/** + * @author Gregory.Shrago + */ +public class MergingFileManager implements FileManager { + + public File getOutputFile(File target) { + File outFile = target; + if (!outFile.getParentFile().exists() && !outFile.getParentFile().mkdirs()) { + Util.logerr("parent mkdirs failed: " + outFile); + return null; + } + for (int i = 0; outFile.exists(); i++) { + outFile = new File(target.getParentFile(), target.getName() + ".tmp." + i); + } + return outFile; + } + + public File releaseOutputFile(File outFile) { + int idx = outFile.getName().indexOf(".tmp."); + File target = outFile; + if (idx > -1) { + target = new File(outFile.getParentFile(), outFile.getName().substring(0, idx)); + String[] curLines = loadFile(outFile); + String[] prevLines = loadFile(target); + String[] mergedLines = mergeLines(curLines, prevLines); + if (mergedLines != prevLines) { + if (mergedLines == curLines) { + if (target.exists() && !target.delete()) { + Util.logerr("file replace failed: " + target); + outFile.delete(); + } else { + outFile.renameTo(target); + Util.logwarn("file replaced: " + target); + } + } else { + outFile.delete(); + if (target.exists() && !target.delete()) { + Util.logerr("file replace failed: " + target); + } else { + writeFile(target, mergedLines); + Util.logwarn("file merged: " + target); + } + } + } else { + outFile.delete(); + } + } + return target; + } + + private static String[] mergeLines(String[] curLines, String[] prevLines) { + if (prevLines.length == 0) return curLines; + ArrayList merged = new ArrayList(); + int curIdx = 0, prevIdx = 0; + String cur, prev; + boolean classScope = false; + boolean importMerged = false; + for (int i = 0; i < Math.max(curLines.length, prevLines.length); i++) { + cur = curIdx < curLines.length ? curLines[curIdx] : ""; + prev = prevIdx < prevLines.length ? prevLines[prevIdx] : ""; + if (classScope) { + merged.addAll(Arrays.asList(curLines).subList(curIdx, curLines.length)); + break; + } else if (prev.trim().startsWith("import ") || cur.trim().startsWith("import ")) { + if (importMerged) continue; + importMerged = true; + int[] indices = new int[]{curIdx, prevIdx}; + mergeImports(merged, curLines, prevLines, indices); + curIdx = indices[0]; + prevIdx = indices[1]; + } else if (cur.equals(prev)) { + if (cur.trim().startsWith("public interface ") + || cur.trim().startsWith("public enum ")) classScope = true; + merged.add(cur); + curIdx++; + prevIdx++; + } else if (prev.trim().startsWith("@")) { + merged.add(prev); + prevIdx++; + } else if (cur.trim().startsWith("@")) { + merged.add(cur); + curIdx++; + } else if (cur.trim().startsWith("package ") && prev.trim().startsWith("package ")) { + merged.add(prev); + curIdx++; + prevIdx++; + } else if (cur.trim().startsWith("public interface ") && prev.trim().startsWith("public interface ")) { + classScope = true; + prevIdx = addAllStringsUpTo(merged, prevLines, prevIdx, "{"); + curIdx = addAllStringsUpTo(null, curLines, curIdx, "{"); + } else if (cur.trim().startsWith("* ")) { + curIdx = addAllStringsUpTo(merged, curLines, curIdx, "*/"); + if (prev.trim().startsWith("* ") || prev.trim().endsWith("*/")) { + prevIdx = addAllStringsUpTo(null, prevLines, prevIdx, "*/"); + } + } else { + merged.add(cur); + curIdx++; + prevIdx++; + } + } + String[] mergedLines = merged.toArray(new String[merged.size()]); + if (compareLines(mergedLines, prevLines, 2) == 0) { + return prevLines; + } else if (compareLines(mergedLines, curLines, 2) == 0) { + return curLines; + } else { + return mergedLines; + } + } + + private static void mergeImports(ArrayList merged, String[] curLines, String[] prevLines, int[] indices) { + TreeSet externalClasses = new TreeSet(); + for (int i = 0; i < curLines.length; i++) { + String line = curLines[i].trim(); + if (line.startsWith("import ") && line.endsWith(";")) { + indices[0] = i + 1; + final String name = line.substring("import ".length(), line.length() - 1).trim(); + if (name.endsWith("*")) continue; + externalClasses.add(name); + } + } + for (int i = 0; i < prevLines.length; i++) { + String line = prevLines[i].trim(); + if (line.startsWith("import ") && line.endsWith(";")) { + indices[1] = i + 1; + final String name = line.substring("import ".length(), line.length() - 1).trim(); + if (name.endsWith("*")) continue; + externalClasses.add(name); + } + } + boolean javaLang = false; + for (String s : externalClasses) { + if (s.startsWith("java.")) { + javaLang = true; + continue; + } + merged.add("import " + s + ";"); + } + if (javaLang) { + merged.add(""); + for (String s : externalClasses) { + if (!s.startsWith("java.")) continue; + merged.add("import " + s + ";"); + } + } + } + + private static int addAllStringsUpTo(ArrayList merged, String[] lines, int startIdx, String upTo) { + String str; + do { + str = startIdx < lines.length ? lines[startIdx] : upTo; + if (merged != null) merged.add(str); + startIdx++; + } while (!str.trim().endsWith(upTo) && startIdx < lines.length); + return startIdx; + } + + private static int compareLines(String[] mergedLines, String[] curLines, int start) { + if (mergedLines.length < curLines.length) return -1; + if (mergedLines.length > curLines.length) return 1; + for (int i = start; i < mergedLines.length; i++) { + final int comp = mergedLines[i].compareTo(curLines[i]); + if (comp != 0) return comp; + } + return 0; + } + + + private static void writeFile(File target, String[] mergedLines) { + PrintWriter out = null; + try { + int lineCount = mergedLines.length; + while (lineCount > 0 && mergedLines[lineCount - 1].length() == 0) lineCount--; + out = new PrintWriter(new FileWriter(target)); + for (int i = 0; i < lineCount; i++) { + String mergedLine = mergedLines[i]; + out.println(mergedLine); + } + } catch (Exception ex) { + ex.printStackTrace(); + } finally { + if (out != null) { + try { + out.close(); + } catch (Exception e) { + } + } + } + } + + + private static String[] loadFile(File f1) { + if (!f1.exists()) return new String[0]; + ArrayList list = new ArrayList(); + BufferedReader in = null; + try { + in = new BufferedReader(new FileReader(f1)); + String str; + while ((str = in.readLine()) != null) { + list.add(str); + } + } catch (Exception ex) { + ex.printStackTrace(); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + return list.toArray(new String[list.size()]); + } + +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/ModelDesc.java b/xml/dom-impl/src/com/intellij/util/dom/generator/ModelDesc.java new file mode 100644 index 000000000000..fc0a23139e6e --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/ModelDesc.java @@ -0,0 +1,170 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * XSD/DTD Model generator tool + * + * By Gregory Shrago + * 2002 - 2006 + */ +package com.intellij.util.dom.generator; + +import org.apache.xerces.xs.XSObject; + +import javax.xml.namespace.QName; +import java.io.PrintWriter; +import java.util.*; + +/** + * @author Gregory.Shrago + */ +public class ModelDesc { + final Map name2replaceMap = new HashMap(); + final Map qname2FileMap = new HashMap(); + final Map nsdMap = new HashMap(); + final Map jtMap = new TreeMap(); + + + public String getNSDPrefix(TypeDesc td) { + return getNSDPrefix(td.xsNamespace, td.name, td.type == TypeDesc.TypeEnum.ENUM); + } + + public String getNSDPrefix(String namespace, String name, boolean isEnum) { + final int lastIdx = name.lastIndexOf("."); + if (lastIdx > -1) return name.substring(0, lastIdx + 1); + final NamespaceDesc nsd = getNSD(namespace); + if (isEnum && nsd.enumPkg != null) { + return nsd.enumPkg + "."; + } + if (nsd.pkgNames != null) { + final QName qname = new QName(namespace, name); + final String files = qname2FileMap.get(qname); + if (files != null) { + for (int i = 0; i < nsd.pkgNames.length; i += 2) { + String file = nsd.pkgNames[i]; + String pkg = nsd.pkgNames[i + 1]; + if (files.contains(":" + file + ":")) { + return pkg + "."; + } + } + } + } + return nsd.pkgName != null && nsd.pkgName.length() > 0 ? nsd.pkgName + "." : ""; + } + + public NamespaceDesc getNSD(String namespace) { + NamespaceDesc nsd = nsdMap.get(namespace); + if (nsd == null) { + nsd = nsdMap.get(""); + } + return nsd; + } + + + public String toJavaTypeName(String tname, String ns) { + final int lastIndex = tname.lastIndexOf('.'); + String xmlName = lastIndex>-1? tname.substring(lastIndex+1): tname; + NamespaceDesc nsd = getNSD(ns); + if (!ns.endsWith(".dtd")) { + if (xmlName.endsWith(Util.ANONYMOUS_ELEM_TYPE_SUFFIX)) xmlName = xmlName.substring(0, xmlName.length() - Util.ANONYMOUS_ELEM_TYPE_SUFFIX.length()); + else if (xmlName.endsWith(Util.ANONYMOUS_ATTR_TYPE_SUFFIX)) xmlName = xmlName.substring(0, xmlName.length() - Util.ANONYMOUS_ATTR_TYPE_SUFFIX.length()); + else if (xmlName.endsWith(Util.TYPE_SUFFIX)) xmlName = xmlName.substring(0, xmlName.length() - Util.TYPE_SUFFIX.length()); + } + String rc = Util.capitalize(Util.toJavaName(xmlName)); + if (nsd.prefix != null && nsd.prefix.length()>0 && !rc.startsWith(nsd.prefix)) { + rc = nsd.prefix + rc; + } + if (Util.RESERVED_NAMES_MAP.containsKey(rc)) { + rc = Util.RESERVED_NAMES_MAP.get(rc); + } + if (name2replaceMap.containsKey(rc)) { + rc = Util.expandProperties(name2replaceMap.get(rc), nsd.props); + } + + return rc; + } + + public String toJavaQualifiedTypeName(XSObject xs, Map nsdMap, boolean isEnum) { + String typeName = toJavaTypeName(xs.getName(), xs.getNamespace()); + return getNSDPrefix(xs.getNamespace(), xs.getName(), isEnum) + typeName; + } + + public String toJavaQualifiedTypeName(String namespace, String xmlname, boolean isEnum) { + return getNSDPrefix(namespace, xmlname, isEnum) + toJavaTypeName(xmlname, namespace); + } + + void dump(final PrintWriter out) { +// out.println("-- qname2FileMap ---"); +// out.println(qname2FileMap); + out.println("-- nsdMap ---"); + for (Map.Entry entry : nsdMap.entrySet()) { + out.println("namespace key: "+entry.getKey()); + dumpNamespace(entry.getValue(), out); + } + out.println("-- jtMap ---"); + for (Map.Entry entry : jtMap.entrySet()) { + out.println("type key: "+entry.getKey()); + dumpTypeDesc(entry.getValue(), out); + } + } + + private void dumpTypeDesc(TypeDesc td, PrintWriter out) { + final ArrayList superList; + if (td.supers !=null) { + superList = new ArrayList(); + for (TypeDesc aSuper : td.supers) { + superList.add(getNSDPrefix(aSuper)+aSuper.name); + } + } else superList = null; + out.println(" name " + td.name); + out.println(" type " + td.type); + out.println(" xsName " + td.xsName); + out.println(" xsNS " + td.xsNamespace); + out.println(" dups " + td.duplicates); + out.println(" supers " + (superList!=null?superList:"null")); + out.println(" doc " + (td.documentation != null ? td.documentation.length() : "null")); + for (Map.Entry entry : td.fdMap.entrySet()) { + out.println(" field key: " + entry.getKey()); + dumpFieldDesc(td, entry.getValue(), out); + } + } + + private void dumpFieldDesc(TypeDesc td, FieldDesc fd, PrintWriter out) { + out.println(" name " + fd.name); + if (td.type == TypeDesc.TypeEnum.ENUM) return; + out.println(" clType " + fd.clType); + out.println(" required " + fd.required); + out.println(" index " + fd.idx+"/"+fd.realIndex); + out.println(" choiceOpt " + fd.choiceOpt); + out.println(" choice " + (fd.choice!=null?fd.choice.length:"null")); + out.println(" content " + fd.contentQualifiedName); + out.println(" dupIdx " + fd.duplicateIndex); + out.println(" elName " + fd.elementName); + out.println(" elType " + fd.elementType); + out.println(" def " + fd.def); + out.println(" doc " + (fd.documentation != null? fd.documentation.length(): "null")); + } + + private void dumpNamespace(NamespaceDesc value, PrintWriter out) { + if (value.skip) return; + out.println(" name "+value.name); + out.println(" prefix "+value.prefix); + out.println(" pkgName "+value.pkgName); + out.println(" pkgNames "+(value.pkgNames != null?Arrays.asList(value.pkgNames):"null")); + out.println(" enumPkg "+value.enumPkg); + out.println(" super "+value.superClass); + } +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/ModelGen.java b/xml/dom-impl/src/com/intellij/util/dom/generator/ModelGen.java new file mode 100644 index 000000000000..aaad3ca302ed --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/ModelGen.java @@ -0,0 +1,176 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.dom.generator; + +import org.jdom.Element; +import org.jdom.Document; +import org.jdom.input.SAXBuilder; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.apache.xerces.xni.parser.XMLEntityResolver; +import org.apache.xerces.xni.parser.XMLInputSource; +import org.apache.xerces.xni.XMLResourceIdentifier; +import org.apache.xerces.xni.XNIException; + +import java.util.*; +import java.io.File; +import java.io.IOException; +import java.io.CharArrayReader; + +/** + * @author Gregory.Shrago + */ +public class ModelGen { + private final ModelDesc model = new ModelDesc(); + private final Map schemaLocationMap = new HashMap(); + private final ModelLoader loader; + private final Emitter emitter; + private final FileManager fileManager; + + + public ModelGen(ModelLoader loader) { + this(loader, new JetBrainsEmitter(), new MergingFileManager()); + } + + public ModelGen(ModelLoader loader, Emitter emitter, FileManager fileManager) { + this.loader = loader; + this.emitter = emitter; + this.fileManager = fileManager; + } + + public ModelDesc getModel() { + return model; + } + + public static Element loadXml(File configXml) throws Exception { + SAXBuilder saxBuilder = new SAXBuilder(); + saxBuilder.setEntityResolver(new EntityResolver() { + public InputSource resolveEntity(String publicId, + String systemId) + throws SAXException, IOException { + return new InputSource(new CharArrayReader(new char[0])); + } + }); + final Document document = saxBuilder.build(configXml); + return document.getRootElement(); + } + + public void loadConfig(File configXml) throws Exception { + loadConfig(loadXml(configXml)); + } + + public void setConfig(String schema, String location, NamespaceDesc desc) { + schemaLocationMap.put(schema, location); + model.nsdMap.put("http://www.w3.org/2001/XMLSchema", new NamespaceDesc("http://www.w3.org/2001/XMLSchema")); + model.nsdMap.put("http://www.w3.org/2001/XMLSchema-instance", new NamespaceDesc("http://www.w3.org/2001/XMLSchema-instance")); + model.nsdMap.put("", new NamespaceDesc("", "", "com.intellij.util.xml.DomElement", "", null, null, null, null)); + model.nsdMap.put(desc.name, desc); + } + + public void loadConfig(Element element) { + final Element namespaceEl = element.getChild("namespaces"); + for (Element e : (List) namespaceEl.getChildren("schemaLocation")) { + final String name = e.getAttributeValue("name"); + final String file = e.getAttributeValue("file"); + schemaLocationMap.put(name, file); + } + for (Element e : (List) namespaceEl.getChildren("reserved-name")) { + final String name = e.getAttributeValue("name"); + final String replacement = e.getAttributeValue("replace-with"); + model.name2replaceMap.put(name, replacement); + } + NamespaceDesc def = new NamespaceDesc("", "generated", "java.lang.Object", "", null, null, null, null); + for (Element nsElement : (List) namespaceEl.getChildren("namespace")) { + final String name = nsElement.getAttributeValue("name"); + final NamespaceDesc nsDesc = new NamespaceDesc(name, def); + + final String skip = nsElement.getAttributeValue("skip"); + final String prefix = nsElement.getAttributeValue("prefix"); + final String superC = nsElement.getAttributeValue("super"); + final String imports = nsElement.getAttributeValue("imports"); + final String packageS = nsElement.getAttributeValue("package"); + final String packageEnumS = nsElement.getAttributeValue("enums"); + final String interfaces = nsElement.getAttributeValue("interfaces"); + final ArrayList list = new ArrayList(); + for (Element pkgElement : (List) nsElement.getChildren("package")) { + final String pkgName = pkgElement.getAttributeValue("name"); + final String fileName = pkgElement.getAttributeValue("file"); + list.add(fileName); + list.add(pkgName); + } + for (Element pkgElement : (List) nsElement.getChildren("property")) { + final String propertyName = pkgElement.getAttributeValue("name"); + final String propertyValue = pkgElement.getAttributeValue("value"); + nsDesc.props.put(propertyName, propertyValue); + } + + if (skip != null) nsDesc.skip = skip.equalsIgnoreCase("true"); + if (prefix != null) nsDesc.prefix = prefix; + if (superC != null) nsDesc.superClass = superC; + if (imports != null) nsDesc.imports = imports; + if (packageS != null) nsDesc.pkgName = packageS; + if (packageEnumS != null) nsDesc.enumPkg = packageEnumS; + if (interfaces != null) nsDesc.intfs = interfaces; + if (!list.isEmpty()) nsDesc.pkgNames = list.toArray(new String[list.size()]); + if (name.length() == 0) def = nsDesc; + model.nsdMap.put(name, nsDesc); + } + } + + public void perform(final File outputRoot, final File... modelRoots) throws Exception { + loadModel(modelRoots); + emitter.emit(fileManager, model, outputRoot); + + Util.log("Done."); + } + + public void loadModel(final File... modelRoots) throws Exception { + XMLEntityResolver resolver = new XMLEntityResolver() { + public XMLInputSource resolveEntity(XMLResourceIdentifier xmlResourceIdentifier) throws XNIException, IOException { + String esid = xmlResourceIdentifier.getExpandedSystemId(); + if (esid == null) { + final String location = schemaLocationMap.get(xmlResourceIdentifier.getNamespace()); + if (location != null) { + esid = location; + } else { + return null; + } + } + // Util.log("resolving "+esid); + File f = null; + for (File root : modelRoots) { + if (root == null) continue; + final String fileName = esid.substring(esid.lastIndexOf('/') + 1); + f = new File(root, fileName); + } + if (f == null || !f.exists()) { + Util.logerr("unable to resolve: " + esid); + return null; + } + esid = f.getPath(); + return new XMLInputSource(null, esid, null); + } + }; + ArrayList files = new ArrayList(); + for (File root : modelRoots) { + files.addAll(Arrays.asList(root.listFiles())); + } + loader.loadModel(model, files, resolver); + Util.log(model.jtMap.size() + " java types loaded"); + } + +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/ModelLoader.java b/xml/dom-impl/src/com/intellij/util/dom/generator/ModelLoader.java new file mode 100644 index 000000000000..c7ff4b70a622 --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/ModelLoader.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.util.dom.generator; + +import org.apache.xerces.xni.parser.XMLEntityResolver; + +import java.io.File; +import java.util.Collection; + +public interface ModelLoader { + void loadModel(ModelDesc model, Collection files, XMLEntityResolver resolver) throws Exception; +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/NamespaceDesc.java b/xml/dom-impl/src/com/intellij/util/dom/generator/NamespaceDesc.java new file mode 100644 index 000000000000..674a976ab34e --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/NamespaceDesc.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * XSD/DTD Model generator tool + * + * By Gregory Shrago + * 2002 - 2006 + */ +package com.intellij.util.dom.generator; + +import java.util.Map; +import java.util.HashMap; + +public class NamespaceDesc { + public NamespaceDesc(String name, + String pkgName, + String superClass, + String prefix, + String factoryClass, + String helperClass, + String imports, + String intfs) { + this.name = name; + this.pkgName = pkgName; + this.superClass = superClass; + this.prefix = prefix; + this.factoryClass = factoryClass; + this.helperClass = helperClass; + this.imports = imports; + this.intfs = intfs; + } + + public NamespaceDesc(String name) { + this(name, "generated", "java.lang.Object", "", null, null, null, null); + skip = true; + } + + + public NamespaceDesc(String name, NamespaceDesc def) { + this.name = name; + this.pkgName = def.pkgName; + this.superClass = def.superClass; + this.prefix = def.prefix; + this.factoryClass = def.factoryClass; + this.helperClass = def.helperClass; + this.imports = def.imports; + this.intfs = def.intfs; + } + + final Map props = new HashMap(); + final String name; + String pkgName; + String superClass; + String prefix; + String factoryClass; + String helperClass; + String imports; + String intfs; + boolean skip; + String[] pkgNames; + String enumPkg; + + + public String toString() { + return "NS:"+name+" "+(skip?"skip":"")+pkgName; + } +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/TypeDesc.java b/xml/dom-impl/src/com/intellij/util/dom/generator/TypeDesc.java new file mode 100644 index 000000000000..cfb7681100e6 --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/TypeDesc.java @@ -0,0 +1,52 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * XSD/DTD Model generator tool + * + * By Gregory Shrago + * 2002 - 2006 + */ +package com.intellij.util.dom.generator; + +import java.util.Map; +import java.util.TreeMap; + +public class TypeDesc { + public enum TypeEnum { + CLASS, ENUM, GROUP_INTERFACE + } + + public TypeDesc(String xsName, String xsNamespace, String name, TypeEnum type) { + this.xsName = xsName; + this.xsNamespace = xsNamespace; + this.name = name; + this.type = type; + } + + TypeEnum type; + final String xsName; + final String xsNamespace; + final String name; + final Map fdMap = new TreeMap(); + boolean duplicates; + String documentation; + TypeDesc[] supers; + + public String toString() { + return (type == TypeEnum.ENUM ? "enum" : "type") + ": " + name + ";" + xsName + ";"; + } +} diff --git a/xml/dom-impl/src/com/intellij/util/dom/generator/Util.java b/xml/dom-impl/src/com/intellij/util/dom/generator/Util.java new file mode 100644 index 000000000000..a6e33d7e42f3 --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/dom/generator/Util.java @@ -0,0 +1,222 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * XSD/DTD Model generator tool + * + * By Gregory Shrago + * 2002 - 2006 + */ +package com.intellij.util.dom.generator; + +import org.apache.xerces.xs.XSObject; + +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + + +public class Util { + + public static final String ANONYMOUS_ELEM_TYPE_SUFFIX = "ElemType"; + public static final String ANONYMOUS_ATTR_TYPE_SUFFIX = "AttrType"; + public static final String TYPE_SUFFIX = "Type"; + + // + // Constants + // + public static final String XSD_NS = "http://www.w3.org/2001/XMLSchema"; + + // reserved names map + public static final String[] RESERVED_NAMES_TABLE = { + "abstract", "default", "if", "private", "this", + "boolean", "do", "implements", "protected", "throw", + "break", "double", "import", "public", "throws", + "byte", "else", "instanceof", "return", "transient", + "case", "extends", "int", "short", "try", + "catch", "final", "interface", "static", "void", + "char", "finally", "long", "strictfp", "volatile", + "class", "float", "native", "super", "while", + "const", "for", "new", "switch", + "continue", "goto", "package", "synchronized" + }; + public static final HashMap RESERVED_NAMES_MAP; + + static { + RESERVED_NAMES_MAP = new HashMap(); + for (String aRESERVED_NAMES_TABLE : RESERVED_NAMES_TABLE) { + // RESERVED_NAMES_MAP.put(RESERVED_NAMES_TABLE[i], RESERVED_NAMES_TABLE[i]+"_"); + // as far as there is no actual field but setters/getters: + RESERVED_NAMES_MAP.put(aRESERVED_NAMES_TABLE, aRESERVED_NAMES_TABLE); + } + //RESERVED_NAMES_MAP.put("class", "clazz"); + } + + + static void log(String str) { + System.out.println(str); + } + + static void logwarn(String str) { + System.out.println("[warn] " + str); + } + + static void logerr(String str) { + System.out.println("[error] " + str); + } + + public static String pluralize(String suggestion) { + // return suggestion+"List"; + final String VOWELS = "aeiouy"; + if (suggestion.endsWith("s") || suggestion.endsWith("x") || + suggestion.endsWith("ch")) { + suggestion += "es"; + } else { + int len = suggestion.length(); + if (suggestion.endsWith("y") && len > 1 && VOWELS.indexOf(suggestion.charAt(len - 2)) < 0) { + suggestion = suggestion.substring(0, len - 1) + "ies"; + } else { + suggestion += "s"; + } + } + return suggestion; + } + + public static String toJavaFieldName(String xmlName) { + String rc = toJavaName(xmlName); + if (RESERVED_NAMES_MAP.containsKey(rc)) { + rc = RESERVED_NAMES_MAP.get(rc); + } + return rc; + } + + + public static String computeEnumConstantName(String val, String typeName) { + String id = val; + for (int i = 1; i < id.length(); i++) { + if (Character.isUpperCase(id.charAt(i)) + && Character.isLowerCase(id.charAt(i - 1))) { + id = id.substring(0, i) + "_" + id.substring(i); + i++; + } + } + id = id.toUpperCase(); + id = id.replace('.', '_').replace('-', '_'); + if (id.length() < 2 || !Character.isJavaIdentifierStart(id.charAt(0))) { + id = typeName + "_" + id; + } + return id; + } + + + public static String capitalize(String str) { + return Character.toUpperCase(str.charAt(0)) + str.substring(1); + } + + public static String decapitalize(String str) { + return Character.toLowerCase(str.charAt(0)) + str.substring(1); + } + + public static String toJavaName(String xmlName) { + xmlName = xmlName.substring(xmlName.lastIndexOf(':') + 1); + StringTokenizer st = new StringTokenizer(xmlName, "-"); + StringBuffer sb = new StringBuffer(st.nextToken()); + while (st.hasMoreTokens()) { + sb.append(capitalize(st.nextToken())); + } + return sb.toString(); + } + + public static String toDefXmlTagName(XSObject xs) { + String xmlName = xs.getName(); + if (xmlName.endsWith(TYPE_SUFFIX)) xmlName = xmlName.substring(0, xmlName.length() - 4); + return xmlName; + } + + public static String toDefXmlTagName(String tname) { + String xmlName = tname; + if (xmlName.endsWith(TYPE_SUFFIX)) xmlName = xmlName.substring(0, xmlName.length() - TYPE_SUFFIX.length()); + return xmlName; + } + + + public static boolean addToNameMap(Map fdMap, FieldDesc fd1, boolean merge) { + boolean duplicates = false; + FieldDesc fd2; + if ((fd2 = fdMap.remove(fd1.name)) != null) { + if (fd2.clType == FieldDesc.ATTR) { + // attr <-> field + fd2.name = fd1.name + "Attr"; + fdMap.put(fd2.name, fd2); + } else if (merge) { + fdMap.put(fd2.name, fd2); + return false; + } else { + duplicates = true; + fd2.name = fd1.name + "1"; + fd2.duplicateIndex = 1; + fdMap.put(fd2.name, fd2); + fd1.name = fd1.name + "2"; + fd1.duplicateIndex = 2; + } + } else if ((fd2 = fdMap.get(fd1.name + "1")) != null) { + int id = 2; + while (fdMap.containsKey(fd1.name + id)) id++; + fd1.name = fd1.name + id; + fd1.duplicateIndex = id; + } + fdMap.put(fd1.name, fd1); + return duplicates; + } + + public static String expandProperties(final String str, final Map map) { + if (str.indexOf("${") == -1) return str; + int state = 0; + final StringBuilder result = new StringBuilder(); + final StringBuilder variable = new StringBuilder(); + for (int i=0; i files, XMLEntityResolver resolver) throws Exception { + this.model = model; + processSchemas(files, resolver); + } + + public static boolean checkComplexType(XSTypeDefinition td) { + if (td.getTypeCategory() != XSTypeDefinition.COMPLEX_TYPE) return false; + XSComplexTypeDefinition ctd = (XSComplexTypeDefinition) td; + if (ctd.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_ELEMENT) { + return true; + } + if ((td instanceof XSComplexTypeDecl) && ((XSComplexTypeDecl) td).getAbstract()) return true; + if (TEXT_ELEMENTS_ARE_COMPLEX) return true; + if (ctd.getAttributeUses() != null) { + for (int i = 0; i < ctd.getAttributeUses().getLength(); i++) { + XSSimpleTypeDefinition xsstd = ((XSAttributeUse) ctd.getAttributeUses().item(i)).getAttrDeclaration().getTypeDefinition(); + if ("ID".equals(xsstd.getName())) continue; + return true; + } + } + return false; + } + + public static boolean checkEnumType(XSTypeDefinition td) { + final XSSimpleTypeDefinition st; + if (td.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) { + XSComplexTypeDefinition ctd = (XSComplexTypeDefinition) td; + if (ctd.getContentType() != XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) return false; + if (ctd.getAttributeUses() != null) { + for (int i = 0; i < ctd.getAttributeUses().getLength(); i++) { + XSSimpleTypeDefinition xsstd = ((XSAttributeUse) ctd.getAttributeUses().item(i)).getAttrDeclaration().getTypeDefinition(); + if ("ID".equals(xsstd.getName())) continue; + return false; + } + } + st = ctd.getSimpleType(); + } else if (td.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) { + st = (XSSimpleTypeDefinition) td; + } else { + return false; + } + return st.getLexicalEnumeration() != null && st.getLexicalEnumeration().getLength() != 0; + } + + private static boolean checkBooleanType(XSTypeDefinition td) { + if (td.getTypeCategory() != XSTypeDefinition.SIMPLE_TYPE) return false; + final XSSimpleTypeDefinition st = ((XSSimpleTypeDefinition) td); + final XSObjectList facets = st.getFacets(); + for (int i = 0; i < facets.getLength(); i++) { + final XSFacet facet = (XSFacet) facets.item(i); + if (facet.getFacetKind() == XSSimpleTypeDefinition.FACET_LENGTH) { + if ("0".equals(facet.getLexicalFacetValue())) { + return true; + } + } + } + return false; + } + + + private XSModel loadSchema(File schemaFile, XMLEntityResolver resolver) throws Exception { + // get DOM Implementation using DOM Registry + System.setProperty( + DOMImplementationRegistry.PROPERTY, + "org.apache.xerces.dom.DOMXSImplementationSourceImpl"); + DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); + XSImplementation impl = (XSImplementation) registry.getDOMImplementation("XS-Loader"); + XSLoader schemaLoader = impl.createXSLoader(null); + DOMConfiguration config = schemaLoader.getConfig(); + + // create Error Handler + DOMErrorHandler errorHandler = new DOMErrorHandler() { + public boolean handleError(DOMError domError) { + Util.log("DOMError: " + domError.getMessage()); + Object relatedException = domError.getRelatedException(); + if (relatedException != null) { + Util.log("DOMError: " + relatedException); + if (relatedException instanceof Throwable) { + ((Throwable) relatedException).printStackTrace(System.out); + } + } + return false; + } + }; + // set error handler + config.setParameter("error-handler", errorHandler); + // set validation feature + config.setParameter("validate", Boolean.TRUE); + // parse document + config.setParameter("error-handler", errorHandler); + config.setParameter("http://apache.org/xml/properties/internal/entity-resolver", resolver); + + XSModel model = schemaLoader.loadURI(schemaFile.getAbsolutePath()); + if (model == null) return null; + XSNamedMap components = model.getComponents(XSConstants.ELEMENT_DECLARATION); + for (int i = 0; i < components.getLength(); i++) { + XSObject obj = components.item(i); + QName qname = new QName(obj.getNamespace(), obj.getName()); + String file = this.model.qname2FileMap.get(qname); + this.model.qname2FileMap.put(qname, (file == null ? "" : file + ";") + schemaFile.getName()); + } + components = model.getComponents(XSConstants.TYPE_DEFINITION); + for (int i = 0; i < components.getLength(); i++) { + XSObject obj = components.item(i); + QName qname = new QName(obj.getNamespace(), obj.getName()); + String file = this.model.qname2FileMap.get(qname); + this.model.qname2FileMap.put(qname, (file == null ? "" : file) + ":" + schemaFile.getName() + ":"); + } + return model; + } + + public void processSchemas(Collection schemas, XMLEntityResolver resolver) throws Exception { + Map nsdMap = model.nsdMap; + Map jtMap = model.jtMap; + final NamespaceDesc nsdDef = nsdMap.get(""); + final ArrayList models = new ArrayList(); + final HashMap types = new HashMap(); + for (File schemaFile : schemas) { + String fileName = schemaFile.getPath(); + if (schemaFile.isDirectory() || !fileName.endsWith(".xsd")) { + Util.log("skipping " + fileName); + continue; + } + Util.log("loading " + fileName + ".."); + + final XSModel model = loadSchema(schemaFile, resolver); + if (model == null) continue; + + final StringList namespaceList = model.getNamespaces(); + for (int i = 0; i < namespaceList.getLength(); i++) { + String ns = namespaceList.item(i); + if (!nsdMap.containsKey(ns)) { + Util.log("Adding default namespace desc for: " + ns); + NamespaceDesc nsd = new NamespaceDesc(ns, nsdDef); + nsdMap.put(ns, nsd); + } + } + models.add(model); + final XSNamedMap typeDefMap = model.getComponents(XSConstants.TYPE_DEFINITION); + for (int i = 0; i < typeDefMap.getLength(); i++) { + XSTypeDefinition o = (XSTypeDefinition) typeDefMap.item(i); + NamespaceDesc nsd = nsdMap.get(o.getNamespace()); + if (nsd != null && nsd.skip) continue; + final String key = o.getName() + "," + o.getNamespace(); + types.put(key, o); + } + final XSNamedMap elementDeclMap = model.getComponents(XSConstants.ELEMENT_DECLARATION); + for (int i = 0; i < elementDeclMap.getLength(); i++) { + XSElementDeclaration o = (XSElementDeclaration) elementDeclMap.item(i); + if (o.getTypeDefinition().getAnonymous() && (o.getTypeDefinition() instanceof XSComplexTypeDefinition)) { + //types.put(o.getName() + "," + o.getNamespace(), o); + XSComplexTypeDefinition ctd = makeTypeFromAnonymous(o); + NamespaceDesc nsd = nsdMap.get(o.getNamespace()); + if (nsd != null && nsd.skip) continue; + final String key = ctd.getName() + "," + ctd.getNamespace(); + types.put(key, ctd); + } + } + } + Util.log(types.size() + " elements loaded, processing.."); + ArrayList toProcess = new ArrayList(types.values()); + ArrayList toAdd = new ArrayList(); + for (ListIterator it = toProcess.listIterator(); it.hasNext();) { + XSTypeDefinition td = it.next(); + Util.log("processing " + td.getName() + "," + td.getNamespace() + ".."); + if (checkComplexType(td)) { + processType((XSComplexTypeDefinition) td, models, jtMap, nsdMap, toAdd); + } else if (checkEnumType(td)) { + processEnumType(td, jtMap, nsdMap); + } + if (toAdd.size() != 0) { + for (XSComplexTypeDefinition o : toAdd) { + final String key = o.getName() + "," + o.getNamespace(); + if (!types.containsKey(key)) { + Util.log(" adding " + o.getName() + "," + o.getNamespace()); + types.put(key, o); + it.add(o); + it.previous(); + } else { + Util.logwarn(key + " already exists"); + } + } + toAdd.clear(); + } + } + } + + private XSComplexTypeDefinition makeTypeFromAnonymous(XSObject o) { + final XSComplexTypeDecl ctd = new XSComplexTypeDecl(); + if (o instanceof XSElementDeclaration && ((XSElementDeclaration) o).getTypeDefinition() instanceof XSComplexTypeDecl) { + final XSComplexTypeDecl ctd1 = (XSComplexTypeDecl) ((XSElementDeclaration) o).getTypeDefinition(); + final XSObjectListImpl annotations = ctd1.getAnnotations() instanceof XSObjectListImpl ? (XSObjectListImpl) ctd1.getAnnotations() : new XSObjectListImpl(); + ctd.setValues(o.getName(), ctd1.getNamespace(), ctd1.getBaseType(), ctd1.getDerivationMethod(), + ctd1.getFinal(), ctd1.getProhibitedSubstitutions(), ctd1.getContentType(), + ctd1.getAbstract(), ctd1.getAttrGrp(), (XSSimpleType) ctd1.getSimpleType(), + (XSParticleDecl) ctd1.getParticle(), annotations); + ctd.setName(o.getName() + Util.ANONYMOUS_ELEM_TYPE_SUFFIX); + } else if (o instanceof XSAttributeDeclaration) { + final XSSimpleTypeDecl ctd1 = (XSSimpleTypeDecl) ((XSAttributeDeclaration) o).getTypeDefinition(); + final XSObjectListImpl annotations = ctd1.getAnnotations() instanceof XSObjectListImpl ? (XSObjectListImpl) ctd1.getAnnotations() : new XSObjectListImpl(); + ctd.setValues(o.getName(), ctd1.getNamespace(), ctd1.getBaseType(), XSConstants.DERIVATION_RESTRICTION, + ctd1.getFinal(), (short) 0, XSComplexTypeDefinition.CONTENTTYPE_SIMPLE, + false, new XSAttributeGroupDecl(), ctd1, + null, annotations); + ctd.setName(o.getName() + Util.ANONYMOUS_ATTR_TYPE_SUFFIX); + } + + model.qname2FileMap.put(new QName(ctd.getNamespace(), ctd.getName()), model.qname2FileMap.get(new QName(o.getNamespace(), o.getName()))); + return ctd; + } + + public void processEnumType(XSTypeDefinition def, Map jtMap, Map nsdMap) throws Exception { + boolean complexType = def instanceof XSComplexTypeDefinition; + if (!nsdMap.containsKey(def.getNamespace())) { + Util.log("Namespace desc not found for: " + def); + } + final String typeName = toJavaTypeName(def, nsdMap); + final TypeDesc td = new TypeDesc(def.getName(), def.getNamespace(), typeName, TypeDesc.TypeEnum.ENUM); + final XSComplexTypeDefinition ct = complexType ? (XSComplexTypeDefinition) def : null; + final XSSimpleTypeDefinition st = (XSSimpleTypeDefinition) (complexType ? ((XSComplexTypeDefinition) def).getSimpleType() : def); + for (int i = 0; i < st.getLexicalEnumeration().getLength(); i++) { + final String s = st.getLexicalEnumeration().item(i); + td.fdMap.put(s, new FieldDesc(Util.computeEnumConstantName(s, td.name), s)); + } + + final XSObjectList anns = complexType ? ct.getAnnotations() : st.getAnnotations(); + + td.documentation = parseAnnotationString("Enumeration " + def.getNamespace() + ":" + def.getName() + " documentation", anns != null && anns.getLength() > 0 ? ((XSAnnotation) anns.item(0)).getAnnotationString() : null); + jtMap.put(model.toJavaQualifiedTypeName(def, nsdMap, true), td); + } + + public void processType(XSComplexTypeDefinition def, List models, Map jtMap, Map nsdMap, ArrayList toAdd) throws Exception { + if (!nsdMap.containsKey(def.getNamespace())) { + Util.log("Namespace desc not found for: " + def); + } + String typeName = toJavaTypeName(def, nsdMap); + TypeDesc td = jtMap.get(model.toJavaQualifiedTypeName(def, nsdMap, false)); + if (td != null) { + if (td.fdMap.size() == 0) { + // Util.log("Reusing forward decl: "+typeName); + } else { + Util.logerr("merging: type names collision: " + typeName); + } + } else { + td = new TypeDesc(def.getName(), def.getNamespace(), typeName, TypeDesc.TypeEnum.CLASS); + } + XSObjectList anns = def.getAnnotations(); + td.documentation = parseAnnotationString("Type " + def.getNamespace() + ":" + def.getName() + " documentation", + anns != null && anns.getLength() > 0 ? ((XSAnnotation) anns.item(0)).getAnnotationString() : null); + TypeDesc tdBase = null; + if (checkComplexType(def.getBaseType())) { + XSComplexTypeDefinition base = (XSComplexTypeDefinition) def.getBaseType(); + String typeNameBase = toJavaTypeName(base, nsdMap); + if ((tdBase = jtMap.get(model.toJavaQualifiedTypeName(base, nsdMap, false))) == null) { + // logwarn("forward decl: "+et); + tdBase = new TypeDesc(base.getName(), base.getNamespace(), typeNameBase, TypeDesc.TypeEnum.CLASS); + jtMap.put(model.toJavaQualifiedTypeName(base, nsdMap, false), tdBase); + } + } + if (def.getSimpleType() != null || def.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_MIXED) { + FieldDesc fd = new FieldDesc(FieldDesc.SIMPLE, "value", "String", null, "null", true); + fd.realIndex = td.fdMap.size(); + td.fdMap.put(fd.name, fd); + } + XSObjectList attrs = def.getAttributeUses(); + for (int i = 0; i < attrs.getLength(); i++) { + XSAttributeUse au = (XSAttributeUse) attrs.item(i); + XSAttributeDeclaration ad = au.getAttrDeclaration(); + XSSimpleTypeDefinition atd = ad.getTypeDefinition(); + XSAnnotation ann = ad.getAnnotation(); + String documentation = parseAnnotationString("Attribute " + ad.getNamespace() + ":" + ad.getName() + " documentation", ann != null ? ann.getAnnotationString() : null); + // skip "ID" and "FIXED" + if ("ID".equals(atd.getName())) continue; + // "language", "dewey-versionType", "boolean" + if (ad.getConstraintType() == XSConstants.VC_FIXED) continue; + FieldDesc fd1 = new FieldDesc(FieldDesc.ATTR, Util.toJavaFieldName(ad.getName()), "String", null, "null", au.getRequired()); + fd1.tagName = ad.getName(); + fd1.documentation = documentation; + fd1.realIndex = td.fdMap.size(); + td.duplicates = Util.addToNameMap(td.fdMap, fd1, false) || td.duplicates; + if (checkEnumType(ad.getTypeDefinition())) { + XSTypeDefinition etRoot = ad.getTypeDefinition(); + if (etRoot.getAnonymous()) { + etRoot = makeTypeFromAnonymous(ad); + if (toAdd != null) toAdd.add((XSComplexTypeDefinition) etRoot); + } + fd1.type = toJavaTypeName(etRoot, nsdMap); + fd1.contentQualifiedName = model.toJavaQualifiedTypeName(etRoot, nsdMap, true); + // forward decl + if (jtMap.get(fd1.contentQualifiedName) == null) { + // logwarn("forward decl: "+et); + TypeDesc ftd = new TypeDesc(etRoot.getName(), etRoot.getNamespace(), fd1.type, TypeDesc.TypeEnum.ENUM); + jtMap.put(fd1.contentQualifiedName, ftd); +// // anonymous (simple type) enum +// if (ad.getTypeDefinition().getAnonymous()) { +// processEnumType(ad.getTypeDefinition(), jtMap, nsdMap); +// } + } + } else { + fd1.simpleTypesString = getSimpleTypesString(ad.getTypeDefinition()); + } + } + LinkedList plist = new LinkedList(); + if (def.getParticle() != null) { + plist.add(new PEntry(def.getParticle(), false)); + } + processParticles(def, plist, nsdMap, jtMap, td, models, toAdd, tdBase); + jtMap.put(model.toJavaQualifiedTypeName(def, nsdMap, false), td); + if (td.fdMap.size() == 1 && def.getSimpleType() != null) { + // calc type hierarchy for simple content + FieldDesc fd = td.fdMap.get("value"); + fd.simpleTypesString = getSimpleTypesString(def); + } + } + + private static String getSimpleTypesString(XSTypeDefinition et) { + StringBuffer typesHierarchy = new StringBuffer(); + while (et != null && !"anySimpleType".equals(et.getName()) && !"anyType".equals(et.getName())) { + typesHierarchy.append(et.getNamespace().substring(et.getNamespace().lastIndexOf("/") + 1)).append(":").append(et.getName()).append(";"); + if (et instanceof XSSimpleType) { + XSSimpleType simpleType = (XSSimpleType) et; + if (simpleType.getVariety() == XSSimpleTypeDefinition.VARIETY_LIST + || simpleType.getVariety() == XSSimpleTypeDefinition.VARIETY_UNION) { + XSObjectList list = simpleType.getMemberTypes(); + if (list.getLength() > 0) { + typesHierarchy.append("{"); + for (int i = 0; i < list.getLength(); i++) { + typesHierarchy.append(getSimpleTypesString((XSTypeDefinition) list.item(i))); + } + typesHierarchy.append("}"); + } + } + } + et = et.getBaseType(); + } + return typesHierarchy.toString(); + } + + private TypeDesc processGroup(XSModelGroup modelGroup, List models, Map jtMap, Map nsdMap) { + XSModelGroupDefinition def = null; + for (XSModel xsModel : models) { + XSNamedMap map = xsModel.getComponents(XSConstants.MODEL_GROUP_DEFINITION); + for (int i = 0; i < map.getLength(); i++) { + XSModelGroupDefinition mg = (XSModelGroupDefinition) map.item(i); + final XSModelGroup xsModelGroup = mg.getModelGroup(); + if (xsModelGroup == modelGroup || xsModelGroup.toString().equals(modelGroup.toString())) { + def = mg; + break; + } + } + } + if (def == null) return null; + if (!nsdMap.containsKey(def.getNamespace())) { + Util.log("Namespace desc not found for: " + def); + } + String typeName = toJavaTypeName(def, nsdMap); + final String typeQName = model.toJavaQualifiedTypeName(def, nsdMap, false); + TypeDesc td = jtMap.get(typeQName); + if (td != null) { + if (td.type == TypeDesc.TypeEnum.GROUP_INTERFACE) { + return td; + } else { + Util.logerr("type-group conflict: " + typeName); + return null; + } + } else { + td = new TypeDesc(def.getName(), def.getNamespace(), typeName, TypeDesc.TypeEnum.GROUP_INTERFACE); + } + + XSAnnotation ann = def.getAnnotation(); + td.documentation = parseAnnotationString("Type " + def.getNamespace() + ":" + def.getName() + " documentation", + ann == null ? null : ann.getAnnotationString()); + td.type = TypeDesc.TypeEnum.GROUP_INTERFACE; + LinkedList plist = new LinkedList(); + for (int i = 0; i < def.getModelGroup().getParticles().getLength(); i++) { + XSParticle p = (XSParticle) def.getModelGroup().getParticles().item(i); + plist.add(new PEntry(p, false)); + } + processParticles(def, plist, nsdMap, jtMap, td, models, null, null); + jtMap.put(typeQName, td); + return td; + } + + private void processParticles(XSObject def, LinkedList plist, Map nsdMap, Map jtMap, TypeDesc td, List models, ArrayList toAdd, TypeDesc baseClass) { + final boolean globalMerge = jtMap.containsKey(model.toJavaQualifiedTypeName(def, nsdMap, td.type == TypeDesc.TypeEnum.ENUM)); + final HashMap globalChoice = new HashMap(); + final ArrayList choiceList = new ArrayList(); + final ArrayList supers = new ArrayList(); + if (baseClass != null) { + supers.add(baseClass); + } + while (!plist.isEmpty()) { + final PEntry pentry = plist.removeFirst(); + final XSParticle p = pentry.p; + if (p.getTerm() instanceof XSElementDecl) { + final XSElementDecl el = (XSElementDecl) p.getTerm(); + if (el.getConstraintType() == XSConstants.VC_FIXED) continue; + XSTypeDefinition etRoot = el.getTypeDefinition(); + XSTypeDefinition et = etRoot; + XSAnnotation ann = el.getAnnotation(); + String documentation = parseAnnotationString("Element " + el.getNamespace() + ":" + el.getName() + " documentation", ann != null ? ann.getAnnotationString() : null); + final FieldDesc fd1 = new FieldDesc(FieldDesc.STR, Util.toJavaFieldName(el.getName()), et.getName(), null, "null", !pentry.many && p.getMinOccurs() > 0); + fd1.documentation = documentation; + fd1.tagName = el.getName(); + while (et.getBaseType() != null && !"anySimpleType".equals(et.getBaseType().getName()) && !"anyType".equals(et.getBaseType().getName())) { + et = et.getBaseType(); + } + if (checkEnumType(etRoot)) { + if (etRoot.getAnonymous()) { + etRoot = makeTypeFromAnonymous(el); + if (toAdd != null) toAdd.add((XSComplexTypeDefinition) etRoot); + } + fd1.type = toJavaTypeName(etRoot, nsdMap); + fd1.clType = FieldDesc.OBJ; + fd1.contentQualifiedName = model.toJavaQualifiedTypeName(etRoot, nsdMap, true); + // forward decl + if (!jtMap.containsKey(fd1.contentQualifiedName)) { + // logwarn("forward decl: "+et); + TypeDesc ftd = new TypeDesc(etRoot.getName(), etRoot.getNamespace(), fd1.type, TypeDesc.TypeEnum.ENUM); + jtMap.put(fd1.contentQualifiedName, ftd); + } + } else if (checkComplexType(etRoot)) { + if (etRoot.getAnonymous()) { + etRoot = makeTypeFromAnonymous(el); + if (toAdd != null) toAdd.add((XSComplexTypeDefinition) etRoot); + } + fd1.type = toJavaTypeName(etRoot, nsdMap); + fd1.clType = FieldDesc.OBJ; + fd1.contentQualifiedName = model.toJavaQualifiedTypeName(etRoot, nsdMap, false); + // forward decl + if (jtMap.get(fd1.contentQualifiedName) == null) { + //logwarn("forward decl: "+etRoot); + jtMap.put(fd1.contentQualifiedName, new TypeDesc(etRoot.getName(), etRoot.getNamespace(), fd1.type, TypeDesc.TypeEnum.CLASS)); + } + } else if (checkBooleanType(etRoot)) { + fd1.type = "boolean"; + fd1.clType = FieldDesc.BOOL; + } else { + if (etRoot instanceof XSComplexTypeDefinition) { + final XSComplexTypeDefinition ct = (XSComplexTypeDefinition) etRoot; + // XXX xerces2.7.1 wierd annotation inheritance bug fix + //ann = (XSAnnotation) (ct.getAnnotations()!=null && ct.getAnnotations().getLength()>0?ct.getAnnotations().item(0):null); + ann = (XSAnnotation) (ct.getAnnotations() != null && ct.getAnnotations().getLength() > 0 ? ct.getAnnotations().item(ct.getAnnotations().getLength() - 1) : null); + documentation = parseAnnotationString("Type " + ct.getNamespace() + ":" + ct.getName() + " documentation", ann != null ? ann.getAnnotationString() : null); + if (documentation != null) { + fd1.documentation = fd1.documentation != null ? fd1.documentation + "\n" + documentation : documentation; + } + } + fd1.simpleTypesString = getSimpleTypesString(etRoot); + + // "fully-qualified-classType", "jndi-nameType", "transaction-typeType" + // "java-identifierType", "pathType" + fd1.type = et.getName(); + if (fd1.type == null) { + fd1.type = "String"; + fd1.def = "null"; + fd1.clType = FieldDesc.STR; +// fd1.type = "boolean"; +// fd1.def = "false"; +// fd1.clType = FieldDesc.BOOL; + } else if (fd1.type.equals("string") || fd1.type.equals("anyURI")) { + fd1.type = "String"; + } else if (fd1.type.equals("boolean")) { + fd1.type = "String"; + } else if (fd1.type.equals("emptyType")) { + fd1.type = "boolean"; + fd1.def = "false"; + fd1.clType = FieldDesc.BOOL; + } else if (fd1.type.equals("decimal")) { + fd1.type = "String"; + fd1.def = "\"0.0\""; + } else if (fd1.type.equals("QName")) { + fd1.type = "String"; + } else if (fd1.type.equals("extensibleType")) { + fd1.type = "Object"; + } else { + if ("anySimpleType".equals(et.getBaseType().getName()) + || "anyType".equals(et.getBaseType().getName())) { + fd1.type = "String"; + fd1.def = "null"; + fd1.clType = FieldDesc.STR; + } else { + fd1.type = "boolean"; + fd1.def = "false"; + fd1.clType = FieldDesc.BOOL; + } + Util.logwarn("using '" + fd1.type + "' for unknown base type: " + et.getName() + " for " + el); + } + } + if ((pentry.many || p.getMaxOccursUnbounded() || p.getMaxOccurs() > 1) && fd1.clType != FieldDesc.BOOL) { + fd1.elementType = fd1.type; + fd1.elementName = fd1.name; + fd1.type = "List<" + fd1.elementType + ">"; + fd1.name = Util.pluralize(fd1.name); + fd1.def = "new ArrayList(0)"; + fd1.clType = -fd1.clType; + fd1.comment = "array of " + fd1.elementType; + } + fd1.realIndex = td.fdMap.size(); + boolean merge = globalMerge || globalChoice.containsKey(p) && globalChoice.values().contains(fd1.name); + td.duplicates = Util.addToNameMap(td.fdMap, fd1, merge) || td.duplicates; + globalChoice.put(p, fd1.name); + } else if (p.getTerm() instanceof XSModelGroup) { + boolean addToGlobalChoice = false; + boolean many = p.getMaxOccursUnbounded() || p.getMaxOccurs() > 1; + XSObjectList l = ((XSModelGroup) p.getTerm()).getParticles(); + if (!many) { + if (((XSModelGroup) p.getTerm()).getCompositor() == XSModelGroup.COMPOSITOR_CHOICE) { + addToGlobalChoice = true; + choiceList.add(l); + } else { + // generate group interface??? + XSModelGroup groupDef = (XSModelGroup) p.getTerm(); + TypeDesc gtd = processGroup(groupDef, models, jtMap, nsdMap); + if (gtd != null) supers.add(gtd); + } + } + if (globalChoice.containsKey(p)) { + addToGlobalChoice = true; + } + for (int i = 0; i < l.getLength(); i++) { + final PEntry o = new PEntry((XSParticle) l.item(i), many); + plist.add(o); + if (addToGlobalChoice && !globalChoice.containsKey(o.p)) { + globalChoice.put(o.p, null); + } + } + } + } + int i = 0; + for (Iterator it = td.fdMap.values().iterator(); it.hasNext(); i++) { + FieldDesc fd = it.next(); + fd.idx = i; + } + for (XSObjectList l : choiceList) { + final ArrayList clist = new ArrayList(); + final LinkedList elist = new LinkedList(); + for (i = 0; i < l.getLength(); i++) { + elist.add((XSParticle) l.item(i)); + } + while (!elist.isEmpty()) { + final XSParticle p = elist.removeFirst(); + if (p.getTerm() instanceof XSModelGroup) { + XSObjectList l2 = ((XSModelGroup) p.getTerm()).getParticles(); + for (int i2 = 0; i2 < l2.getLength(); i2++) { + elist.addFirst((XSParticle) l2.item(i2)); + } + } else if (p.getTerm() instanceof XSElementDecl) { + clist.add(p); + } + } + boolean choiceOpt = true; + FieldDesc[] choice = new FieldDesc[clist.size()]; + for (i = 0; i < choice.length; i++) { + XSParticle p = clist.get(i); + XSElementDecl el = (XSElementDecl) p.getTerm(); + String s = Util.toJavaFieldName(el.getName()); + if (p.getMaxOccursUnbounded() || p.getMaxOccurs() > 1) { + s = Util.pluralize(s); + } + FieldDesc fd = td.fdMap.get(s); + if (fd == null) { + fd = td.fdMap.get(Util.pluralize(s)); + if (fd == null) { + Util.logerr("uknown choice element: " + s); + } + } + choice[i] = fd; + choice[i].choice = choice; + if (fd.required) choiceOpt = false; + } + for (i = 0; i < choice.length; i++) { + choice[i].choiceOpt = choiceOpt; + } + } + td.supers = supers.toArray(new TypeDesc[supers.size()]); + } + public static String parseAnnotationString(String title, String str) { + if (str == null) return null; + int idx = str.indexOf(":documentation"); + if (idx == -1) idx = str.indexOf("", idx + 1); + if (idx == -1) return null; + int idx2 = str.indexOf(":documentation", idx + 1); + if (idx2 == -1) idx2 = str.indexOf(" -1) { + idx = str.indexOf("[", idx + 3); + idx2 = str.indexOf("]]>", idx + 1); + str = str.substring(idx + 1, idx2); + } + return "
\n

" + title + "

\n" + str + "\n
"; + } + + public String toJavaTypeName(XSObject xs, Map nsdMap) { + String name = xs.getName(); + if (name == null) { + if (xs instanceof TypeInfo) { + name = ((TypeInfo) xs).getTypeName(); + if (name != null && name.startsWith("#")) { + name = name.substring(1); + } + } + } + return model.toJavaTypeName(name, xs.getNamespace()); + } + + public static class PEntry { + public PEntry(XSParticle p, boolean many) { + this.p = p; + this.many = many; + } + + XSParticle p; + boolean many; + } + +} From 2a61c0ed56561ae4bf37c1d60758b3eb0b29ce09 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Mon, 28 Jun 2010 17:54:46 +0400 Subject: [PATCH 05/19] [by peter:] get quick doc info from the place-specific documentation provider; DomChildrenDescription<: PomTarget --- .../documentation/DocumentationManager.java | 2 +- .../navigation/CtrlMouseHandler.java | 6 ++-- .../psi/impl/PomTargetPsiElementImpl.java | 9 ++++++ .../xml/impl/DomChildDescriptionImpl.java | 19 +++++++++++++ .../xml/impl/dom/DomElementXmlDescriptor.java | 28 +++++++++++++++---- .../xml/reflect/DomChildrenDescription.java | 3 +- 6 files changed, 56 insertions(+), 11 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java index c762b8c32109..9f1ddcece42c 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/documentation/DocumentationManager.java @@ -753,7 +753,7 @@ public class DocumentationManager { } @NotNull - private static DocumentationProvider getProviderFromElement(@Nullable PsiElement element, @Nullable PsiElement originalElement) { + public static DocumentationProvider getProviderFromElement(@Nullable PsiElement element, @Nullable PsiElement originalElement) { if (element != null && !element.isValid()) { element = null; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java index da222b6d45ff..41c1532c96d1 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/navigation/CtrlMouseHandler.java @@ -223,8 +223,8 @@ public class CtrlMouseHandler extends AbstractProjectComponent { } @Nullable - private static String generateInfo(PsiElement element) { - final DocumentationProvider documentationProvider = DocumentationManager.getProviderFromElement(element); + private static String generateInfo(PsiElement element, PsiElement atPointer) { + final DocumentationProvider documentationProvider = DocumentationManager.getProviderFromElement(element, atPointer); String info = documentationProvider.getQuickNavigateInfo(element); if (info != null) { @@ -305,7 +305,7 @@ public class CtrlMouseHandler extends AbstractProjectComponent { @Nullable public String getInfo() { try { - return generateInfo(myTargetElement); + return generateInfo(myTargetElement, myElementAtPointer); } catch (IndexNotReadyException e) { showDumbModeNotification(myTargetElement.getProject()); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/PomTargetPsiElementImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/PomTargetPsiElementImpl.java index 085a802a8cf7..61102051d940 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/PomTargetPsiElementImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/PomTargetPsiElementImpl.java @@ -16,6 +16,7 @@ package com.intellij.psi.impl; import com.intellij.ide.IconProvider; +import com.intellij.lang.Language; import com.intellij.openapi.project.Project; import com.intellij.pom.*; import com.intellij.psi.PsiElement; @@ -164,6 +165,14 @@ public class PomTargetPsiElementImpl extends RenameableFakePsiElement implements return null; } + @NotNull + public Language getLanguage() { + if (myTarget instanceof PsiTarget) { + return ((PsiTarget)myTarget).getNavigationElement().getLanguage(); + } + return Language.ANY; + } + @NotNull @Override public Project getProject() { diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomChildDescriptionImpl.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomChildDescriptionImpl.java index 2a04bde8752b..957e8b8ea0b8 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomChildDescriptionImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomChildDescriptionImpl.java @@ -21,6 +21,25 @@ public abstract class DomChildDescriptionImpl extends AbstractDomChildDescriptio myTagName = tagName; } + public String getName() { + return myTagName.getLocalName(); + } + + public boolean isValid() { + return true; + } + + public void navigate(boolean requestFocus) { + } + + public boolean canNavigate() { + return false; + } + + public boolean canNavigateToSource() { + return false; + } + @NotNull public String getXmlElementName() { return myTagName.getLocalName(); diff --git a/xml/dom-impl/src/com/intellij/xml/impl/dom/DomElementXmlDescriptor.java b/xml/dom-impl/src/com/intellij/xml/impl/dom/DomElementXmlDescriptor.java index d16e6d3e4ad3..0cba5fe1b7a6 100644 --- a/xml/dom-impl/src/com/intellij/xml/impl/dom/DomElementXmlDescriptor.java +++ b/xml/dom-impl/src/com/intellij/xml/impl/dom/DomElementXmlDescriptor.java @@ -17,9 +17,9 @@ package com.intellij.xml.impl.dom; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.pom.references.PomService; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.impl.FakePsiElement; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlDocument; import com.intellij.psi.xml.XmlFile; @@ -204,11 +204,7 @@ public class DomElementXmlDescriptor implements XmlElementDescriptor { if (declaration != null) return declaration.getXmlElement(); - return new FakePsiElement() { - public PsiElement getParent() { - return DomUtil.getFile(mySomeElement); - } - }; + return PomService.convertToPsi(myManager.getProject(), myChildrenDescription); } @NonNls @@ -255,6 +251,25 @@ public class DomElementXmlDescriptor implements XmlElementDescriptor { myDomElement = domElement; } + public String getName() { + return getXmlElementName(); + } + + public boolean isValid() { + return true; + } + + public void navigate(boolean requestFocus) { + } + + public boolean canNavigate() { + return false; + } + + public boolean canNavigateToSource() { + return false; + } + @NotNull public XmlName getXmlName() { throw new UnsupportedOperationException("Method getXmlName not implemented in " + getClass()); @@ -304,4 +319,5 @@ public class DomElementXmlDescriptor implements XmlElementDescriptor { throw new UnsupportedOperationException("Method getAnnotation not implemented in " + getClass()); } } + } diff --git a/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomChildrenDescription.java b/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomChildrenDescription.java index e86e69ca3f7e..90639030baaa 100644 --- a/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomChildrenDescription.java +++ b/xml/dom-openapi/src/com/intellij/util/xml/reflect/DomChildrenDescription.java @@ -15,6 +15,7 @@ */ package com.intellij.util.xml.reflect; +import com.intellij.pom.PomNamedTarget; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomNameStrategy; import com.intellij.util.xml.XmlName; @@ -23,7 +24,7 @@ import org.jetbrains.annotations.NotNull; /** * @author peter */ -public interface DomChildrenDescription extends AbstractDomChildrenDescription { +public interface DomChildrenDescription extends AbstractDomChildrenDescription, PomNamedTarget { @NotNull XmlName getXmlName(); From 1423525d364bc6b22504e4e94d10a2bf43b839ca Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Mon, 28 Jun 2010 18:12:51 +0400 Subject: [PATCH 06/19] fix empty folder problem --- .../impl/config/IntentionActionMetaData.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/IntentionActionMetaData.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/IntentionActionMetaData.java index de250f02bc37..47d1ebd52076 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/IntentionActionMetaData.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/config/IntentionActionMetaData.java @@ -149,13 +149,23 @@ public final class IntentionActionMetaData { return urls.isEmpty() ? null : urls.toArray(new TextDescriptor[urls.size()]); } + @Nullable private static URL getIntentionDescriptionDirURL(ClassLoader aClassLoader, String intentionFolderName) { - final URL pageURL = aClassLoader.getResource(INTENTION_DESCRIPTION_FOLDER + "/" + intentionFolderName); + final URL pageURL = aClassLoader.getResource(INTENTION_DESCRIPTION_FOLDER + "/" + intentionFolderName+"/"+ DESCRIPTION_FILE_NAME); if (LOG.isDebugEnabled()) { LOG.debug("Path:"+"intentionDescriptions/" + intentionFolderName); LOG.debug("URL:"+pageURL); } - return pageURL; + if (pageURL != null) { + try { + final String url = pageURL.toExternalForm(); + return new URL(url.substring(0, url.lastIndexOf('/'))); + } + catch (MalformedURLException e) { + LOG.error(e); + } + } + return null; } private URL getDirURL() { From de3d22a478646094a0273209c90639472d04b2df Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 28 Jun 2010 14:44:38 +0400 Subject: [PATCH 07/19] move tests --- .../gant/{completion => }/GantReferenceCompletionTest.groovy | 2 +- .../gant/{completion => }/StandaloneGantCompletionTest.groovy | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename plugins/groovy/test/org/jetbrains/plugins/groovy/gant/{completion => }/GantReferenceCompletionTest.groovy (98%) rename plugins/groovy/test/org/jetbrains/plugins/groovy/gant/{completion => }/StandaloneGantCompletionTest.groovy (96%) diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/completion/GantReferenceCompletionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/GantReferenceCompletionTest.groovy similarity index 98% rename from plugins/groovy/test/org/jetbrains/plugins/groovy/gant/completion/GantReferenceCompletionTest.groovy rename to plugins/groovy/test/org/jetbrains/plugins/groovy/gant/GantReferenceCompletionTest.groovy index 59c3f88f87f9..e05bdbc4687c 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/completion/GantReferenceCompletionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/GantReferenceCompletionTest.groovy @@ -1,4 +1,4 @@ -package org.jetbrains.plugins.groovy.gant.completion; +package org.jetbrains.plugins.groovy.gant; import com.intellij.openapi.module.Module diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/completion/StandaloneGantCompletionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantCompletionTest.groovy similarity index 96% rename from plugins/groovy/test/org/jetbrains/plugins/groovy/gant/completion/StandaloneGantCompletionTest.groovy rename to plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantCompletionTest.groovy index e551897ab51e..0839f1a98b78 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/completion/StandaloneGantCompletionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantCompletionTest.groovy @@ -1,4 +1,4 @@ -package org.jetbrains.plugins.groovy.gant.completion; +package org.jetbrains.plugins.groovy.gant; import com.intellij.openapi.util.io.FileUtil From 233751abb574da5f81f11d3619622674a2aae474 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 28 Jun 2010 15:31:22 +0400 Subject: [PATCH 08/19] don't yellow-highlight gant task calls without attributes --- .../plugins/groovy/gant/AntBuilderMethod.java | 11 ++++++++-- .../typedef/members/GrBuilderMethod.java | 10 +++++++++ .../groovy/lang/psi/impl/GrMapType.java | 6 ++++- .../plugins/groovy/lang/psi/util/PsiUtil.java | 22 ++++++++++++++----- ...nTest.groovy => StandaloneGantTest.groovy} | 14 +++++++++++- 5 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrBuilderMethod.java rename plugins/groovy/test/org/jetbrains/plugins/groovy/gant/{StandaloneGantCompletionTest.groovy => StandaloneGantTest.groovy} (77%) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/AntBuilderMethod.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/AntBuilderMethod.java index cca70bd491b8..c069b435da28 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/AntBuilderMethod.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/AntBuilderMethod.java @@ -9,6 +9,8 @@ import com.intellij.psi.scope.PsiScopeProcessor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrBuilderMethod; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrMapType; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; @@ -17,7 +19,7 @@ import java.util.Collections; /** * @author peter */ -class AntBuilderMethod extends LightMethodBuilder { +class AntBuilderMethod extends LightMethodBuilder implements GrBuilderMethod { private final PsiFile myPlace; @Nullable private final Class myAntClass; @@ -26,7 +28,7 @@ class AntBuilderMethod extends LightMethodBuilder { myPlace = place; myAntClass = antClass; setModifiers(PsiModifier.PUBLIC); - addParameter("args", CommonClassNames.JAVA_UTIL_MAP); + addParameter("args", new GrMapType(place.getResolveScope())); setBaseIcon(GantIcons.ANT_TASK); addParameter(new GrLightParameter("body", closureType, this).setOptional(true)); } @@ -59,4 +61,9 @@ class AntBuilderMethod extends LightMethodBuilder { } return true; } + + @Override + public boolean hasObligatoryNamedArguments() { + return false; + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrBuilderMethod.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrBuilderMethod.java new file mode 100644 index 000000000000..addc19a24f80 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/typedef/members/GrBuilderMethod.java @@ -0,0 +1,10 @@ +package org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members; + +/** + * @author peter + */ +public interface GrBuilderMethod { + + boolean hasObligatoryNamedArguments(); + +} \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java index 102e1d413ef3..7f0c4391bbf4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrMapType.java @@ -42,6 +42,10 @@ public class GrMapType extends GrLiteralClassType { private final String myJavaClassName; + public GrMapType(GlobalSearchScope scope) { + this(JavaPsiFacade.getInstance(scope.getProject()), scope, Collections.emptyMap(), Collections.>emptyList(), LanguageLevel.JDK_1_5); + } + public GrMapType(JavaPsiFacade facade, GlobalSearchScope scope, Map stringEntries, @@ -144,7 +148,7 @@ public class GrMapType extends GrLiteralClassType { } public boolean isAssignableFrom(@NotNull PsiType type) { - return false; + return type instanceof GrMapType || myFacade.getElementFactory().createTypeFromText(getJavaClassName(), null).isAssignableFrom(type); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index f02284db37f9..f5495c2cad2c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java @@ -58,14 +58,12 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrM import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrPropertySelection; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstant; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMember; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.*; import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType; +import org.jetbrains.plugins.groovy.lang.psi.impl.GrMapType; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.JavaIdentifier; @@ -136,7 +134,19 @@ public class PsiUtil { return InheritanceUtil.isInheritor(argumentTypes[0], CommonClassNames.JAVA_UTIL_MAP); } LOG.assertTrue(signature != null); - return GrClosureSignatureUtil.isSignatureApplicable(signature, argumentTypes, place); + if (GrClosureSignatureUtil.isSignatureApplicable(signature, argumentTypes, place)) { + return true; + } + + if (method instanceof GrBuilderMethod && + !((GrBuilderMethod)method).hasObligatoryNamedArguments()) { + final PsiParameter[] parameters = method.getParameterList().getParameters(); + if (parameters.length > 0 && parameters[0].getType() instanceof GrMapType && + (argumentTypes.length == 0 || !(argumentTypes[0] instanceof GrMapType))) { + return GrClosureSignatureUtil.isSignatureApplicable(signature.curry(1), argumentTypes, place); + } + } + return false; } public static boolean isApplicable(@Nullable PsiType[] argumentTypes, GrClosureType type, GroovyPsiElement context) { @@ -147,7 +157,7 @@ public class PsiUtil { } public static PsiClassType createMapType(PsiManager manager, GlobalSearchScope scope) { - return JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createTypeByFQClassName(CommonClassNames.JAVA_UTIL_MAP, scope); + return new GrMapType(scope); } @Nullable diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantCompletionTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantTest.groovy similarity index 77% rename from plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantCompletionTest.groovy rename to plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantTest.groovy index 0839f1a98b78..6a702b8b0287 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantCompletionTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantTest.groovy @@ -10,7 +10,7 @@ import org.jetbrains.plugins.groovy.util.TestUtils /** * @author peter */ -public class StandaloneGantCompletionTest extends LightCodeInsightFixtureTestCase { +public class StandaloneGantTest extends LightCodeInsightFixtureTestCase { @Override protected String getBasePath() { @@ -47,6 +47,18 @@ target(aaa: "") { checkVariants "ant.pattt", "patternset" } + public void testOptionalArgumentsHighlighting() throws Exception { + myFixture.configureByText "a.gant", """ + ant.java(classname: "com.intellij.util.io.zip.ReorderJarsMain", fork: "true") { + arg(value: "aaa") + classpath { + pathelement(location: "sss") + } + } +""" + myFixture.checkHighlighting(true, false, false) + } + static final def GANT_JARS = ["gant.jar", "ant.jar", "ant-junit.jar", "ant-launcher.jar", "commons.jar"] } From c98ac24a44e7a20fa0f101401107a80c9f968615 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 28 Jun 2010 17:45:50 +0400 Subject: [PATCH 09/19] deeply nested gant elements completion --- .../plugins/groovy/gant/AntBuilderMethod.java | 3 ++- .../plugins/groovy/gant/StandaloneGantTest.groovy | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/AntBuilderMethod.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/AntBuilderMethod.java index c069b435da28..27857d70ffde 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/AntBuilderMethod.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/AntBuilderMethod.java @@ -49,10 +49,11 @@ class AntBuilderMethod extends LightMethodBuilder implements GrBuilderMethod { final AntIntrospector introspector = AntDomExtender.getIntrospector(myAntClass); if (introspector != null) { final NameHint hint = processor.getHint(NameHint.KEY); + final PsiType closureType = getParameterList().getParameters()[1].getType(); for (String name : Collections.list(introspector.getNestedElements())) { if (hint == null || hint.getName(ResolveState.initial()).equals(name)) { - final AntBuilderMethod method = new AntBuilderMethod(myPlace, name, getParameterList().getParameters()[1].getType(), null); + final AntBuilderMethod method = new AntBuilderMethod(myPlace, name, closureType, introspector.getElementType(name)); if (!ResolveUtil.processElement(processor, method)) { return false; } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantTest.groovy index 6a702b8b0287..e954a8a97a9a 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/gant/StandaloneGantTest.groovy @@ -59,6 +59,17 @@ target(aaa: "") { myFixture.checkHighlighting(true, false, false) } + public void testPathElement() throws Exception { + checkVariants """ + ant.java(classname: "com.intellij.util.io.zip.ReorderJarsMain", fork: "true") { + arg(value: "aaa") + classpath { + pathelement(location: "sss") + } + } +""", "pathelement" + } + static final def GANT_JARS = ["gant.jar", "ant.jar", "ant-junit.jar", "ant-launcher.jar", "commons.jar"] } From 0eeb73e4c9340a15ffc3ccf64945073e8e80fca0 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 28 Jun 2010 18:05:13 +0400 Subject: [PATCH 10/19] gant plugin should depend on ant --- plugins/groovy/src/META-INF/plugin.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index b18f7fd1fc1e..05f396c29432 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -25,6 +25,7 @@ com.intellij.copyright org.intellij.intelliLang + AntSupport From 565d121b2b3bac03fb6d6fbb3b0e78be29648188 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 28 Jun 2010 18:39:13 +0400 Subject: [PATCH 11/19] cache super types used in groovy resolve --- .../PatternEditorContextMembersProvider.java | 4 +- .../dynamic/DynamicMembersContributor.java | 6 +- .../groovy/gant/GantMemberContributor.java | 6 +- .../lang/psi/impl/GdkMembersContributor.java | 6 +- .../GrConstructorInvocationImpl.java | 2 +- .../arguments/GrArgumentLabelImpl.java | 2 +- .../blocks/GrClosableBlockImpl.java | 2 +- .../CompleteReferenceExpression.java | 4 +- .../GrReferenceExpressionImpl.java | 4 +- .../statements/expressions/TypesUtil.java | 2 +- .../plugins/groovy/lang/psi/util/PsiUtil.java | 4 +- .../resolve/NonCodeMembersContributor.java | 11 ++- .../groovy/lang/resolve/ResolveUtil.java | 97 +++++++++++-------- .../noncode/ImmutableAnnotationProcessor.java | 4 +- 14 files changed, 79 insertions(+), 75 deletions(-) diff --git a/plugins/groovy/src/org/intellij/plugins/intelliLang/inject/groovy/PatternEditorContextMembersProvider.java b/plugins/groovy/src/org/intellij/plugins/intelliLang/inject/groovy/PatternEditorContextMembersProvider.java index efbf5346f3ca..22a30647323b 100644 --- a/plugins/groovy/src/org/intellij/plugins/intelliLang/inject/groovy/PatternEditorContextMembersProvider.java +++ b/plugins/groovy/src/org/intellij/plugins/intelliLang/inject/groovy/PatternEditorContextMembersProvider.java @@ -22,7 +22,6 @@ import com.intellij.openapi.util.UserDataHolderEx; import com.intellij.patterns.compiler.PatternCompilerImpl; import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; -import com.intellij.util.ProcessingContext; import org.intellij.plugins.intelliLang.inject.InjectorUtils; import org.intellij.plugins.intelliLang.inject.config.BaseInjection; import org.jetbrains.annotations.NotNull; @@ -41,8 +40,7 @@ public class PatternEditorContextMembersProvider extends NonCodeMembersContribut public void processDynamicElements(@NotNull PsiType qualifierType, PsiScopeProcessor processor, PsiElement place, - ResolveState state, - ProcessingContext ctx) { + ResolveState state) { final PsiFile file = place.getContainingFile().getOriginalFile(); final BaseInjection injection = file.getUserData(BaseInjection.INJECTION_KEY); if (injection == null) return; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/dynamic/DynamicMembersContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/dynamic/DynamicMembersContributor.java index 2f9fdae91894..8ed517620788 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/dynamic/DynamicMembersContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/dynamic/DynamicMembersContributor.java @@ -2,7 +2,6 @@ package org.jetbrains.plugins.groovy.annotator.intentions.dynamic; import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; -import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; @@ -15,10 +14,9 @@ public class DynamicMembersContributor extends NonCodeMembersContributor { public void processDynamicElements(@NotNull PsiType qualifierType, PsiScopeProcessor processor, PsiElement place, - ResolveState state, - ProcessingContext ctx) { + ResolveState state) { final DynamicManager manager = DynamicManager.getInstance(place.getProject()); - for (String qName : ResolveUtil.getAllSuperTypes(qualifierType, place, ctx).keySet()) { + for (String qName : ResolveUtil.getAllSuperTypes(qualifierType, place).keySet()) { for (PsiMethod method : manager.getMethods(qName)) { if (!ResolveUtil.processElement(processor, method)) return; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/GantMemberContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/GantMemberContributor.java index ee19342d1520..f73772850609 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/GantMemberContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/GantMemberContributor.java @@ -20,7 +20,6 @@ import com.intellij.psi.impl.light.LightMethodBuilder; import com.intellij.psi.impl.light.LightVariableBuilder; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel; @@ -38,9 +37,8 @@ public class GantMemberContributor extends NonCodeMembersContributor { public void processDynamicElements(@NotNull PsiType qualifierType, PsiScopeProcessor processor, PsiElement place, - ResolveState state, - ProcessingContext ctx) { - if (ResolveUtil.isInheritor(qualifierType, "groovy.util.AntBuilder", place, ctx)) { + ResolveState state) { + if (ResolveUtil.isInheritor(qualifierType, "groovy.util.AntBuilder", place)) { processAntTasks(processor, place); return; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GdkMembersContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GdkMembersContributor.java index 68866ec26627..ea008352555e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GdkMembersContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GdkMembersContributor.java @@ -2,7 +2,6 @@ package org.jetbrains.plugins.groovy.lang.psi.impl; import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; -import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; @@ -15,10 +14,9 @@ public class GdkMembersContributor extends NonCodeMembersContributor { public void processDynamicElements(@NotNull PsiType qualifierType, PsiScopeProcessor processor, PsiElement place, - ResolveState state, - ProcessingContext ctx) { + ResolveState state) { final GroovyPsiManager manager = GroovyPsiManager.getInstance(place.getProject()); - for (String qName : ResolveUtil.getAllSuperTypes(qualifierType, place, ctx).keySet()) { + for (String qName : ResolveUtil.getAllSuperTypes(qualifierType, place).keySet()) { for (PsiMethod defaultMethod : manager.getDefaultMethods(qName)) { if (!ResolveUtil.processElement(processor, defaultMethod)) return; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java index 49d469391899..f8460706203f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java @@ -106,7 +106,7 @@ public class GrConstructorInvocationImpl extends GroovyPsiElementImpl implements for (NonCodeMembersProcessor membersProcessor : NonCodeMembersProcessor.EP_NAME.getExtensions()) { if (!membersProcessor.processNonCodeMembers(thisType, processor, this, true)) break; } - NonCodeMembersContributor.processDynamicElements(thisType, processor, this, ResolveState.initial()); + NonCodeMembersContributor.runContributors(thisType, processor, this, ResolveState.initial()); return processor.getCandidates(); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java index 7bdae5a3ae8b..bba1e3457a9f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java @@ -138,7 +138,7 @@ public class GrArgumentLabelImpl extends GroovyPsiElementImpl implements GrArgum if (field != null) return field; final PropertyResolverProcessor processor = new PropertyResolverProcessor(propName, this); ResolveUtil - .processNonCodeMethods(JavaPsiFacade.getElementFactory(getProject()).createType(clazz), processor, getProject(), this, false); + .processNonCodeMethods(JavaPsiFacade.getElementFactory(getProject()).createType(clazz), processor, this, false); final GroovyResolveResult[] candidates = processor.getCandidates(); if (candidates.length == 0) return null; return candidates[0].getElement(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java index c00bff44a108..7a36dd0e7e60 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java @@ -87,7 +87,7 @@ public class GrClosableBlockImpl extends GrBlockImpl implements GrClosableBlock // Process non-code in closures PsiType clType = JavaPsiFacade.getInstance(getProject()).getElementFactory().createType(closureClass, PsiSubstitutor.EMPTY); - if (!ResolveUtil.processNonCodeMethods(clType, processor, getProject(), place, true)) return false; + if (!ResolveUtil.processNonCodeMethods(clType, processor, place, true)) return false; } return true; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java index 4d2206c1ca3c..597ae379e12b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java @@ -284,7 +284,7 @@ public class CompleteReferenceExpression { javaLangClass.processDeclarations(processor, ResolveState.initial(), null, refExpr); PsiType javaLangClassType = JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory().createType(javaLangClass, substitutor); - ResolveUtil.processNonCodeMethods(javaLangClassType, processor, refExpr.getProject(), refExpr, true); + ResolveUtil.processNonCodeMethods(javaLangClassType, processor, refExpr, true); } } } @@ -352,6 +352,6 @@ public class CompleteReferenceExpression { return; } - ResolveUtil.processNonCodeMethods(qualifierType, processor, project, refExpr, true); + ResolveUtil.processNonCodeMethods(qualifierType, processor, refExpr, true); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java index 01d1dbd25144..06ceb4c3ae75 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java @@ -624,7 +624,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements } if (!javaLangClass.processDeclarations(processor, state, null, refExpr)) return; PsiType javaLangClassType = JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory().createType(javaLangClass, substitutor); - ResolveUtil.processNonCodeMethods(javaLangClassType, processor, refExpr.getProject(), refExpr, false); + ResolveUtil.processNonCodeMethods(javaLangClassType, processor, refExpr, false); } } } @@ -653,7 +653,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements return; } - ResolveUtil.processNonCodeMethods(qualifierType, processor, project, refExpr, false); + ResolveUtil.processNonCodeMethods(qualifierType, processor, refExpr, false); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java index 1a862014ea1b..86c9cb6953bf 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java @@ -134,7 +134,7 @@ public class TypesUtil { } } - ResolveUtil.processNonCodeMethods(thisType, processor, place.getProject(), place, false); + ResolveUtil.processNonCodeMethods(thisType, processor, place, false); return processor.getCandidates(); } return GroovyResolveResult.EMPTY_ARRAY; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index f5495c2cad2c..00e34a6b0e07 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java @@ -641,7 +641,7 @@ public class PsiUtil { qClass.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, PsiSubstitutor.EMPTY), null, expr); } - ResolveUtil.processNonCodeMethods(qualifierType, processor, expr.getProject(), expr, false); + ResolveUtil.processNonCodeMethods(qualifierType, processor, expr, false); final GroovyResolveResult[] candidates = processor.getCandidates(); PsiType type = null; if (candidates.length == 1) { @@ -838,7 +838,7 @@ public class PsiUtil { for (NonCodeMembersProcessor membersProcessor : NonCodeMembersProcessor.EP_NAME.getExtensions()) { if (!membersProcessor.processNonCodeMembers(thisType, processor, place, true)) break; } - NonCodeMembersContributor.processDynamicElements(thisType, processor, place, ResolveState.initial()); + NonCodeMembersContributor.runContributors(thisType, processor, place, ResolveState.initial()); constructorResults.addAll(Arrays.asList(processor.getCandidates())); if (!toBreak) break; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersContributor.java index f35a41b3f358..69c72870a868 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersContributor.java @@ -23,7 +23,6 @@ import com.intellij.psi.PsiType; import com.intellij.psi.ResolveState; import com.intellij.psi.scope.DelegatingScopeProcessor; import com.intellij.psi.scope.PsiScopeProcessor; -import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; /** @@ -32,13 +31,15 @@ import org.jetbrains.annotations.NotNull; public abstract class NonCodeMembersContributor { private static final ExtensionPointName EP_NAME = ExtensionPointName.create("org.intellij.groovy.membersContributor"); - public abstract void processDynamicElements(@NotNull PsiType qualifierType, PsiScopeProcessor processor, PsiElement place, ResolveState state, ProcessingContext ctx); + public abstract void processDynamicElements(@NotNull PsiType qualifierType, + PsiScopeProcessor processor, + PsiElement place, + ResolveState state); - public static boolean processDynamicElements(@NotNull final PsiType qualifierType, + public static boolean runContributors(@NotNull final PsiType qualifierType, PsiScopeProcessor processor, final PsiElement place, final ResolveState state) { - final ProcessingContext ctx = new ProcessingContext(); final Ref result = Ref.create(true); final PsiScopeProcessor wrapper = new DelegatingScopeProcessor(processor) { @Override @@ -52,7 +53,7 @@ public abstract class NonCodeMembersContributor { } }; for (final NonCodeMembersContributor contributor : EP_NAME.getExtensions()) { - contributor.processDynamicElements(qualifierType, wrapper, place, state, ctx); + contributor.processDynamicElements(qualifierType, wrapper, place, state); if (!result.get()) { return false; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java index fad55bef5900..3b592ea7c8d3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java @@ -17,6 +17,7 @@ package org.jetbrains.plugins.groovy.lang.resolve; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.psi.*; @@ -24,11 +25,11 @@ import com.intellij.psi.scope.JavaScopeProcessorEvent; import com.intellij.psi.scope.NameHint; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.MethodSignature; -import com.intellij.psi.util.MethodSignatureUtil; -import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.util.ProcessingContext; +import com.intellij.psi.util.*; +import com.intellij.util.containers.ConcurrentFactoryMap; +import com.intellij.util.containers.ConcurrentHashMap; import com.intellij.util.containers.FactoryMap; +import gnu.trove.TObjectHashingStrategy; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.dsl.GroovyDslFileIndex; @@ -60,7 +61,19 @@ import java.util.*; */ @SuppressWarnings({"StringBufferReplaceableByString"}) public class ResolveUtil { - public static final PsiScopeProcessor.Event DECLARATION_SCOPE_PASSED = new PsiScopeProcessor.Event() { + public static final PsiScopeProcessor.Event DECLARATION_SCOPE_PASSED = new PsiScopeProcessor.Event() {}; + private static final Key>>> SUPER_TYPES = Key.create("SUPER_TYPES"); + + private static final TObjectHashingStrategy RAW_TYPE_HASHING_STRATEGY = new TObjectHashingStrategy() { + @Override + public int computeHashCode(PsiType object) { + return rawCanonicalText(object).hashCode(); + } + + @Override + public boolean equals(PsiType o1, PsiType o2) { + return rawCanonicalText(o1).equals(rawCanonicalText(o2)); + } }; private ResolveUtil() { @@ -77,12 +90,12 @@ public class ResolveUtil { if (!run.processDeclarations(processor, ResolveState.initial(), lastParent, place)) return false; if (processNonCodeMethods) { if (run instanceof GrTypeDefinition) { - processNonCodeMethods(factory.createType(((GrTypeDefinition)run)), processor, project, place, false); + processNonCodeMethods(factory.createType(((GrTypeDefinition)run)), processor, place, false); } else if ((run instanceof GroovyFileBase) && ((GroovyFileBase)run).isScript()) { final PsiClass psiClass = ((GroovyFileBase)run).getScriptClass(); if (psiClass != null) { - processNonCodeMethods(factory.createType(psiClass), processor, project, place, false); + processNonCodeMethods(factory.createType(psiClass), processor, place, false); } } } @@ -121,16 +134,9 @@ public class ResolveUtil { public static boolean processNonCodeMethods(PsiType type, PsiScopeProcessor processor, - Project project, PsiElement place, boolean forCompletion) { - String qName = rawCanonicalText(type); - - if (qName == null) { - return true; - } - - if (!NonCodeMembersContributor.processDynamicElements(type, processor, place, ResolveState.initial())) { + if (!NonCodeMembersContributor.runContributors(type, processor, place, ResolveState.initial())) { return false; } @@ -141,8 +147,7 @@ public class ResolveUtil { } } - final HashMap visited = getSuperTypes(type, project); - for (PsiType psiType : visited.values()) { + for (PsiType psiType : getAllSuperTypes(type, place).values()) { for (NonCodeMembersProcessor membersProcessor : NonCodeMembersProcessor.EP_NAME.getExtensions()) { if (!membersProcessor.processNonCodeMembers(psiType, processor, place, forCompletion)) return false; } @@ -152,51 +157,59 @@ public class ResolveUtil { private static HashMap getSuperTypes(PsiType type, Project project) { final HashMap visited = new HashMap(); - collectSuperTypes(type, visited, project); + collectSuperTypes(type, visited); if (type instanceof PsiArrayType) { final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); - collectSuperTypes(factory.createTypeFromText(CommonClassNames.JAVA_LANG_COMPARABLE, null), visited, project); - collectSuperTypes(factory.createTypeFromText(CommonClassNames.JAVA_IO_SERIALIZABLE, null), visited, project); + collectSuperTypes(factory.createTypeFromText(CommonClassNames.JAVA_LANG_COMPARABLE, null), visited); + collectSuperTypes(factory.createTypeFromText(CommonClassNames.JAVA_IO_SERIALIZABLE, null), visited); } return visited; } - private static void collectSuperTypes(PsiType type, Map visited, Project project) { + private static void collectSuperTypes(PsiType type, Map visited) { String qName = rawCanonicalText(type); - if (qName == null || visited.put(qName, type) != null) { + if (visited.put(qName, type) != null) { return; } for (PsiType superType : type.getSuperTypes()) { - collectSuperTypes(TypeConversionUtil.erasure(superType), visited, project); + collectSuperTypes(TypeConversionUtil.erasure(superType), visited); } } - private static final Key>> SUPER_TYPES = Key.create("SUPER_TYPES"); - public static Map getAllSuperTypes(PsiType base, final PsiElement place, ProcessingContext ctx) { - FactoryMap> classes = ctx.get(SUPER_TYPES); - if (classes == null) { - ctx.put(SUPER_TYPES, classes = new FactoryMap>() { - @Override - protected Map create(PsiType key) { - return getSuperTypes(key, place.getProject()); - } - }); - } - return classes.get(base); + public static Map getAllSuperTypes(PsiType base, final PsiElement place) { + final Project project = place.getProject(); + return CachedValuesManager.getManager(project).getCachedValue(project, SUPER_TYPES, new CachedValueProvider>>() { + @Override + public Result>> compute() { + final FactoryMap> map = new ConcurrentFactoryMap>() { + + @Override + protected Map> createMap() { + return new ConcurrentHashMap>(RAW_TYPE_HASHING_STRATEGY); + } + + @Override + protected Map create(PsiType key) { + return getSuperTypes(key, project); + } + }; + return Result.create(map, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project)); + } + }, false).get(base); } - public static boolean isInheritor(PsiType type, @NotNull String baseClass, PsiElement place, ProcessingContext ctx) { - return getAllSuperTypes(type, place, ctx).keySet().contains(baseClass); + public static boolean isInheritor(PsiType type, @NotNull String baseClass, PsiElement place) { + return getAllSuperTypes(type, place).keySet().contains(baseClass); } - @Nullable - private static String rawCanonicalText(PsiType type) { + @NotNull + private static String rawCanonicalText(@NotNull PsiType type) { final String result = type.getCanonicalText(); - if (result == null) return null; + if (result == null) return ""; final int i = result.indexOf('<'); if (i > 0) return result.substring(0, i); return result; @@ -446,7 +459,7 @@ public class ResolveUtil { } } else if (parent instanceof GrApplicationStatement) { - final GrExpression funExpr = ((GrApplicationStatement) parent).getFunExpression(); + final GrExpression funExpr = ((GrApplicationStatement) parent).getInvokedExpression(); if (funExpr instanceof GrReferenceExpression) { variants = ((GrReferenceExpression) funExpr).getSameNameVariants(); } @@ -459,7 +472,7 @@ public class ResolveUtil { public static GroovyResolveResult[] getConstructorResolveResult(PsiMethod[] constructors, PsiElement place) { GroovyResolveResult[] variants = new GroovyResolveResult[constructors.length]; for (int i = 0; i < constructors.length; i++) { - final boolean isAccessible = com.intellij.psi.util.PsiUtil.isAccessible(constructors[i], place, null); + final boolean isAccessible = PsiUtil.isAccessible(constructors[i], place, null); variants[i] = new GroovyResolveResultImpl(constructors[i], isAccessible); } return variants; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/ImmutableAnnotationProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/ImmutableAnnotationProcessor.java index a99ea6cf7304..1de4da194e97 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/ImmutableAnnotationProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/noncode/ImmutableAnnotationProcessor.java @@ -19,7 +19,6 @@ package org.jetbrains.plugins.groovy.lang.resolve.noncode; import com.intellij.psi.*; import com.intellij.psi.impl.light.LightMethodBuilder; import com.intellij.psi.scope.PsiScopeProcessor; -import com.intellij.util.ProcessingContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.annotator.inspections.GroovyImmutableAnnotationInspection; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; @@ -34,8 +33,7 @@ public class ImmutableAnnotationProcessor extends NonCodeMembersContributor { public void processDynamicElements(@NotNull PsiType qualifierType, PsiScopeProcessor processor, PsiElement place, - ResolveState state, - ProcessingContext ctx) { + ResolveState state) { if (!(qualifierType instanceof PsiClassType)) return; PsiClass psiClass = ((PsiClassType)qualifierType).resolve(); From 37d6f83aefe667c932f2d4bd6ce4c67a832acdc6 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 28 Jun 2010 18:51:49 +0400 Subject: [PATCH 12/19] wrap directory creation into a command --- .../plugins/groovy/compiler/GroovyCompilerTestCase.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java index 44d8358c994b..a3e543213df1 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/compiler/GroovyCompilerTestCase.java @@ -64,7 +64,11 @@ public abstract class GroovyCompilerTestCase extends JavaCodeInsightFixtureTestC getProject().getComponent(GroovyCompilerLoader.class).projectOpened(); CompilerManagerImpl.testSetup(); - CompilerProjectExtension.getInstance(getProject()).setCompilerOutputUrl(myMainOutput.findOrCreateDir("out").getUrl()); + new WriteCommandAction(getProject()) { + protected void run(Result result) throws Throwable { + CompilerProjectExtension.getInstance(getProject()).setCompilerOutputUrl(myMainOutput.findOrCreateDir("out").getUrl()); + } + }.execute(); addGroovyLibrary(myModule, getName().contains("1_7")); From 11d811d3bed82145568afa9f2b4e1a1b983de5cc Mon Sep 17 00:00:00 2001 From: anna Date: Thu, 24 Jun 2010 17:24:39 +0400 Subject: [PATCH 13/19] replace all inheritors with static imports --- ...placeImplementsWithStaticImportAction.java | 162 +++++++++++++++--- .../after6.java | 2 +- .../after7.java | 18 ++ .../after8.java | 19 ++ .../before7.java | 15 ++ .../before8.java | 15 ++ 6 files changed, 204 insertions(+), 27 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after7.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after8.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before7.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before8.java diff --git a/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java b/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java index 18b3deee91b0..680ed0a1afb7 100644 --- a/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java +++ b/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java @@ -17,25 +17,29 @@ package com.intellij.codeInspection.actions; import com.intellij.codeInsight.CodeInsightUtilBase; import com.intellij.codeInsight.TargetElementUtilBase; -import com.intellij.codeInsight.actions.OptimizeImportsProcessor; -import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.Result; +import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.searches.DirectClassInheritorsSearch; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; -public class ReplaceImplementsWithStaticImportAction implements IntentionAction { +public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseIntentionAction { private static final Logger LOG = Logger.getInstance("#" + ReplaceImplementsWithStaticImportAction.class.getName()); @NotNull @@ -48,8 +52,14 @@ public class ReplaceImplementsWithStaticImportAction implements IntentionAction return getText(); } - public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - if (!(file instanceof PsiJavaFile)) return false; + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { + if (!(element.getContainingFile() instanceof PsiJavaFile)) return false; + if (element instanceof PsiIdentifier) { + final PsiElement parent = element.getParent(); + if (parent instanceof PsiClass) { + return isEmptyClass(project, (PsiClass)parent) && DirectClassInheritorsSearch.search((PsiClass)parent).findFirst() != null; + } + } final PsiReference psiReference = TargetElementUtilBase.findReference(editor); if (psiReference == null) return false; @@ -64,11 +74,13 @@ public class ReplaceImplementsWithStaticImportAction implements IntentionAction final PsiElement target = psiReference.resolve(); if (target == null || !(target instanceof PsiClass)) return false; - PsiClass targetClass = (PsiClass)target; + return isEmptyClass(project, (PsiClass)target); + } + + private static boolean isEmptyClass(Project project, PsiClass targetClass) { if (!targetClass.isInterface()) { return false; } - final PsiReferenceList extendsList = targetClass.getExtendsList(); LOG.assertTrue(extendsList != null); if (extendsList.getReferencedTypes().length > 0) { @@ -78,7 +90,8 @@ public class ReplaceImplementsWithStaticImportAction implements IntentionAction if (objectClass == null) return false; methods.removeAll(Arrays.asList(objectClass.getMethods())); if (methods.size() > 0) return false; - } else if (targetClass.getMethods().length > 0){ + } + else if (targetClass.getMethods().length > 0) { return false; } return targetClass.getAllFields().length > 0; @@ -87,30 +100,127 @@ public class ReplaceImplementsWithStaticImportAction implements IntentionAction public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { if (!CodeInsightUtilBase.preparePsiElementForWrite(file)) return; - final PsiReference psiReference = file.findReferenceAt(editor.getCaretModel().getOffset()); - LOG.assertTrue(psiReference != null); + final int offset = editor.getCaretModel().getOffset(); + final PsiReference psiReference = file.findReferenceAt(offset); + if (psiReference != null) { + final PsiElement element = psiReference.getElement(); - final PsiElement element = psiReference.getElement(); + final PsiClass psiClass = PsiTreeUtil.getParentOfType(element, PsiClass.class); + LOG.assertTrue(psiClass != null); - final PsiClass psiClass = PsiTreeUtil.getParentOfType(element, PsiClass.class); - LOG.assertTrue(psiClass != null); + final PsiElement target = psiReference.resolve(); + LOG.assertTrue(target instanceof PsiClass); - final PsiElement target = psiReference.resolve(); - LOG.assertTrue(target instanceof PsiClass); + final PsiClass targetClass = (PsiClass)target; - final PsiClass targetClass = (PsiClass)target; + new WriteCommandAction(project, getText(), file) { + protected void run(Result result) throws Throwable { + for (PsiField constField : targetClass.getAllFields()) { + final PsiClass containingClass = constField.getContainingClass(); + for (PsiReference ref : ReferencesSearch.search(constField, new LocalSearchScope(psiClass))) { + ((PsiReferenceExpressionImpl)ref) + .bindToElementViaStaticImport(containingClass, constField.getName(), ((PsiJavaFile)file).getImportList()); + } + } + element.delete(); + JavaCodeStyleManager.getInstance(project).optimizeImports(file); + } + }.execute(); + } + else { + final PsiElement identifier = file.findElementAt(offset); + LOG.assertTrue(identifier instanceof PsiIdentifier); + final PsiElement element = identifier.getParent(); + LOG.assertTrue(element instanceof PsiClass); + final PsiClass targetClass = (PsiClass)element; + final Map>> refs = new HashMap>>(); + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { + public void run() { + final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); + for (PsiField field : targetClass.getAllFields()) { + final String fieldName = field.getName(); + if (progressIndicator != null) { + progressIndicator.setText2(fieldName); + } + for (PsiReference reference : ReferencesSearch.search(field)) { + final PsiFile psiFile = reference.getElement().getContainingFile(); + if (psiFile instanceof PsiJavaFile) { + Map> references = refs.get(psiFile); + if (references == null) { + references = new HashMap>(); + refs.put(psiFile, references); + } + Set fieldsRefs = references.get(field); + if (fieldsRefs == null) { + fieldsRefs = new HashSet(); + references.put(field, fieldsRefs); + } + fieldsRefs.add(reference); + } + } + } + } + }, "Find constant field usages...", true, project)) { + return; + } - for (PsiField constField : targetClass.getAllFields()) { - for (PsiReference ref : ReferencesSearch.search(constField, new LocalSearchScope(psiClass))) { - ((PsiReferenceExpressionImpl)ref).bindToElementViaStaticImport(targetClass, constField.getName(), ((PsiJavaFile)file).getImportList()); + final Set refs2Unimplement = new HashSet(); + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { + public void run() { + for (PsiClass psiClass : DirectClassInheritorsSearch.search(targetClass)) { + PsiFile containingFile = psiClass.getContainingFile(); + if (!refs.containsKey(containingFile)) { + refs.put(containingFile, new HashMap>()); + } + if (collectExtendsImplements(targetClass, psiClass.getExtendsList(), refs2Unimplement)) continue; + collectExtendsImplements(targetClass, psiClass.getImplementsList(), refs2Unimplement); + } + } + }, "Find references in implement/extends lists...", true, project)) { + return; + } + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + for (PsiFile psiFile : refs.keySet()) { + final Map> map = refs.get(psiFile); + for (PsiField psiField : map.keySet()) { + final PsiClass containingClass = psiField.getContainingClass(); + final String fieldName = psiField.getName(); + for (PsiReference reference : map.get(psiField)) { + ((PsiReferenceExpressionImpl)reference) + .bindToElementViaStaticImport(containingClass, fieldName, ((PsiJavaFile)psiFile).getImportList()); + } + } + } + + for (PsiJavaCodeReferenceElement referenceElement : refs2Unimplement) { + referenceElement.delete(); + } + + for (PsiFile psiFile : refs.keySet()) { + JavaCodeStyleManager.getInstance(project).optimizeImports(psiFile); + } + } + }); + } + } + + private static boolean collectExtendsImplements(final PsiClass targetClass, + final PsiReferenceList referenceList, + final Set refs) { + if (referenceList != null) { + for (PsiJavaCodeReferenceElement referenceElement : referenceList.getReferenceElements()) { + if (referenceElement.resolve() == targetClass) { + refs.add(referenceElement); + return true; + } } } - - element.delete(); - new OptimizeImportsProcessor(project, file).run(); + return false; } public boolean startInWriteAction() { - return true; + return false; } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after6.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after6.java index be8d6fae6ad5..b7ba086dc7b0 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after6.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after6.java @@ -1,7 +1,7 @@ // "Replace Implements with Static Import" "true" -import static I.BAZZ; import static I.FOO; +import static I1.BAZZ; public class X { void bar() { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after7.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after7.java new file mode 100644 index 000000000000..1fe20918f90e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after7.java @@ -0,0 +1,18 @@ +// "Replace Implements with Static Import" "true" + +import static I1.BAZZ; + +public class X implements I { + void bar() { + System.out.println(FOO); + System.out.println(BAZZ); + } +} + +interface I { + String FOO = "foo"; +} + +interface I1 { + String BAZZ = "bazz"; +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after8.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after8.java new file mode 100644 index 000000000000..aff3ae5ca505 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after8.java @@ -0,0 +1,19 @@ +// "Replace Implements with Static Import" "true" + +import static I1.BAZZ; +import static II.FOO; + +public class X { + void bar() { + System.out.println(FOO); + System.out.println(BAZZ); + } +} + +interface II extends I1{ + String FOO = "foo"; +} + +interface I1 { + String BAZZ = "bazz"; +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before7.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before7.java new file mode 100644 index 000000000000..bada6cc62c6e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before7.java @@ -0,0 +1,15 @@ +// "Replace Implements with Static Import" "true" +public class X implements I { + void bar() { + System.out.println(FOO); + System.out.println(BAZZ); + } +} + +interface I extends I1{ + String FOO = "foo"; +} + +interface I1 { + String BAZZ = "bazz"; +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before8.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before8.java new file mode 100644 index 000000000000..e75d5ebadca5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before8.java @@ -0,0 +1,15 @@ +// "Replace Implements with Static Import" "true" +public class X implements II { + void bar() { + System.out.println(FOO); + System.out.println(BAZZ); + } +} + +interface II extends I1{ + String FOO = "foo"; +} + +interface I1 { + String BAZZ = "bazz"; +} From bd2a8d62302d4b25998437f4c895b0a9deec1ef4 Mon Sep 17 00:00:00 2001 From: anna Date: Thu, 24 Jun 2010 20:04:10 +0400 Subject: [PATCH 14/19] replace all inheritors with static imports: optimization --- ...placeImplementsWithStaticImportAction.java | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java b/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java index 680ed0a1afb7..ebddef797957 100644 --- a/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java +++ b/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java @@ -31,6 +31,7 @@ import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.DirectClassInheritorsSearch; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; @@ -137,25 +138,31 @@ public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseInten if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { public void run() { final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); - for (PsiField field : targetClass.getAllFields()) { - final String fieldName = field.getName(); - if (progressIndicator != null) { - progressIndicator.setText2(fieldName); - } - for (PsiReference reference : ReferencesSearch.search(field)) { - final PsiFile psiFile = reference.getElement().getContainingFile(); - if (psiFile instanceof PsiJavaFile) { - Map> references = refs.get(psiFile); - if (references == null) { - references = new HashMap>(); - refs.put(psiFile, references); + for (PsiClass psiClass : ClassInheritorsSearch.search(targetClass)) { + final PsiFile psiFile = psiClass.getContainingFile(); + if (psiFile instanceof PsiJavaFile) { + for (PsiField field : targetClass.getAllFields()) { + final String fieldName = field.getName(); + if (progressIndicator != null) { + progressIndicator.setText2(fieldName); } - Set fieldsRefs = references.get(field); - if (fieldsRefs == null) { - fieldsRefs = new HashSet(); - references.put(field, fieldsRefs); + for (PsiReference reference : ReferencesSearch.search(field, new LocalSearchScope(psiClass))) { + if (reference == null) { + continue; + } + + Map> references = refs.get(psiFile); + if (references == null) { + references = new HashMap>(); + refs.put(psiFile, references); + } + Set fieldsRefs = references.get(field); + if (fieldsRefs == null) { + fieldsRefs = new HashSet(); + references.put(field, fieldsRefs); + } + fieldsRefs.add(reference); } - fieldsRefs.add(reference); } } } @@ -182,22 +189,25 @@ public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseInten ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { + for (PsiJavaCodeReferenceElement referenceElement : refs2Unimplement) { + referenceElement.delete(); + } + for (PsiFile psiFile : refs.keySet()) { final Map> map = refs.get(psiFile); for (PsiField psiField : map.keySet()) { final PsiClass containingClass = psiField.getContainingClass(); final String fieldName = psiField.getName(); for (PsiReference reference : map.get(psiField)) { - ((PsiReferenceExpressionImpl)reference) - .bindToElementViaStaticImport(containingClass, fieldName, ((PsiJavaFile)psiFile).getImportList()); + if (reference instanceof PsiReferenceExpressionImpl) { + ((PsiReferenceExpressionImpl)reference).bindToElementViaStaticImport(containingClass, fieldName, ((PsiJavaFile)psiFile).getImportList()); + } else { + reference.bindToElement(psiField); + } } } } - for (PsiJavaCodeReferenceElement referenceElement : refs2Unimplement) { - referenceElement.delete(); - } - for (PsiFile psiFile : refs.keySet()) { JavaCodeStyleManager.getInstance(project).optimizeImports(psiFile); } From d6b4e25b2b2d9d169cd9a1087ea07f41634595e7 Mon Sep 17 00:00:00 2001 From: anna Date: Thu, 24 Jun 2010 20:40:30 +0400 Subject: [PATCH 15/19] replace all inheritors with static imports: revert optimization --- ...placeImplementsWithStaticImportAction.java | 56 ++++++++----------- 1 file changed, 23 insertions(+), 33 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java b/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java index ebddef797957..3d3fb2a64264 100644 --- a/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java +++ b/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java @@ -30,8 +30,6 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.LocalSearchScope; -import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.search.searches.DirectClassInheritorsSearch; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; @@ -118,7 +116,7 @@ public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseInten protected void run(Result result) throws Throwable { for (PsiField constField : targetClass.getAllFields()) { final PsiClass containingClass = constField.getContainingClass(); - for (PsiReference ref : ReferencesSearch.search(constField, new LocalSearchScope(psiClass))) { + for (PsiReference ref : ReferencesSearch.search(constField)) { ((PsiReferenceExpressionImpl)ref) .bindToElementViaStaticImport(containingClass, constField.getName(), ((PsiJavaFile)file).getImportList()); } @@ -138,32 +136,28 @@ public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseInten if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { public void run() { final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); - for (PsiClass psiClass : ClassInheritorsSearch.search(targetClass)) { - final PsiFile psiFile = psiClass.getContainingFile(); - if (psiFile instanceof PsiJavaFile) { - for (PsiField field : targetClass.getAllFields()) { - final String fieldName = field.getName(); - if (progressIndicator != null) { - progressIndicator.setText2(fieldName); - } - for (PsiReference reference : ReferencesSearch.search(field, new LocalSearchScope(psiClass))) { - if (reference == null) { - continue; - } - - Map> references = refs.get(psiFile); - if (references == null) { - references = new HashMap>(); - refs.put(psiFile, references); - } - Set fieldsRefs = references.get(field); - if (fieldsRefs == null) { - fieldsRefs = new HashSet(); - references.put(field, fieldsRefs); - } - fieldsRefs.add(reference); - } + for (PsiField field : targetClass.getAllFields()) { + final String fieldName = field.getName(); + if (progressIndicator != null) { + progressIndicator.setText2(fieldName); + } + for (PsiReference reference : ReferencesSearch.search(field)) { + if (reference == null) { + continue; } + + final PsiFile psiFile = reference.getElement().getContainingFile(); + Map> references = refs.get(psiFile); + if (references == null) { + references = new HashMap>(); + refs.put(psiFile, references); + } + Set fieldsRefs = references.get(field); + if (fieldsRefs == null) { + fieldsRefs = new HashSet(); + references.put(field, fieldsRefs); + } + fieldsRefs.add(reference); } } } @@ -199,7 +193,7 @@ public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseInten final PsiClass containingClass = psiField.getContainingClass(); final String fieldName = psiField.getName(); for (PsiReference reference : map.get(psiField)) { - if (reference instanceof PsiReferenceExpressionImpl) { + if (reference instanceof PsiReferenceExpressionImpl && psiFile instanceof PsiJavaFile) { ((PsiReferenceExpressionImpl)reference).bindToElementViaStaticImport(containingClass, fieldName, ((PsiJavaFile)psiFile).getImportList()); } else { reference.bindToElement(psiField); @@ -207,10 +201,6 @@ public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseInten } } } - - for (PsiFile psiFile : refs.keySet()) { - JavaCodeStyleManager.getInstance(project).optimizeImports(psiFile); - } } }); } From 7dd54f8acecab3e4aeaebdf17b4b641fe67a9fa1 Mon Sep 17 00:00:00 2001 From: anna Date: Mon, 28 Jun 2010 12:57:06 +0400 Subject: [PATCH 16/19] replace all inheritors with static imports: minimize touched files count --- .../codeInsight/ChangeContextUtil.java | 2 +- .../AddSingleMemberStaticImportAction.java | 6 +- ...placeImplementsWithStaticImportAction.java | 142 ++++++++++++++---- .../tree/java/PsiReferenceExpressionImpl.java | 35 +---- .../refactoring/util/RefactoringUtil.java | 31 ++++ .../after10.java | 26 ++++ .../after11.java | 29 ++++ .../after12.java | 29 ++++ .../after9.java | 20 +++ .../before10.java | 23 +++ .../before11.java | 26 ++++ .../before12.java | 27 ++++ .../before9.java | 17 +++ 13 files changed, 344 insertions(+), 69 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after10.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after11.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after12.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after9.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before10.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before11.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before12.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before9.java diff --git a/java/java-impl/src/com/intellij/codeInsight/ChangeContextUtil.java b/java/java-impl/src/com/intellij/codeInsight/ChangeContextUtil.java index 16d3d54b893f..be7b52124491 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ChangeContextUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/ChangeContextUtil.java @@ -280,7 +280,7 @@ public class ChangeContextUtil { return refMemberClass; } - private static boolean canRemoveQualifier(PsiReferenceExpression refExpr) { + public static boolean canRemoveQualifier(PsiReferenceExpression refExpr) { try{ PsiExpression qualifier = refExpr.getQualifierExpression(); if (!(qualifier instanceof PsiReferenceExpression)) return false; diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddSingleMemberStaticImportAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddSingleMemberStaticImportAction.java index 5fc72da94f04..74c8049add86 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddSingleMemberStaticImportAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AddSingleMemberStaticImportAction.java @@ -27,9 +27,9 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.psi.*; -import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; @@ -91,7 +91,7 @@ public class AddSingleMemberStaticImportAction extends PsiElementBaseIntentionAc } }); - ((PsiReferenceExpressionImpl)refExpr).bindToElementViaStaticImport(((PsiMember)resolved).getContainingClass(), ((PsiNamedElement)resolved).getName(), ((PsiJavaFile)file).getImportList()); + RefactoringUtil.bindToElementViaStaticImport(((PsiMember)resolved).getContainingClass(), ((PsiNamedElement)resolved).getName(), ((PsiJavaFile)file).getImportList()); file.accept(new JavaRecursiveElementVisitor() { @Override public void visitReferenceExpression(PsiReferenceExpression expression) { @@ -134,4 +134,4 @@ public class AddSingleMemberStaticImportAction extends PsiElementBaseIntentionAc } }); } -} \ No newline at end of file +} diff --git a/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java b/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java index 3d3fb2a64264..5e17e98119a8 100644 --- a/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java +++ b/java/java-impl/src/com/intellij/codeInspection/actions/ReplaceImplementsWithStaticImportAction.java @@ -15,6 +15,7 @@ */ package com.intellij.codeInspection.actions; +import com.intellij.codeInsight.ChangeContextUtil; import com.intellij.codeInsight.CodeInsightUtilBase; import com.intellij.codeInsight.TargetElementUtilBase; import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; @@ -23,23 +24,26 @@ import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl; +import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.DirectClassInheritorsSearch; import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.*; public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseIntentionAction { private static final Logger LOG = Logger.getInstance("#" + ReplaceImplementsWithStaticImportAction.class.getName()); + @NonNls private static final String FIND_CONSTANT_FIELD_USAGES = "Find constant field usages..."; @NotNull public String getText() { @@ -111,14 +115,27 @@ public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseInten LOG.assertTrue(target instanceof PsiClass); final PsiClass targetClass = (PsiClass)target; - - new WriteCommandAction(project, getText(), file) { + new WriteCommandAction(project, getText()) { protected void run(Result result) throws Throwable { for (PsiField constField : targetClass.getAllFields()) { + final String fieldName = constField.getName(); final PsiClass containingClass = constField.getContainingClass(); for (PsiReference ref : ReferencesSearch.search(constField)) { - ((PsiReferenceExpressionImpl)ref) - .bindToElementViaStaticImport(containingClass, constField.getName(), ((PsiJavaFile)file).getImportList()); + final PsiElement psiElement = ref.getElement(); + if (ref instanceof PsiReferenceExpression) { + final PsiElement qualifier = ((PsiReferenceExpression)ref).getQualifier(); + if (qualifier != null) { + if (qualifier instanceof PsiReferenceExpression) { + final PsiElement resolved = ((PsiReferenceExpression)qualifier).resolve(); + if (resolved instanceof PsiClass && !InheritanceUtil.isInheritorOrSelf(psiClass, (PsiClass)resolved, true)) { + continue; + } + } + qualifier.putCopyableUserData(ChangeContextUtil.CAN_REMOVE_QUALIFIER_KEY, + ChangeContextUtil.canRemoveQualifier((PsiReferenceExpression)ref)); + } + } + bindReference(psiElement.getContainingFile(), constField, containingClass, fieldName, ref, project); } } element.delete(); @@ -135,33 +152,32 @@ public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseInten final Map>> refs = new HashMap>>(); if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() { public void run() { - final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); for (PsiField field : targetClass.getAllFields()) { - final String fieldName = field.getName(); - if (progressIndicator != null) { - progressIndicator.setText2(fieldName); - } + final PsiClass containingClass = field.getContainingClass(); for (PsiReference reference : ReferencesSearch.search(field)) { if (reference == null) { continue; } - - final PsiFile psiFile = reference.getElement().getContainingFile(); - Map> references = refs.get(psiFile); - if (references == null) { - references = new HashMap>(); - refs.put(psiFile, references); + final PsiElement refElement = reference.getElement(); + if (encodeQualifier(containingClass, reference, targetClass)) continue; + final PsiFile psiFile = refElement.getContainingFile(); + if (psiFile instanceof PsiJavaFile) { + Map> references = refs.get(psiFile); + if (references == null) { + references = new HashMap>(); + refs.put(psiFile, references); + } + Set fieldsRefs = references.get(field); + if (fieldsRefs == null) { + fieldsRefs = new HashSet(); + references.put(field, fieldsRefs); + } + fieldsRefs.add(reference); } - Set fieldsRefs = references.get(field); - if (fieldsRefs == null) { - fieldsRefs = new HashSet(); - references.put(field, fieldsRefs); - } - fieldsRefs.add(reference); } } } - }, "Find constant field usages...", true, project)) { + }, FIND_CONSTANT_FIELD_USAGES, true, project)) { return; } @@ -183,9 +199,6 @@ public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseInten ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { - for (PsiJavaCodeReferenceElement referenceElement : refs2Unimplement) { - referenceElement.delete(); - } for (PsiFile psiFile : refs.keySet()) { final Map> map = refs.get(psiFile); @@ -193,19 +206,84 @@ public class ReplaceImplementsWithStaticImportAction extends PsiElementBaseInten final PsiClass containingClass = psiField.getContainingClass(); final String fieldName = psiField.getName(); for (PsiReference reference : map.get(psiField)) { - if (reference instanceof PsiReferenceExpressionImpl && psiFile instanceof PsiJavaFile) { - ((PsiReferenceExpressionImpl)reference).bindToElementViaStaticImport(containingClass, fieldName, ((PsiJavaFile)psiFile).getImportList()); - } else { - reference.bindToElement(psiField); - } + bindReference(psiFile, psiField, containingClass, fieldName, reference, project); + } + } + } + + for (PsiJavaCodeReferenceElement referenceElement : refs2Unimplement) { + referenceElement.delete(); + } + } + }); + + final Set> redundant = new HashSet>(); + final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project); + final SmartPointerManager pointerManager = SmartPointerManager.getInstance(project); + if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable(){ + public void run() { + for (PsiFile psiFile : refs.keySet()) { + final Collection red = codeStyleManager.findRedundantImports((PsiJavaFile)psiFile); + if (red != null) { + for (PsiImportStatementBase statementBase : red) { + redundant.add(pointerManager.createSmartPsiElementPointer(statementBase)); } } } } + }, "Collect redundant imports...", true, project)) return; + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + for (SmartPsiElementPointer pointer : redundant) { + final PsiImportStatementBase statementBase = pointer.getElement(); + if (statementBase != null) statementBase.delete(); + } + } }); } } + private static boolean encodeQualifier(PsiClass containingClass, PsiReference reference, PsiClass targetClass) { + if (reference instanceof PsiReferenceExpression) { + final PsiElement qualifier = ((PsiReferenceExpression)reference).getQualifier(); + if (qualifier != null) { + if (qualifier instanceof PsiReferenceExpression) { + final PsiElement resolved = ((PsiReferenceExpression)qualifier).resolve(); + if (resolved == containingClass || resolved instanceof PsiClass && InheritanceUtil.isInheritorOrSelf(targetClass, (PsiClass)resolved, true)) { + return true; + } + } + qualifier.putCopyableUserData(ChangeContextUtil.CAN_REMOVE_QUALIFIER_KEY, + ChangeContextUtil.canRemoveQualifier((PsiReferenceExpression)reference)); + } + } + return false; + } + + private static void bindReference(PsiFile psiFile, + PsiField psiField, + PsiClass containingClass, + String fieldName, + PsiReference reference, + Project project) { + if (reference instanceof PsiReferenceExpression) { + RefactoringUtil.bindToElementViaStaticImport(containingClass, fieldName, ((PsiJavaFile)psiFile).getImportList()); + final PsiElement qualifier = ((PsiReferenceExpression)reference).getQualifier(); + if (qualifier != null) { + final Boolean canRemoveQualifier = qualifier.getCopyableUserData(ChangeContextUtil.CAN_REMOVE_QUALIFIER_KEY); + if (canRemoveQualifier != null && canRemoveQualifier.booleanValue()) { + qualifier.delete(); + } else { + final PsiJavaCodeReferenceElement classReferenceElement = + JavaPsiFacade.getElementFactory(project).createReferenceExpression(containingClass); + qualifier.replace(classReferenceElement); + } + } + } else if (reference.getElement() instanceof PsiDocMethodOrFieldRef){ + reference.bindToElement(psiField); //todo refs through inheritors + } + } + private static boolean collectExtendsImplements(final PsiClass targetClass, final PsiReferenceList referenceList, final Set refs) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java index 16c1b2ca634c..717e7c903aa5 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java @@ -17,7 +17,6 @@ package com.intellij.psi.impl.source.tree.java; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.TextRange; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; @@ -47,9 +46,9 @@ import com.intellij.psi.scope.processor.MethodResolverProcessor; import com.intellij.psi.scope.util.PsiScopesUtil; import com.intellij.psi.tree.ChildRoleBase; import com.intellij.psi.tree.IElementType; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.CharTable; import com.intellij.util.Function; @@ -59,8 +58,6 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; import java.util.Set; public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements PsiReferenceExpression, SourceJavaCodeReference { @@ -105,7 +102,7 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements doImportStatic = false; } if (doImportStatic) { - bindToElementViaStaticImport(qualifierClass, staticName, importList); + RefactoringUtil.bindToElementViaStaticImport(qualifierClass, staticName, importList); } else { PsiManagerEx manager = getManager(); @@ -118,34 +115,6 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements return this; } - public void bindToElementViaStaticImport(final PsiClass qualifierClass, final String staticName, final PsiImportList importList) - throws IncorrectOperationException { - final String qualifiedName = qualifierClass.getQualifiedName(); - final List refs = getImportsFromClass(importList, qualifiedName); - if (refs.size() < CodeStyleSettingsManager.getSettings(getProject()).NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND) { - importList.add(JavaPsiFacade.getInstance(getProject()).getElementFactory().createImportStaticStatement(qualifierClass, staticName)); - } else { - for (PsiJavaCodeReferenceElement ref : refs) { - final PsiImportStaticStatement importStatement = PsiTreeUtil.getParentOfType(ref, PsiImportStaticStatement.class); - if (importStatement != null) { - importStatement.delete(); - } - } - importList.add(JavaPsiFacade.getInstance(getProject()).getElementFactory().createImportStaticStatement(qualifierClass, "*")); - } - } - - private static List getImportsFromClass(@NotNull PsiImportList importList, String className){ - final List array = new ArrayList(); - for (PsiImportStaticStatement staticStatement : importList.getImportStaticStatements()) { - final PsiClass psiClass = staticStatement.resolveTargetClass(); - if (psiClass != null && Comparing.strEqual(psiClass.getQualifiedName(), className)) { - array.add(staticStatement.getImportReference()); - } - } - return array; - } - public void setQualifierExpression(@Nullable PsiExpression newQualifier) throws IncorrectOperationException { final PsiExpression oldQualifier = getQualifierExpression(); if (newQualifier == null) { diff --git a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java index 5d690a7d6d53..aae71ee656f4 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/RefactoringUtil.java @@ -36,6 +36,7 @@ import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.VariableKind; import com.intellij.psi.controlFlow.ControlFlowUtil; @@ -62,6 +63,8 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import static com.intellij.psi.JavaTokenType.*; + public class RefactoringUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.util.RefactoringUtil"); public static final int EXPR_COPY_SAFE = 0; @@ -882,6 +885,34 @@ public class RefactoringUtil { } } + public static void bindToElementViaStaticImport(final PsiClass qualifierClass, final String staticName, final PsiImportList importList) + throws IncorrectOperationException { + final String qualifiedName = qualifierClass.getQualifiedName(); + final List refs = getImportsFromClass(importList, qualifiedName); + if (refs.size() < CodeStyleSettingsManager.getSettings(qualifierClass.getProject()).NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND) { + importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass, staticName)); + } else { + for (PsiJavaCodeReferenceElement ref : refs) { + final PsiImportStaticStatement importStatement = PsiTreeUtil.getParentOfType(ref, PsiImportStaticStatement.class); + if (importStatement != null) { + importStatement.delete(); + } + } + importList.add(JavaPsiFacade.getInstance(qualifierClass.getProject()).getElementFactory().createImportStaticStatement(qualifierClass, "*")); + } + } + + public static List getImportsFromClass(@NotNull PsiImportList importList, String className){ + final List array = new ArrayList(); + for (PsiImportStaticStatement staticStatement : importList.getImportStaticStatements()) { + final PsiClass psiClass = staticStatement.resolveTargetClass(); + if (psiClass != null && Comparing.strEqual(psiClass.getQualifiedName(), className)) { + array.add(staticStatement.getImportReference()); + } + } + return array; + } + public static interface ImplicitConstructorUsageVisitor { void visitConstructor(PsiMethod constructor, PsiMethod baseConstructor); diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after10.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after10.java new file mode 100644 index 000000000000..32e18d994951 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after10.java @@ -0,0 +1,26 @@ +// "Replace Implements with Static Import" "true" + +import static In.FOO; + +interface In { + int FOO = 0; +} + +class II { + public static void main(String[] args) { + System.out.println(FOO); + } +} + +class II1 { + public static void main(String[] args) { + System.out.println(FOO); + } +} + +class Uc { + static final String FOO = ""; + void g() { + System.out.println("" + In.FOO); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after11.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after11.java new file mode 100644 index 000000000000..927269a1590a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after11.java @@ -0,0 +1,29 @@ +// "Replace Implements with Static Import" "true" + +import static In.FOO; + +interface In { + int FOO = 0; +} + +class II { + public static void main(String[] args) { + System.out.println(FOO); + } +} + +/** + * {@link In.FOO} + */ +class II1 { + public static void main(String[] args) { + System.out.println(FOO); + } +} + +class Uc { + static final String FOO = ""; + void g() { + System.out.println("" + In.FOO); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after12.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after12.java new file mode 100644 index 000000000000..7e89a69b9da9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after12.java @@ -0,0 +1,29 @@ +// "Replace Implements with Static Import" "true" + +import static In.FOO; + +interface In { + int FOO = 0; +} + +class II { + public static void main(String[] args) { + System.out.println(FOO); + } +} + +/** + * {@link I#FOO} + */ +class II1 { + public static void main(String[] args) { + System.out.println(FOO); + } +} + +class Uc { + static final String FOO = ""; + void g() { + System.out.println("" + In.FOO); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after9.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after9.java new file mode 100644 index 000000000000..1d0d3e4d4a2b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/after9.java @@ -0,0 +1,20 @@ +// "Replace Implements with Static Import" "true" + +import static In.FOO; + +interface In { + int FOO = 0; +} + +class II { + public static void main(String[] args) { + System.out.println(FOO); + } +} + +class II1 { + int FOO = 9; + public static void main(String[] args) { + System.out.println(In.FOO); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before10.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before10.java new file mode 100644 index 000000000000..a870d34f5f0a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before10.java @@ -0,0 +1,23 @@ +// "Replace Implements with Static Import" "true" +interface In { + int FOO = 0; +} + +class II implements In { + public static void main(String[] args) { + System.out.println(FOO); + } +} + +class II1 implements In { + public static void main(String[] args) { + System.out.println(II1.FOO); + } +} + +class Uc { + static final String FOO = ""; + void g() { + System.out.println("" + II.FOO); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before11.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before11.java new file mode 100644 index 000000000000..5d159cea8666 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before11.java @@ -0,0 +1,26 @@ +// "Replace Implements with Static Import" "true" +interface In { + int FOO = 0; +} + +class II implements In { + public static void main(String[] args) { + System.out.println(FOO); + } +} + +/** + * {@link In.FOO} + */ +class II1 implements In { + public static void main(String[] args) { + System.out.println(II1.FOO); + } +} + +class Uc { + static final String FOO = ""; + void g() { + System.out.println("" + II.FOO); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before12.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before12.java new file mode 100644 index 000000000000..9c9db6bb962c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before12.java @@ -0,0 +1,27 @@ +// "Replace Implements with Static Import" "true" + +interface In { + int FOO = 0; +} + +class II implements In { + public static void main(String[] args) { + System.out.println(FOO); + } +} + +/** + * {@link I#FOO} + */ +class II1 implements In { + public static void main(String[] args) { + System.out.println(II1.FOO); + } +} + +class Uc { + static final String FOO = ""; + void g() { + System.out.println("" + II.FOO); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before9.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before9.java new file mode 100644 index 000000000000..6bb182fb744a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/replaceImplementsWithStaticImport/before9.java @@ -0,0 +1,17 @@ +// "Replace Implements with Static Import" "true" +interface In { + int FOO = 0; +} + +class II implements In { + public static void main(String[] args) { + System.out.println(FOO); + } +} + +class II1 implements In { + int FOO = 9; + public static void main(String[] args) { + System.out.println(In.FOO); + } +} From 6e67b58e861ec41fa42fd51617fb71717f0568c3 Mon Sep 17 00:00:00 2001 From: anna Date: Mon, 28 Jun 2010 15:54:56 +0400 Subject: [PATCH 17/19] preselect existing configs --- .../actions/BaseRunConfigurationAction.java | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/actions/BaseRunConfigurationAction.java b/platform/lang-impl/src/com/intellij/execution/actions/BaseRunConfigurationAction.java index 95ddd65e0696..62afe1947d0b 100644 --- a/platform/lang-impl/src/com/intellij/execution/actions/BaseRunConfigurationAction.java +++ b/platform/lang-impl/src/com/intellij/execution/actions/BaseRunConfigurationAction.java @@ -45,7 +45,7 @@ import java.util.Comparator; import java.util.List; public abstract class BaseRunConfigurationAction extends ActionGroup { - private static final Logger LOG = Logger.getInstance("#" + BaseRunConfigurationAction.class.getName()); + protected static final Logger LOG = Logger.getInstance("#" + BaseRunConfigurationAction.class.getName()); protected BaseRunConfigurationAction(final String text, final String description, final Icon icon) { super(text, description, icon); @@ -175,12 +175,15 @@ public abstract class BaseRunConfigurationAction extends ActionGroup { public void update(final AnActionEvent event){ final ConfigurationContext context = ConfigurationContext.getFromContext(event.getDataContext()); final Presentation presentation = event.getPresentation(); - RunnerAndConfigurationSettings configuration; - try { - configuration = context.getConfiguration(); - } - catch (IndexNotReadyException e) { - configuration = null; + final RunnerAndConfigurationSettings existing = context.findExisting(); + RunnerAndConfigurationSettings configuration = existing; + if (configuration == null) { + try { + configuration = context.getConfiguration(); + } + catch (IndexNotReadyException e) { + configuration = null; + } } if (configuration == null){ presentation.setEnabled(false); @@ -189,13 +192,13 @@ public abstract class BaseRunConfigurationAction extends ActionGroup { else{ presentation.setEnabled(true); presentation.setVisible(true); - final String name = suggestRunActionName((LocatableConfiguration)configuration.getConfiguration()); final List producers = getEnabledProducers(context); - if (!producers.isEmpty()) { - //todo[nik,anna] it's dirty fix. Otherwise wrong configuration will be returned from context.getConfiguration() + if (existing == null && !producers.isEmpty()) { + //todo[nik,anna] it's dirty fix. Otherwise wrong configuration will be returned from context.getConfiguration() context.getConfiguration(producers.get(0)); } - updatePresentation(presentation, context.findExisting() != null || producers.size() <= 1 ? " " + name : "", context); + final String name = suggestRunActionName((LocatableConfiguration)configuration.getConfiguration()); + updatePresentation(presentation, existing != null || producers.size() <= 1 ? " " + name : "", context); } } From bbc4bdaa7571ced170c32275137c566d2447372f Mon Sep 17 00:00:00 2001 From: anna Date: Mon, 28 Jun 2010 17:07:54 +0400 Subject: [PATCH 18/19] NPE --- .../com/intellij/execution/actions/ConfigurationContext.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/lang-api/src/com/intellij/execution/actions/ConfigurationContext.java b/platform/lang-api/src/com/intellij/execution/actions/ConfigurationContext.java index 953f22d423cb..dda59b795c58 100644 --- a/platform/lang-api/src/com/intellij/execution/actions/ConfigurationContext.java +++ b/platform/lang-api/src/com/intellij/execution/actions/ConfigurationContext.java @@ -60,7 +60,10 @@ public class ConfigurationContext { final ConfigurationContext context = new ConfigurationContext(dataContext); final DataManager dataManager = DataManager.getInstance(); ConfigurationContext sharedContext = dataManager.loadFromDataContext(dataContext, SHARED_CONTEXT); - if (sharedContext == null || !Comparing.equal(sharedContext.getLocation().getPsiElement(), context.getLocation().getPsiElement())) { + if (sharedContext == null || + sharedContext.getLocation() == null || + context.getLocation() == null || + !Comparing.equal(sharedContext.getLocation().getPsiElement(), context.getLocation().getPsiElement())) { sharedContext = context; dataManager.saveInDataContext(dataContext, SHARED_CONTEXT, sharedContext); } From a09b9ba4203b136bddd22965c2412e7bf933ca6e Mon Sep 17 00:00:00 2001 From: anna Date: Mon, 28 Jun 2010 18:41:32 +0400 Subject: [PATCH 19/19] do not guess --- .../eclipse/conversion/IdeaSpecificSettings.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java b/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java index c7b507114679..9eacfd3fc948 100644 --- a/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java +++ b/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/IdeaSpecificSettings.java @@ -111,21 +111,6 @@ public class IdeaSpecificSettings { replaceModuleRelatedRoots(model.getProject(), modifiableModel, libElement, JavadocOrderRootType.getInstance(), RELATIVE_MODULE_JAVADOC); modifiableModel.commit(); } - else { - libraryByName = EclipseClasspathReader.findLibraryByName(model.getProject(), libName); - if (libraryByName != null) { - appendLibraryScope(model, libElement, libraryByName); - } - final Library[] libraries = model.getModuleLibraryTable().getLibraries(); - for (Library library : libraries) { - final Library.ModifiableModel modifiableModel = library.getModifiableModel(); - replaceCollapsedByEclipseSourceRoots(libElement, modifiableModel); - replaceModuleRelatedRoots(model.getProject(), modifiableModel, libElement, OrderRootType.SOURCES, RELATIVE_MODULE_SRC); - replaceModuleRelatedRoots(model.getProject(), modifiableModel, libElement, OrderRootType.CLASSES, RELATIVE_MODULE_CLS); - replaceModuleRelatedRoots(model.getProject(), modifiableModel, libElement, JavadocOrderRootType.getInstance(), RELATIVE_MODULE_JAVADOC); - modifiableModel.commit(); - } - } } }