diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2013-04-01 14:41:51 -0700 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2013-04-01 14:41:51 -0700 |
commit | 2bd2b7c2623d4266384e890271869efc044aabff (patch) | |
tree | 0b31f50e55975b6354ed458314e17b4441bb4e17 /plugins/devkit | |
parent | 1d526b16d476792ca7ce47616d55833115e8d6ab (diff) | |
download | idea-2bd2b7c2623d4266384e890271869efc044aabff.tar.gz |
Snapshot ee98b298267d0e09d2cd2f0731b6480a56dd48e7 from master branch of git://git.jetbrains.org/idea/community.git
Change-Id: I4515f72af131fdea9fc6905a4dc0fe9532409a81
Diffstat (limited to 'plugins/devkit')
24 files changed, 3361 insertions, 11 deletions
diff --git a/plugins/devkit/devkit.iml b/plugins/devkit/devkit.iml index a4dfa50b500e..cfc7dad256db 100644 --- a/plugins/devkit/devkit.iml +++ b/plugins/devkit/devkit.iml @@ -28,6 +28,16 @@ <orderEntry type="library" scope="TEST" name="Groovy" level="project" /> <orderEntry type="module" module-name="jps-builders" /> <orderEntry type="library" name="Guava" level="project" /> + <orderEntry type="library" name="Xerces" level="project" /> + <orderEntry type="module-library"> + <library name="DTDParser"> + <CLASSES> + <root url="jar://$MODULE_DIR$/lib/dtdparser113.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES /> + </library> + </orderEntry> </component> </module> diff --git a/plugins/devkit/lib/dtdparser113.jar b/plugins/devkit/lib/dtdparser113.jar Binary files differnew file mode 100644 index 000000000000..4172640cb9d8 --- /dev/null +++ b/plugins/devkit/lib/dtdparser113.jar diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index 099b9b23dfb4..667fcd76911f 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -140,6 +140,10 @@ <action internal="true" class="org.jetbrains.idea.devkit.actions.ShowSerializedXmlAction" text="Show Serialized XML for Class" id="ShowSerializedXml"/> + + <action internal="true" class="org.jetbrains.idea.devkit.dom.generator.GenerateDomModelAction" text="Generate Dom Model" + id="GenerateDomModel"/> + <add-to-group group-id="Internal" anchor="last"/> </group> diff --git a/plugins/devkit/src/build/PluginModuleBuildScopeProvider.java b/plugins/devkit/src/build/PluginModuleBuildScopeProvider.java index d4899785a7f7..79bccac2c71e 100644 --- a/plugins/devkit/src/build/PluginModuleBuildScopeProvider.java +++ b/plugins/devkit/src/build/PluginModuleBuildScopeProvider.java @@ -22,6 +22,7 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.devkit.module.PluginModuleType; +import org.jetbrains.jps.api.CmdlineProtoUtil; import org.jetbrains.jps.api.CmdlineRemoteProto.Message.ControllerMessage.ParametersMessage.TargetTypeBuildScope; import org.jetbrains.jps.incremental.artifacts.ArtifactBuildTargetType; @@ -35,7 +36,8 @@ import java.util.List; public class PluginModuleBuildScopeProvider extends BuildTargetScopeProvider { @NotNull @Override - public List<TargetTypeBuildScope> getBuildTargetScopes(@NotNull CompileScope baseScope, @NotNull CompilerFilter filter, @NotNull Project project) { + public List<TargetTypeBuildScope> getBuildTargetScopes(@NotNull CompileScope baseScope, @NotNull CompilerFilter filter, + @NotNull Project project, boolean forceBuild) { List<String> pluginArtifactTargetIds = new ArrayList<String>(); for (Module module : baseScope.getAffectedModules()) { if (PluginModuleType.isOfType(module)) { @@ -46,6 +48,6 @@ public class PluginModuleBuildScopeProvider extends BuildTargetScopeProvider { if (pluginArtifactTargetIds.isEmpty()) { return Collections.emptyList(); } - return Collections.singletonList(TargetTypeBuildScope.newBuilder().setTypeId(ArtifactBuildTargetType.INSTANCE.getTypeId()).addAllTargetId(pluginArtifactTargetIds).build()); + return Collections.singletonList(CmdlineProtoUtil.createTargetsScope(ArtifactBuildTargetType.INSTANCE.getTypeId(), pluginArtifactTargetIds, forceBuild)); } } diff --git a/plugins/devkit/src/dom/generator/DTDModelLoader.java b/plugins/devkit/src/dom/generator/DTDModelLoader.java new file mode 100644 index 000000000000..4a49d6eacbcf --- /dev/null +++ b/plugins/devkit/src/dom/generator/DTDModelLoader.java @@ -0,0 +1,339 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import com.wutka.dtd.*; +import org.apache.xerces.xni.parser.XMLEntityResolver; + +import java.io.File; +import java.util.*; + +/** + * @author Gregory.Shrago + * @author Konstantin Bulenkov + */ +public class DTDModelLoader implements ModelLoader { + private ModelDesc model; + + public void loadModel(ModelDesc model, Collection<File> 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<String, TypeDesc> jtMap, Map<String, NamespaceDesc> nsdMap) { + final NamespaceDesc nsd = ensureNamespaceExists(namespace); + if (nsd.skip) return; + final ArrayList<String> resultQNames = new ArrayList<String>(); + final DTDElement[] elements = new DTDElement[dtd.elements.size()]; + int ptr = 1; + + final HashSet<DTDElement> visitedElements = new HashSet<DTDElement>(); + 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<List<DTDItem>> choiceList = new ArrayList<List<DTDItem>>(); + final LinkedList<Entry> plist = new LinkedList<Entry>(); + 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<DTDItem> 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<DTDItem> l : choiceList) { + ArrayList<DTDItem> clist = new ArrayList<DTDItem>(); + LinkedList<DTDItem> elist = new LinkedList<DTDItem>(); + for (i = 0; i < l.size(); i++) { + elist.add(l.get(i)); + } + while (!elist.isEmpty()) { + DTDItem p = elist.removeFirst(); + if (p instanceof DTDContainer) { + List<DTDItem> 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<DTDEntity> 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" + "<pre>\n" + comment.getText() + "\n</pre>"; + } + + 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/plugins/devkit/src/dom/generator/DomGenDialog.java b/plugins/devkit/src/dom/generator/DomGenDialog.java new file mode 100644 index 000000000000..52d2675e0ea3 --- /dev/null +++ b/plugins/devkit/src/dom/generator/DomGenDialog.java @@ -0,0 +1,79 @@ +/* + * 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 org.jetbrains.idea.devkit.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(); + panel.restore(); + setTitle("Generate Dom Model from XSD or DTD"); + init(); + getOKAction().putValue(Action.NAME, "Generate"); + } + + @Override + protected JComponent createCenterPanel() { + return comp; + } + + @Override + protected void doOKAction() { + if (!panel.validate()) return; + final String location = panel.getLocation(); + ModelLoader loader = location.toLowerCase().endsWith(".xsd") ? new XSDModelLoader() : new DTDModelLoader(); + final JetBrainsEmitter emitter = new JetBrainsEmitter(); + final MergingFileManager fileManager = new MergingFileManager(); + if (panel.getAuthor().trim().length() > 0) { + emitter.setAuthor(panel.getAuthor()); + } + final ModelGen modelGen = new ModelGen(loader, emitter, fileManager); + final NamespaceDesc desc = panel.getNamespaceDescriptor(); + modelGen.setConfig(desc.name, location, desc, panel.getSkippedSchemas()); + try { + final File output = new File(panel.getOutputDir()); + modelGen.perform(output, new File(location).getParentFile()); + } catch (Exception e) { + e.printStackTrace(System.err); + } + panel.saveAll(); + super.doOKAction(); + } + + @Override + public void doCancelAction() { + panel.saveAll(); + super.doCancelAction(); + } + + @Override + protected String getDimensionServiceKey() { + return getClass().getName(); + } +} diff --git a/plugins/devkit/src/dom/generator/DomGenPanel.form b/plugins/devkit/src/dom/generator/DomGenPanel.form new file mode 100644 index 000000000000..1cc5978d395b --- /dev/null +++ b/plugins/devkit/src/dom/generator/DomGenPanel.form @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.idea.devkit.dom.generator.DomGenPanel"> + <grid id="27dc6" binding="mainPanel" layout-manager="GridLayoutManager" row-count="8" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <margin top="0" left="0" bottom="0" right="0"/> + <constraints> + <xy x="20" y="20" width="424" height="262"/> + </constraints> + <properties/> + <clientProperties> + <BorderFactoryClass class="java.lang.String" value="com.intellij.ui.IdeBorderFactory$PlainSmallWithIndent"/> + </clientProperties> + <border type="none" title="Dom Generator Config"/> + <children> + <component id="29b47" class="javax.swing.JLabel"> + <constraints> + <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text value="Namespace:"/> + </properties> + </component> + <vspacer id="e8ccd"> + <constraints> + <grid row="7" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> + </constraints> + </vspacer> + <component id="f5680" class="javax.swing.JLabel"> + <constraints> + <grid row="3" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text value="Super Class:"/> + </properties> + </component> + <component id="20f66" class="javax.swing.JTextField" binding="mySuperClass"> + <constraints> + <grid row="3" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false"> + <preferred-size width="327" height="22"/> + </grid> + </constraints> + <properties> + <text value="com.intellij.util.xml.DomElement"/> + </properties> + </component> + <component id="4a359" class="javax.swing.JLabel"> + <constraints> + <grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text value="Package:"/> + </properties> + </component> + <component id="afd05" class="javax.swing.JTextField" binding="myPackage"> + <constraints> + <grid row="2" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false"> + <preferred-size width="327" height="22"/> + </grid> + </constraints> + <properties> + <text value="com.intellij.myFramework.model"/> + </properties> + </component> + <component id="d62dc" class="javax.swing.JLabel"> + <constraints> + <grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text value="Output Dir:"/> + </properties> + </component> + <component id="6a48b" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="myOutputDir" custom-create="true"> + <constraints> + <grid row="4" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"> + <preferred-size width="327" height="22"/> + </grid> + </constraints> + <properties/> + </component> + <component id="7fb9d" class="com.intellij.openapi.ui.TextFieldWithBrowseButton" binding="mySchemaLocation" custom-create="true"> + <constraints> + <grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"> + <preferred-size width="327" height="22"/> + </grid> + </constraints> + <properties/> + </component> + <component id="7940e" class="javax.swing.JLabel"> + <constraints> + <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text value="Scheme File:"/> + </properties> + </component> + <component id="833b2" class="javax.swing.JTextField" binding="myNamespace"> + <constraints> + <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false"> + <preferred-size width="327" height="22"/> + </grid> + </constraints> + <properties/> + </component> + <component id="bdaaf" class="javax.swing.JLabel"> + <constraints> + <grid row="6" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="9" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text value="Skip schemes:"/> + </properties> + </component> + <component id="f4732" class="javax.swing.JTextArea" binding="mySkipSchemas"> + <constraints> + <grid row="6" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="6" anchor="0" fill="3" indent="0" use-parent-layout="false"> + <preferred-size width="150" height="50"/> + </grid> + </constraints> + <properties/> + </component> + <component id="54b59" class="javax.swing.JLabel"> + <constraints> + <grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <text value="@author:"/> + </properties> + </component> + <component id="b033e" class="javax.swing.JTextField" binding="myAuthor"> + <constraints> + <grid row="5" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="8" fill="1" indent="0" use-parent-layout="false"> + <preferred-size width="150" height="-1"/> + </grid> + </constraints> + <properties/> + </component> + </children> + </grid> +</form> diff --git a/plugins/devkit/src/dom/generator/DomGenPanel.java b/plugins/devkit/src/dom/generator/DomGenPanel.java new file mode 100644 index 000000000000..df2a6d8e8701 --- /dev/null +++ b/plugins/devkit/src/dom/generator/DomGenPanel.java @@ -0,0 +1,177 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; +import com.intellij.openapi.fileChooser.FileTypeDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.xml.XmlAttribute; +import com.intellij.psi.xml.XmlDocument; +import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; +import com.intellij.util.ArrayUtil; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; + +/** + * @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 JTextArea mySkipSchemas; + private JTextField myAuthor; + 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, "Make sure there are only necessary schemes in directory where your XSD or DTD schema is located", myProject, new FileTypeDescriptor(title, "xsd", "dtd")); + mySchemaLocation.getTextField().setEditable(false); + mySchemaLocation.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + final File file = new File(mySchemaLocation.getText()); + if (file.exists() && file.getName().toLowerCase().endsWith(".xsd")) { + final VirtualFile vf = LocalFileSystem.getInstance().findFileByIoFile(file); + if (vf != null) { + final PsiFile psiFile = PsiManager.getInstance(myProject).findFile(vf); + if (psiFile instanceof XmlFile) { + final XmlDocument xml = ((XmlFile)psiFile).getDocument(); + if (xml != null) { + final XmlTag rootTag = xml.getRootTag(); + if (rootTag != null) { + String target = null; + ArrayList<String> ns = new ArrayList<String>(); + for (XmlAttribute attr : rootTag.getAttributes()) { + if ("targetNamespace".equals(attr.getName())) { + target = attr.getValue(); + } + else if (attr.getName().startsWith("xmlns")) { + ns.add(attr.getValue()); + } + } + + ns.remove(target); + if (target != null) { + myNamespace.setText(target); + } + mySkipSchemas.setText(StringUtil.join(ArrayUtil.toStringArray(ns), "\n")); + } + } + } + } + } + } + }); + 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(); + } + + private static final String PREFIX = "DomGenPanel."; + public void restore() { + myNamespace.setText(getValue("namespace", "")); + myPackage.setText(getValue("package", "com.intellij.myframework.model")); + mySchemaLocation.setText(getValue("schemaLocation", "")); + mySuperClass.setText(getValue("superClass", "com.intellij.util.xml.DomElement")); + myOutputDir.setText(getValue("output", "")); + mySkipSchemas.setText(getValue("skipSchemas", "http://www.w3.org/2001/XMLSchema\nhttp://www.w3.org/2001/XMLSchema-instance")); + myAuthor.setText(getValue("author", "")); + } + + private static String getValue(String name, String defaultValue) { + return PropertiesComponent.getInstance().getOrInit(PREFIX + name, defaultValue); + } + + private static void setValue(String name, String value) { + PropertiesComponent.getInstance().setValue(PREFIX + name, value); + } + + public void saveAll() { + setValue("namespace", myNamespace.getText()); + setValue("package", myPackage.getText()); + setValue("schemaLocation", mySchemaLocation.getText()); + setValue("superClass", mySuperClass.getText()); + setValue("output", myOutputDir.getText()); + setValue("skipSchemas", mySkipSchemas.getText()); + setValue("author", myAuthor.getText()); + } + + public boolean validate() { + if (!new File(mySchemaLocation.getText()).exists()) { + Messages.showErrorDialog(myProject, "Schema location doesn't exist", "Error"); + IdeFocusManager.getInstance(myProject).requestFocus(mySchemaLocation, true); + return false; + } + + if (!new File(myOutputDir.getText()).exists()) { + Messages.showErrorDialog(myProject, "Output dir doesn't exist", "Error"); + IdeFocusManager.getInstance(myProject).requestFocus(myOutputDir, true); + return false; + } + + return true; + } + + public String[] getSkippedSchemas() { + final String schemes = mySkipSchemas.getText().replaceAll("\r\n", "\n").trim(); + if (schemes.length() > 0) { + return schemes.split("\n"); + } + return new String[0]; + } + + public String getAuthor() { + return myAuthor.getText(); + } +} diff --git a/plugins/devkit/src/dom/generator/Emitter.java b/plugins/devkit/src/dom/generator/Emitter.java new file mode 100644 index 000000000000..4bc212f8a242 --- /dev/null +++ b/plugins/devkit/src/dom/generator/Emitter.java @@ -0,0 +1,36 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import java.io.File; + +/** + * @author Konstantin Bulenkov + */ +public interface Emitter { + String JDOC_OPEN = "/**"; + String JDOC_CONT = " * "; + String JDOC_CLOSE = " */"; + + void emit(FileManager fileManager, ModelDesc model, File outputRoot); +} diff --git a/plugins/devkit/src/dom/generator/FieldDesc.java b/plugins/devkit/src/dom/generator/FieldDesc.java new file mode 100644 index 000000000000..13f3bf6e065f --- /dev/null +++ b/plugins/devkit/src/dom/generator/FieldDesc.java @@ -0,0 +1,78 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +/** + * @author Konstantin Bulenkov + */ +public class FieldDesc implements Comparable<FieldDesc> { + 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/plugins/devkit/src/dom/generator/FileManager.java b/plugins/devkit/src/dom/generator/FileManager.java new file mode 100644 index 000000000000..8ad5c711a6c4 --- /dev/null +++ b/plugins/devkit/src/dom/generator/FileManager.java @@ -0,0 +1,33 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import java.io.File; + +/** + * @author Konstantin Bulenkov + */ +public interface FileManager { + public File releaseOutputFile(File outFile); + public File getOutputFile(File target); +} diff --git a/plugins/devkit/src/dom/generator/GenerateDomModelAction.java b/plugins/devkit/src/dom/generator/GenerateDomModelAction.java new file mode 100644 index 000000000000..fb8efd87244c --- /dev/null +++ b/plugins/devkit/src/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 org.jetbrains.idea.devkit.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/plugins/devkit/src/dom/generator/JetBrainsEmitter.java b/plugins/devkit/src/dom/generator/JetBrainsEmitter.java new file mode 100644 index 000000000000..bf3c3bc35565 --- /dev/null +++ b/plugins/devkit/src/dom/generator/JetBrainsEmitter.java @@ -0,0 +1,679 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.*; + +/** + * @author Gregory.Shrago + * @author Konstantin Bulenkov + */ +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; + private String AUTHOR = null; + + + public void emit(FileManager fileManager, ModelDesc model, File outputRoot) { + final NamespaceDesc nsdDef = model.nsdMap.get(""); + final Set<String> simpleTypes = new TreeSet<String>(); + 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<String> simpleTypes) { + final Map<String, TypeDesc> jtMap = model.jtMap; + final Map<String, NamespaceDesc> 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<String> externalClasses = new TreeSet<String>(); + 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"); + externalClasses.add("com.intellij.util.xml.*"); + } + } + + 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<String, FieldDesc> 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."); + if (AUTHOR != null) out.println(JDOC_CONT + AUTHOR); + 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<String, FieldDesc> 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); + if (AUTHOR != null) out.println(JDOC_CONT + AUTHOR); + 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<FieldDesc>() { + public int compare(FieldDesc o1, FieldDesc o2) { + return o1.realIndex - o2.realIndex; + } + }); + out.println(""); + for (FieldDesc field : fields) { + String tagName = field.tagName; + String type = field.type; + String elementType = field.elementType; + String name = field.name; + String paramName = toJavaIdName(field.clType > 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<Boolean>"; + 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@Attribute (\"").append(tagName).append("\")"); + } + else if (isList) { + // sbAnnotations.append((JB_OFF ? "//" : "") +"\t@SubTagList (\"" + tagName + "\")"); + if (nameChanged) { + sbAnnotations.append((JB_OFF ? "//" : "") + "\t@SubTag (\"").append(tagName).append("\")"); + } + else { + if (isBadTagName(tagName)) { + sbAnnotations.append((JB_OFF ? "//" : "") + "\t@SubTagList (\"").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@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("();"); + } + + out.println(""); + out.println(""); + } + out.println("}"); + } + catch (IOException ex) { + ex.printStackTrace(); + } + finally { + try { + out.close(); + } + catch (Exception ex) { + } + fileManager.releaseOutputFile(outFile); + } + } + + private static boolean isBadTagName(String tagName) { + if (Character.isUpperCase(tagName.charAt(0))) return false; + final char[] chars = tagName.toCharArray(); + for (int i = 1; i < chars.length; i++) { + if (Character.isUpperCase(chars[i])) { + return true; + } + } + return false; + } + + 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."); + if (AUTHOR != null) out.println(JDOC_CONT + AUTHOR); + 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<String, TypeDesc> jtMap = model.jtMap; + final Map<String, NamespaceDesc> nsdMap = model.nsdMap; + if (nsd.helperClass == null || nsd.helperClass.length() == 0) return; + ArrayList<TypeDesc> jtList = new ArrayList<TypeDesc>(); + 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."); + if (AUTHOR != null) out.println(JDOC_CONT + AUTHOR); + out.println(JDOC_CLOSE); + out.print("public class " + typeName + " "); + out.println("{"); + out.println(""); + out.println("\tprivate interface GetName { String getName(Object o); }"); + out.println("\tprivate static java.util.HashMap<Class, GetName> nameMap = new java.util.HashMap();"); + out.println("\tstatic {"); + + for (TypeDesc td : jtList) { + ArrayList<FieldDesc> fields = new ArrayList<FieldDesc>(td.fdMap.values()); + Collections.sort(fields, new Comparator<FieldDesc>() { + 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; + } + + + public void setAuthor(String author) { + AUTHOR = "@author " + author; + } +} diff --git a/plugins/devkit/src/dom/generator/Main.java b/plugins/devkit/src/dom/generator/Main.java new file mode 100644 index 000000000000..e164a89dcbff --- /dev/null +++ b/plugins/devkit/src/dom/generator/Main.java @@ -0,0 +1,61 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import java.io.File; + +/** + * @author Gregory.Shrago + * @author Konstantin Bulenkov + */ +public class Main { + + + public static void main(String argv[]) throws Exception { + if (argv.length != 4) { + System.out.println("Usage: Main <XSD or DTD> <input folder> <output folder> <config xml>"); + } + 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/plugins/devkit/src/dom/generator/MergingFileManager.java b/plugins/devkit/src/dom/generator/MergingFileManager.java new file mode 100644 index 000000000000..1076eedb9d06 --- /dev/null +++ b/plugins/devkit/src/dom/generator/MergingFileManager.java @@ -0,0 +1,248 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import com.intellij.util.ArrayUtil; + +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.TreeSet; + +/** + * @author Gregory.Shrago + * @author Konstantin Bulenkov + */ +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<String> merged = new ArrayList<String>(); + 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 = ArrayUtil.toStringArray(merged); + 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<String> merged, String[] curLines, String[] prevLines, int[] indices) { + TreeSet<String> externalClasses = new TreeSet<String>(); + 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<String> 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 ArrayUtil.EMPTY_STRING_ARRAY; + ArrayList<String> list = new ArrayList<String>(); + 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 ArrayUtil.toStringArray(list); + } + +} diff --git a/plugins/devkit/src/dom/generator/ModelDesc.java b/plugins/devkit/src/dom/generator/ModelDesc.java new file mode 100644 index 000000000000..e940fd24b733 --- /dev/null +++ b/plugins/devkit/src/dom/generator/ModelDesc.java @@ -0,0 +1,171 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import org.apache.xerces.xs.XSObject; + +import javax.xml.namespace.QName; +import java.io.PrintWriter; +import java.util.*; + +/** + * @author Gregory.Shrago + * @author Konstantin Bulenkov + */ +public class ModelDesc { + final Map<String, String> name2replaceMap = new HashMap<String, String>(); + final Map<QName, String> qname2FileMap = new HashMap<QName, String>(); + final Map<String, NamespaceDesc> nsdMap = new HashMap<String, NamespaceDesc>(); + final Map<String, TypeDesc> jtMap = new TreeMap<String, TypeDesc>(); + + + 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 == null || !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<String, NamespaceDesc> 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<String, NamespaceDesc> entry : nsdMap.entrySet()) { + out.println("namespace key: "+entry.getKey()); + dumpNamespace(entry.getValue(), out); + } + out.println("-- jtMap ---"); + for (Map.Entry<String, TypeDesc> entry : jtMap.entrySet()) { + out.println("type key: "+entry.getKey()); + dumpTypeDesc(entry.getValue(), out); + } + } + + private void dumpTypeDesc(TypeDesc td, PrintWriter out) { + final ArrayList<String> superList; + if (td.supers !=null) { + superList = new ArrayList<String>(); + 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<String, FieldDesc> 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/plugins/devkit/src/dom/generator/ModelGen.java b/plugins/devkit/src/dom/generator/ModelGen.java new file mode 100644 index 000000000000..8f1f709a6906 --- /dev/null +++ b/plugins/devkit/src/dom/generator/ModelGen.java @@ -0,0 +1,189 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.ContainerUtil; +import org.apache.xerces.xni.XMLResourceIdentifier; +import org.apache.xerces.xni.XNIException; +import org.apache.xerces.xni.parser.XMLEntityResolver; +import org.apache.xerces.xni.parser.XMLInputSource; +import org.jdom.Document; +import org.jdom.Element; +import org.jdom.input.SAXBuilder; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import java.io.CharArrayReader; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Gregory.Shrago + * @author Konstantin Bulenkov + */ +public class ModelGen { + private final ModelDesc model = new ModelDesc(); + private final Map<String, String> schemaLocationMap = new HashMap<String, String>(); + 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, String... schemasToSkip) { + schemaLocationMap.put(schema, location); + for (String sch : schemasToSkip) { + if (sch != null && sch.length() > 0) { + model.nsdMap.put(sch, new NamespaceDesc(sch)); + } + } + 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<Element>) namespaceEl.getChildren("schemaLocation")) { + final String name = e.getAttributeValue("name"); + final String file = e.getAttributeValue("file"); + schemaLocationMap.put(name, file); + } + for (Element e : (List<Element>) 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<Element>) 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<String> list = new ArrayList<String>(); + for (Element pkgElement : (List<Element>) 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<Element>) 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 = ArrayUtil.toStringArray(list); + 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; + if (root.isDirectory()) { + final String fileName = esid.substring(esid.lastIndexOf('/') + 1); + f = new File(root, fileName); + } else { + f = root; + } + } + if (f == null || !f.exists()) { + Util.logerr("unable to resolve: " + esid); + return null; + } + esid = f.getPath(); + return new XMLInputSource(null, esid, null); + } + }; + ArrayList<File> files = new ArrayList<File>(); + for (File root : modelRoots) { + ContainerUtil.addAll(files, root.listFiles()); + } + loader.loadModel(model, files, resolver); + Util.log(model.jtMap.size() + " java types loaded"); + } + +} diff --git a/plugins/devkit/src/dom/generator/ModelLoader.java b/plugins/devkit/src/dom/generator/ModelLoader.java new file mode 100644 index 000000000000..cf71534b473d --- /dev/null +++ b/plugins/devkit/src/dom/generator/ModelLoader.java @@ -0,0 +1,28 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import org.apache.xerces.xni.parser.XMLEntityResolver; + +import java.io.File; +import java.util.Collection; + +/** + * @author Konstantin Bulenkov + */ +public interface ModelLoader { + void loadModel(ModelDesc model, Collection<File> files, XMLEntityResolver resolver) throws Exception; +} diff --git a/plugins/devkit/src/dom/generator/NamespaceDesc.java b/plugins/devkit/src/dom/generator/NamespaceDesc.java new file mode 100644 index 000000000000..f73d16ad2cee --- /dev/null +++ b/plugins/devkit/src/dom/generator/NamespaceDesc.java @@ -0,0 +1,84 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import java.util.Map; +import java.util.HashMap; + +/** + * @author Konstantin Bulenkov + */ +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<String, String> props = new HashMap<String, String>(); + 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/plugins/devkit/src/dom/generator/TypeDesc.java b/plugins/devkit/src/dom/generator/TypeDesc.java new file mode 100644 index 000000000000..c644fd73124a --- /dev/null +++ b/plugins/devkit/src/dom/generator/TypeDesc.java @@ -0,0 +1,55 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import java.util.Map; +import java.util.TreeMap; + +/** + * @author Konstantin Bulenkov + */ +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<String, FieldDesc> fdMap = new TreeMap<String, FieldDesc>(); + boolean duplicates; + String documentation; + TypeDesc[] supers; + + public String toString() { + return (type == TypeEnum.ENUM ? "enum" : "type") + ": " + name + ";" + xsName + ";"; + } +} diff --git a/plugins/devkit/src/dom/generator/Util.java b/plugins/devkit/src/dom/generator/Util.java new file mode 100644 index 000000000000..a393bb143602 --- /dev/null +++ b/plugins/devkit/src/dom/generator/Util.java @@ -0,0 +1,224 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import org.apache.xerces.xs.XSObject; + +import java.util.HashMap; +import java.util.Map; +import java.util.StringTokenizer; + + +/** + * @author Konstantin Bulenkov + */ +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<String, String> RESERVED_NAMES_MAP; + + static { + RESERVED_NAMES_MAP = new HashMap<String, String>(); + 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<String, FieldDesc> 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<String, String> 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<str.length(); i++) { + final char ch = str.charAt(i); + switch (state) { + case 0: + if (ch == '$') state = 1; + else result.append(ch); + break; + case 1: + if (ch == '{') { + state = 2; + variable.setLength(0); + } + else { + state = 0; + result.append('$').append(ch); + } + break; + case 2: + if (ch == '}') { + final String value = map.get(variable.toString()); + result.append(value == null? variable : value); + state = 0; + } + else { + variable.append(ch); + } + break; + } + } + return result.toString(); + } +} + diff --git a/plugins/devkit/src/dom/generator/XSDModelLoader.java b/plugins/devkit/src/dom/generator/XSDModelLoader.java new file mode 100644 index 000000000000..b3e25e0dbaf6 --- /dev/null +++ b/plugins/devkit/src/dom/generator/XSDModelLoader.java @@ -0,0 +1,681 @@ +/* + * 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 org.jetbrains.idea.devkit.dom.generator; + +import org.apache.xerces.xs.*; +import org.apache.xerces.impl.xs.XSComplexTypeDecl; +import org.apache.xerces.impl.xs.XSParticleDecl; +import org.apache.xerces.impl.xs.XSAttributeGroupDecl; +import org.apache.xerces.impl.xs.XSElementDecl; +import org.apache.xerces.impl.xs.util.XSObjectListImpl; +import org.apache.xerces.impl.dv.XSSimpleType; +import org.apache.xerces.impl.dv.xs.XSSimpleTypeDecl; +import org.apache.xerces.xni.parser.XMLEntityResolver; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.DOMConfiguration; +import org.w3c.dom.DOMErrorHandler; +import org.w3c.dom.DOMError; +import org.w3c.dom.TypeInfo; + +import javax.xml.namespace.QName; +import java.io.File; +import java.util.*; + +/** + * @author Gregory.Shrago + * @author Konstantin Bulenkov + */ +public class XSDModelLoader implements ModelLoader { + private static final boolean TEXT_ELEMENTS_ARE_COMPLEX = false; + + + private ModelDesc model; + + public void loadModel(ModelDesc model, Collection<File> 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<File> schemas, XMLEntityResolver resolver) throws Exception { + Map<String, NamespaceDesc> nsdMap = model.nsdMap; + Map<String, TypeDesc> jtMap = model.jtMap; + final NamespaceDesc nsdDef = nsdMap.get(""); + final ArrayList<XSModel> models = new ArrayList<XSModel>(); + final HashMap<String, XSTypeDefinition> types = new HashMap<String, XSTypeDefinition>(); + 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<XSTypeDefinition> toProcess = new ArrayList<XSTypeDefinition>(types.values()); + ArrayList<XSComplexTypeDefinition> toAdd = new ArrayList<XSComplexTypeDefinition>(); + for (ListIterator<XSTypeDefinition> 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<String, TypeDesc> jtMap, Map<String, NamespaceDesc> 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<XSModel> models, Map<String, TypeDesc> jtMap, Map<String, NamespaceDesc> nsdMap, ArrayList<XSComplexTypeDefinition> 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<PEntry> plist = new LinkedList<PEntry>(); + 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()) && et.getNamespace() != null) { + 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<XSModel> models, Map<String, TypeDesc> jtMap, Map<String, NamespaceDesc> 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<PEntry> plist = new LinkedList<PEntry>(); + 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<PEntry> plist, Map<String, NamespaceDesc> nsdMap, Map<String, TypeDesc> jtMap, TypeDesc td, List<XSModel> models, ArrayList<XSComplexTypeDefinition> toAdd, TypeDesc baseClass) { + final boolean globalMerge = jtMap.containsKey(model.toJavaQualifiedTypeName(def, nsdMap, td.type == TypeDesc.TypeEnum.ENUM)); + final HashMap<XSParticle, String> globalChoice = new HashMap<XSParticle, String>(); + final ArrayList<XSObjectList> choiceList = new ArrayList<XSObjectList>(); + final ArrayList<TypeDesc> supers = new ArrayList<TypeDesc>(); + 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 (et.getBaseType() != null && + ("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<FieldDesc> it = td.fdMap.values().iterator(); it.hasNext(); i++) { + FieldDesc fd = it.next(); + fd.idx = i; + } + for (XSObjectList l : choiceList) { + final ArrayList<XSParticle> clist = new ArrayList<XSParticle>(); + final LinkedList<XSParticle> elist = new LinkedList<XSParticle>(); + 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); + } + } + + if (fd != null) { + choice[i] = fd; + choice[i].choice = choice; + if (fd.required) choiceOpt = false; + } + } + for (i = 0; i < choice.length; i++) { + if (choice[i] != null) { + 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("<documentation"); + if (idx == -1) return null; + idx = str.indexOf(">", idx + 1); + if (idx == -1) return null; + int idx2 = str.indexOf(":documentation", idx + 1); + if (idx2 == -1) idx2 = str.indexOf("</documentation", idx + 1); + idx2 = str.lastIndexOf("<", idx2 + 1); + str = str.substring(idx + 1, idx2).trim(); + + idx = str.indexOf("<![CDATA["); + if (idx > -1) { + idx = str.indexOf("[", idx + 3); + idx2 = str.indexOf("]]>", idx + 1); + str = str.substring(idx + 1, idx2); + } + return "<pre>\n<h3>" + title + "</h3>\n" + str + "\n</pre>"; + } + + public String toJavaTypeName(XSObject xs, Map<String, NamespaceDesc> 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; + } + +} diff --git a/plugins/devkit/src/dom/impl/InspectionsPropertiesReferenceProviderContributor.java b/plugins/devkit/src/dom/impl/InspectionsPropertiesReferenceProviderContributor.java index 662e78840841..de5ab53b5dab 100644 --- a/plugins/devkit/src/dom/impl/InspectionsPropertiesReferenceProviderContributor.java +++ b/plugins/devkit/src/dom/impl/InspectionsPropertiesReferenceProviderContributor.java @@ -30,8 +30,8 @@ public class InspectionsPropertiesReferenceProviderContributor extends PsiRefere @Override public void registerReferenceProviders(PsiReferenceRegistrar registrar) { ElementPattern pattern = XmlPatterns.xmlAttributeValue() - .withParent(XmlPatterns.xmlAttribute().withLocalName(string().oneOf("key", "groupKey")) - .withParent(XmlPatterns.xmlTag().withName(string().oneOf("localInspection", "globalInspection")) + .withParent(XmlPatterns.xmlAttribute().withLocalName("key", "groupKey") + .withParent(XmlPatterns.xmlTag().withName("localInspection", "globalInspection") .withSuperParent(2, XmlPatterns.xmlTag().withName("idea-plugin")))); registrar.registerReferenceProvider(pattern, new InspectionsKeyPropertiesReferenceProvider(false), PsiReferenceRegistrar.DEFAULT_PRIORITY); diff --git a/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java b/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java index cf629df6796f..2c1aec6f72c7 100644 --- a/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java +++ b/plugins/devkit/src/inspections/quickfix/RegisterInspectionFix.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 JetBrains s.r.o. + * Copyright 2000-2013 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. @@ -20,7 +20,6 @@ import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.InspectionEP; import com.intellij.ide.TypePresentationService; -import com.intellij.ide.plugins.PluginManager; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Editor; @@ -32,7 +31,6 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; @@ -125,6 +123,12 @@ class RegisterInspectionFix implements IntentionAction { final BaseListPopupStep<DomFileElement<IdeaPlugin>> popupStep = new BaseListPopupStep<DomFileElement<IdeaPlugin>>("Choose Plugin Descriptor", elements) { + + @Override + public boolean isSpeedSearchEnabled() { + return true; + } + @Override public Icon getIconFor(DomFileElement<IdeaPlugin> aValue) { return TypePresentationService.getService().getIcon(aValue); @@ -134,11 +138,8 @@ class RegisterInspectionFix implements IntentionAction { @Override public String getTextFor(DomFileElement<IdeaPlugin> value) { final String name = value.getFile().getName(); - if (!Comparing.equal(PluginManager.PLUGIN_XML, name)) { - return name; - } final Module module = value.getModule(); - return module != null ? name + " (" + module.getName() + ")" : name; + return module != null ? name + " [" + module.getName() + "]" : name; } @Override |