| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.netbeans.modules.java.freeform; |
| |
| import java.io.File; |
| import java.net.MalformedURLException; |
| import java.util.ArrayList; |
| import java.util.EnumSet; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import org.netbeans.api.java.project.JavaProjectConstants; |
| import org.netbeans.api.java.queries.AnnotationProcessingQuery; |
| import org.netbeans.api.project.ant.AntArtifact; |
| import org.netbeans.api.project.ant.AntArtifactQuery; |
| import org.netbeans.modules.ant.freeform.spi.support.Util; |
| import org.netbeans.spi.project.AuxiliaryConfiguration; |
| import org.netbeans.spi.project.support.ant.AntProjectHelper; |
| import org.netbeans.spi.project.support.ant.EditableProperties; |
| import org.netbeans.spi.project.support.ant.PropertyEvaluator; |
| import org.netbeans.spi.project.support.ant.PropertyUtils; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.modules.SpecificationVersion; |
| import org.openide.util.Exceptions; |
| import org.openide.util.Utilities; |
| import org.openide.xml.XMLUtil; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| |
| /** |
| * Reads/writes project.xml. |
| * Handling of /1 vs. /2 namespace: either namespace can be read; |
| * when writing, attempts to keep existing namespace when possible, but |
| * will always write a /2 namespace when it is necessary (for isTests or javadoc). |
| * @author Jesse Glick, David Konecny, Pavel Buzek |
| */ |
| public class JavaProjectGenerator { |
| |
| /** Keep root elements in the order specified by project's XML schema. */ |
| private static final String[] rootElementsOrder = new String[]{"name", "properties", "folders", "ide-actions", "export", "view", "subprojects", "project-license"}; // NOI18N |
| private static final String[] viewElementsOrder = new String[]{"items", "context-menu"}; // NOI18N |
| |
| // this order is not required by schema, but follow it to minimize randomness a bit |
| private static final String[] folderElementsOrder = new String[]{"source-folder", "build-folder", "build-file"}; // NOI18N |
| private static final String[] viewItemElementsOrder = new String[]{"source-folder", "source-file"}; // NOI18N |
| |
| /** |
| * Structure describing source folder. |
| * Data in the struct are in the same format as they are stored in XML. |
| * Beware that when used in <folders> you must specify label, location, and optional type; |
| * in <view><items>, you must specify label, location, and style. So if you are switching |
| * from the latter to the former you must add style; if vice-versa, you may need to add type. |
| */ |
| public static final class SourceFolder { |
| public SourceFolder() {} |
| public String label; |
| public String type; |
| public String location; |
| public String style; |
| public String includes; |
| public String excludes; |
| public String encoding; |
| public String toString() { |
| return "FPG.SF[label=" + label + ",type=" + type + ",location=" + location + ",style=" + style + ",includes=" + includes + ",excludes=" + excludes + ",encoding=" + encoding + "]"; // NOI18N |
| } |
| } |
| |
| /** |
| * Read source folders from the project. |
| * @param helper AntProjectHelper instance |
| * @param type type of source folders to be read. Can be null in which case |
| * all types will be read. Useful for reading one type of source folders. |
| * Source folders without type are read only when type == null. |
| * @return list of SourceFolder instances; style value will be always null |
| */ |
| public static List<SourceFolder> getSourceFolders(AntProjectHelper helper, String type) { |
| //assert ProjectManager.mutex().isReadAccess() || ProjectManager.mutex().isWriteAccess(); |
| List<SourceFolder> list = new ArrayList<SourceFolder>(); |
| Element data = Util.getPrimaryConfigurationData(helper); |
| Element foldersEl = XMLUtil.findElement(data, "folders", Util.NAMESPACE); // NOI18N |
| if (foldersEl == null) { |
| return list; |
| } |
| for (Element sourceFolderEl : XMLUtil.findSubElements(foldersEl)) { |
| if (!sourceFolderEl.getLocalName().equals("source-folder")) { // NOI18N |
| continue; |
| } |
| SourceFolder sf = new SourceFolder(); |
| Element el = XMLUtil.findElement(sourceFolderEl, "label", Util.NAMESPACE); // NOI18N |
| if (el != null) { |
| sf.label = XMLUtil.findText(el); |
| } |
| el = XMLUtil.findElement(sourceFolderEl, "type", Util.NAMESPACE); // NOI18N |
| if (el != null) { |
| sf.type = XMLUtil.findText(el); |
| } |
| el = XMLUtil.findElement(sourceFolderEl, "location", Util.NAMESPACE); // NOI18N |
| if (el != null) { |
| sf.location = XMLUtil.findText(el); |
| } |
| el = XMLUtil.findElement(sourceFolderEl, "includes", Util.NAMESPACE); // NOI18N |
| if (el != null) { |
| sf.includes = XMLUtil.findText(el); |
| } |
| el = XMLUtil.findElement(sourceFolderEl, "excludes", Util.NAMESPACE); // NOI18N |
| if (el != null) { |
| sf.excludes = XMLUtil.findText(el); |
| } |
| el = XMLUtil.findElement(sourceFolderEl, "encoding", Util.NAMESPACE); // NOI18N |
| if (el != null) { |
| sf.encoding = XMLUtil.findText(el); |
| } |
| if (type == null || type.equals(sf.type)) { |
| if (sf.label == null || sf.label.length() == 0) { |
| throw new IllegalArgumentException("label element is empty or not specified. "+helper.getProjectDirectory()); // NOI18N |
| } |
| if (sf.location == null || sf.location.length() == 0) { |
| throw new IllegalArgumentException("location element is empty or not specified. "+helper.getProjectDirectory()); // NOI18N |
| } |
| list.add(sf); |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * Update source folders of the project. Project is left modified and you |
| * must save it explicitely. |
| * @param helper AntProjectHelper instance |
| * @param sources list of SourceFolder instances |
| * @param type type of source folders to update. |
| * Can be null in which case all types will be overriden. |
| * Useful for overriding just one type of source folders. Source folders |
| * without type are overriden only when type == null. |
| */ |
| public static void putSourceFolders(AntProjectHelper helper, List<SourceFolder> sources, String type) { |
| //assert ProjectManager.mutex().isWriteAccess(); |
| Element data = Util.getPrimaryConfigurationData(helper); |
| Document doc = data.getOwnerDocument(); |
| Element foldersEl = XMLUtil.findElement(data, "folders", Util.NAMESPACE); // NOI18N |
| if (foldersEl == null) { |
| foldersEl = doc.createElementNS(Util.NAMESPACE, "folders"); // NOI18N |
| XMLUtil.appendChildElement(data, foldersEl, rootElementsOrder); |
| } else { |
| for (Element sourceFolderEl : XMLUtil.findSubElements(foldersEl)) { |
| if (!sourceFolderEl.getLocalName().equals("source-folder")) { // NOI18N |
| continue; |
| } |
| if (type == null) { |
| foldersEl.removeChild(sourceFolderEl); |
| } else { |
| Element typeEl = XMLUtil.findElement(sourceFolderEl, "type", Util.NAMESPACE); // NOI18N |
| if (typeEl != null) { |
| String typeElValue = XMLUtil.findText(typeEl); |
| if (type.equals(typeElValue)) { |
| foldersEl.removeChild(sourceFolderEl); |
| } |
| } |
| } |
| } |
| } |
| for (SourceFolder sf : sources) { |
| Element sourceFolderEl = doc.createElementNS(Util.NAMESPACE, "source-folder"); // NOI18N |
| Element el; |
| if (sf.label != null && sf.label.length() > 0) { |
| el = doc.createElementNS(Util.NAMESPACE, "label"); // NOI18N |
| el.appendChild(doc.createTextNode(sf.label)); // NOI18N |
| sourceFolderEl.appendChild(el); |
| } else { |
| throw new IllegalArgumentException("label cannot be empty. "+helper.getProjectDirectory()); // NOI18N |
| } |
| if (sf.type != null) { |
| el = doc.createElementNS(Util.NAMESPACE, "type"); // NOI18N |
| el.appendChild(doc.createTextNode(sf.type)); // NOI18N |
| sourceFolderEl.appendChild(el); |
| } |
| if (sf.location != null && sf.location.length() > 0) { |
| el = doc.createElementNS(Util.NAMESPACE, "location"); // NOI18N |
| el.appendChild(doc.createTextNode(sf.location)); // NOI18N |
| sourceFolderEl.appendChild(el); |
| } else { |
| throw new IllegalArgumentException("location cannot be empty. "+helper.getProjectDirectory()); // NOI18N |
| } |
| if (sf.includes != null) { |
| el = doc.createElementNS(Util.NAMESPACE, "includes"); // NOI18N |
| el.appendChild(doc.createTextNode(sf.includes)); // NOI18N |
| sourceFolderEl.appendChild(el); |
| } |
| if (sf.excludes != null) { |
| el = doc.createElementNS(Util.NAMESPACE, "excludes"); // NOI18N |
| el.appendChild(doc.createTextNode(sf.excludes)); // NOI18N |
| sourceFolderEl.appendChild(el); |
| } |
| if (sf.encoding != null) { |
| el = doc.createElementNS(Util.NAMESPACE, "encoding"); // NOI18N |
| el.appendChild(doc.createTextNode(sf.encoding)); // NOI18N |
| sourceFolderEl.appendChild(el); |
| } |
| XMLUtil.appendChildElement(foldersEl, sourceFolderEl, folderElementsOrder); |
| } |
| Util.putPrimaryConfigurationData(helper, data); |
| } |
| |
| /** |
| * Read source views from the project. At the moment only source-folder |
| * elements are read and source-file ones are ignored. |
| * @param helper AntProjectHelper instance |
| * @param style style of source folders to be read. Can be null in which case |
| * all styles will be read. Useful for reading one style of source folders. |
| * @return list of SourceFolder instances; type value will be always null |
| */ |
| public static List getSourceViews(AntProjectHelper helper, String style) { |
| //assert ProjectManager.mutex().isReadAccess() || ProjectManager.mutex().isWriteAccess(); |
| List<SourceFolder> list = new ArrayList<SourceFolder>(); |
| Element data = Util.getPrimaryConfigurationData(helper); |
| Element viewEl = XMLUtil.findElement(data, "view", Util.NAMESPACE); // NOI18N |
| if (viewEl == null) { |
| return list; |
| } |
| Element itemsEl = XMLUtil.findElement(viewEl, "items", Util.NAMESPACE); // NOI18N |
| if (itemsEl == null) { |
| return list; |
| } |
| for (Element sourceFolderEl : XMLUtil.findSubElements(itemsEl)) { |
| if (!sourceFolderEl.getLocalName().equals("source-folder")) { // NOI18N |
| continue; |
| } |
| SourceFolder sf = new SourceFolder(); |
| sf.style = sourceFolderEl.getAttribute("style"); // NOI18N |
| assert sf.style != null && sf.style.length() > 0 : "Bad style attr on <source-folder> in " + helper; // NOI18N |
| Element el = XMLUtil.findElement(sourceFolderEl, "label", Util.NAMESPACE); // NOI18N |
| if (el != null) { |
| sf.label = XMLUtil.findText(el); |
| } |
| el = XMLUtil.findElement(sourceFolderEl, "location", Util.NAMESPACE); // NOI18N |
| if (el != null) { |
| sf.location = XMLUtil.findText(el); |
| } |
| el = XMLUtil.findElement(sourceFolderEl, "includes", Util.NAMESPACE); // NOI18N |
| if (el != null) { |
| sf.includes = XMLUtil.findText(el); |
| } |
| el = XMLUtil.findElement(sourceFolderEl, "excludes", Util.NAMESPACE); // NOI18N |
| if (el != null) { |
| sf.excludes = XMLUtil.findText(el); |
| } |
| if (style == null || style.equals(sf.style)) { |
| list.add(sf); |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * Update source views of the project. |
| * This method should be called always after the putSourceFolders method |
| * to keep views and folders in sync. |
| * Project is left modified and you must save it explicitely. |
| * @param helper AntProjectHelper instance |
| * @param sources list of SourceFolder instances |
| * @param style style of source views to update. |
| * Can be null in which case all styles will be overriden. |
| * Useful for overriding just one style of source view. |
| */ |
| public static void putSourceViews(AntProjectHelper helper, List<SourceFolder> sources, String style) { |
| //assert ProjectManager.mutex().isWriteAccess(); |
| Element data = Util.getPrimaryConfigurationData(helper); |
| Document doc = data.getOwnerDocument(); |
| Element viewEl = XMLUtil.findElement(data, "view", Util.NAMESPACE); // NOI18N |
| if (viewEl == null) { |
| viewEl = doc.createElementNS(Util.NAMESPACE, "view"); // NOI18N |
| XMLUtil.appendChildElement(data, viewEl, rootElementsOrder); |
| } |
| Element itemsEl = XMLUtil.findElement(viewEl, "items", Util.NAMESPACE); // NOI18N |
| if (itemsEl == null) { |
| itemsEl = doc.createElementNS(Util.NAMESPACE, "items"); // NOI18N |
| XMLUtil.appendChildElement(viewEl, itemsEl, viewElementsOrder); |
| } |
| List<Element> sourceViews = XMLUtil.findSubElements(itemsEl); |
| for (Element sourceViewEl : sourceViews) { |
| if (!sourceViewEl.getLocalName().equals("source-folder")) { // NOI18N |
| continue; |
| } |
| String sourceStyle = sourceViewEl.getAttribute("style"); // NOI18N |
| if (style == null || style.equals(sourceStyle)) { |
| itemsEl.removeChild(sourceViewEl); |
| } |
| } |
| |
| for (SourceFolder sf : sources) { |
| if (sf.style == null || sf.style.length() == 0) { |
| // perhaps this is principal source folder? |
| continue; |
| } |
| Element sourceFolderEl = doc.createElementNS(Util.NAMESPACE, "source-folder"); // NOI18N |
| sourceFolderEl.setAttribute("style", sf.style); // NOI18N |
| Element el; |
| if (sf.label != null) { |
| el = doc.createElementNS(Util.NAMESPACE, "label"); // NOI18N |
| el.appendChild(doc.createTextNode(sf.label)); // NOI18N |
| sourceFolderEl.appendChild(el); |
| } |
| if (sf.location != null) { |
| el = doc.createElementNS(Util.NAMESPACE, "location"); // NOI18N |
| el.appendChild(doc.createTextNode(sf.location)); // NOI18N |
| sourceFolderEl.appendChild(el); |
| } |
| if (sf.includes != null) { |
| el = doc.createElementNS(Util.NAMESPACE, "includes"); // NOI18N |
| el.appendChild(doc.createTextNode(sf.includes)); // NOI18N |
| sourceFolderEl.appendChild(el); |
| } |
| if (sf.excludes != null) { |
| el = doc.createElementNS(Util.NAMESPACE, "excludes"); // NOI18N |
| el.appendChild(doc.createTextNode(sf.excludes)); // NOI18N |
| sourceFolderEl.appendChild(el); |
| } |
| XMLUtil.appendChildElement(itemsEl, sourceFolderEl, viewItemElementsOrder); |
| } |
| Util.putPrimaryConfigurationData(helper, data); |
| } |
| |
| /** |
| * Returns {@link Element} for {@link JavaCompilationUnit}. |
| * @param aux AuxiliaryConfiguration instance |
| * @return {@link Element} representing JavaCompilationUnit instances or null |
| */ |
| public static Element getJavaCompilationUnits (final AuxiliaryConfiguration aux) { |
| for (String ns : JavaProjectNature.JAVA_NAMESPACES) { |
| Element data = aux.getConfigurationFragment(JavaProjectNature.EL_JAVA, ns, true); |
| if (data != null) return data; |
| } |
| return null; |
| } |
| |
| /** |
| * Read Java compilation units from the project. |
| * @param helper AntProjectHelper instance |
| * @param aux AuxiliaryConfiguration instance |
| * @return list of JavaCompilationUnit instances; never null; |
| */ |
| public static List<JavaCompilationUnit> getJavaCompilationUnits( |
| AntProjectHelper helper, AuxiliaryConfiguration aux) { |
| //assert ProjectManager.mutex().isReadAccess() || ProjectManager.mutex().isWriteAccess(); |
| List<JavaCompilationUnit> list = new ArrayList<JavaCompilationUnit>(); |
| final Element data = getJavaCompilationUnits(aux); |
| if (data == null) { |
| return list; |
| } |
| for (Element cuEl : XMLUtil.findSubElements(data)) { |
| JavaCompilationUnit cu = new JavaCompilationUnit(); |
| List<String> outputs = new ArrayList<String>(); |
| List<String> javadoc = new ArrayList<String>(); |
| List<JavaCompilationUnit.CP> cps = new ArrayList<JavaCompilationUnit.CP>(); |
| List<String> packageRoots = new ArrayList<String>(); |
| for (Element el : XMLUtil.findSubElements(cuEl)) { |
| if (el.getLocalName().equals("package-root")) { // NOI18N |
| packageRoots.add(XMLUtil.findText(el)); |
| continue; |
| } |
| if (el.getLocalName().equals("classpath")) { // NOI18N |
| JavaCompilationUnit.CP cp = new JavaCompilationUnit.CP(); |
| cp.classpath = XMLUtil.findText(el); |
| cp.mode = el.getAttribute("mode"); // NOI18N |
| if (cp.mode != null && cp.classpath != null) { |
| cps.add(cp); |
| } |
| continue; |
| } |
| if (el.getLocalName().equals("built-to")) { // NOI18N |
| outputs.add(XMLUtil.findText(el)); |
| continue; |
| } |
| if (el.getLocalName().equals("javadoc-built-to")) { // NOI18N |
| javadoc.add(XMLUtil.findText(el)); |
| continue; |
| } |
| if (el.getLocalName().equals("source-level")) { // NOI18N |
| cu.sourceLevel = XMLUtil.findText(el); |
| continue; |
| } |
| if (el.getLocalName().equals("unit-tests")) { // NOI18N |
| cu.isTests = true; |
| continue; |
| } |
| if ("annotation-processing".equals(el.getLocalName())&& //NOI18N |
| JavaProjectNature.namespaceAtLeast(el.getNamespaceURI(), JavaProjectNature.NS_JAVA_3)) { |
| cu.annotationPorocessing = new JavaCompilationUnit.AnnotationProcessing(); |
| cu.annotationPorocessing.trigger = EnumSet.<AnnotationProcessingQuery.Trigger>noneOf(AnnotationProcessingQuery.Trigger.class); |
| cu.annotationPorocessing.processors = new ArrayList<String>(); |
| cu.annotationPorocessing.processorParams = new LinkedHashMap<String, String>(); |
| for (Element apEl : XMLUtil.findSubElements(el)) { |
| final String localName = apEl.getLocalName(); |
| if ("scan-trigger".equals(localName)) { //NOI18N |
| cu.annotationPorocessing.trigger.add(AnnotationProcessingQuery.Trigger.ON_SCAN); |
| } else if ("editor-trigger".equals(localName)) { //NOI18N |
| cu.annotationPorocessing.trigger.add(AnnotationProcessingQuery.Trigger.IN_EDITOR); |
| } else if ("source-output".equals(localName)) { |
| cu.annotationPorocessing.sourceOutput = XMLUtil.findText(apEl); |
| } else if ("processor-path".equals(localName)) { //NOI18N |
| cu.annotationPorocessing.processorPath = XMLUtil.findText(apEl); |
| } else if ("processor".equals(localName)) { //NOI18N |
| cu.annotationPorocessing.processors.add(XMLUtil.findText(apEl)); |
| } else if ("processor-option".equals(localName)) { //NOI18N |
| final Element keyEl = XMLUtil.findElement(apEl, "key", el.getNamespaceURI()); //NOI18N |
| final Element valueEl = XMLUtil.findElement(apEl, "value", el.getNamespaceURI()); //NOI18N |
| if (keyEl != null && valueEl != null) { |
| final String key = XMLUtil.findText(keyEl); |
| final String value = XMLUtil.findText(valueEl); |
| if (key != null) { |
| cu.annotationPorocessing.processorParams.put(key, value); |
| } |
| } |
| } |
| } |
| } |
| } |
| cu.output = outputs.size() > 0 ? outputs : null; |
| cu.javadoc = javadoc.size() > 0 ? javadoc : null; |
| cu.classpath = cps.size() > 0 ? cps: null; |
| cu.packageRoots = packageRoots.size() > 0 ? packageRoots: null; |
| list.add(cu); |
| } |
| return list; |
| } |
| |
| /** |
| * Update Java compilation units of the project. Project is left modified |
| * and you must save it explicitely. |
| * @param helper AntProjectHelper instance |
| * @param aux AuxiliaryConfiguration instance |
| * @param compUnits list of JavaCompilationUnit instances |
| */ |
| public static void putJavaCompilationUnits(AntProjectHelper helper, |
| AuxiliaryConfiguration aux, List<JavaCompilationUnit> compUnits) { |
| //assert ProjectManager.mutex().isWriteAccess(); |
| int requiredVersion = 1; |
| |
| // detect minimal required namespace: |
| for (JavaCompilationUnit unit : compUnits) { |
| requiredVersion = Math.max(requiredVersion, minimalNS(unit)); |
| } |
| |
| String namespace; |
| |
| switch (requiredVersion) { |
| case 5: namespace = JavaProjectNature.NS_JAVA_5; break; |
| case 4: namespace = JavaProjectNature.NS_JAVA_4; break; |
| case 3: namespace = JavaProjectNature.NS_JAVA_3; break; |
| case 2: namespace = JavaProjectNature.NS_JAVA_2; break; |
| default: namespace = JavaProjectNature.NS_JAVA_1; break; |
| } |
| |
| Element data = getJavaCompilationUnits(aux); |
| |
| if (data == null || !JavaProjectNature.namespaceAtLeast(data.getNamespaceURI(), namespace)) { |
| if (data != null) { |
| aux.removeConfigurationFragment(JavaProjectNature.EL_JAVA, data.getNamespaceURI(), true); |
| } |
| data = Util.getPrimaryConfigurationData(helper).getOwnerDocument(). |
| createElementNS(namespace, JavaProjectNature.EL_JAVA); |
| } |
| Document doc = data.getOwnerDocument(); |
| for (Element cuEl : XMLUtil.findSubElements(data)) { |
| data.removeChild(cuEl); |
| } |
| for (JavaCompilationUnit cu : compUnits) { |
| Element cuEl = doc.createElementNS(data.getNamespaceURI(), "compilation-unit"); // NOI18N |
| data.appendChild(cuEl); |
| Element el; |
| if (cu.packageRoots != null) { |
| for (String packageRoot : cu.packageRoots) { |
| el = doc.createElementNS(data.getNamespaceURI(), "package-root"); // NOI18N |
| el.appendChild(doc.createTextNode(packageRoot)); |
| cuEl.appendChild(el); |
| } |
| } |
| if (cu.isTests) { |
| assert JavaProjectNature.namespaceAtLeast(namespace, JavaProjectNature.NS_JAVA_2); |
| cuEl.appendChild(doc.createElementNS(data.getNamespaceURI(), "unit-tests")); // NOI18N |
| } |
| if (cu.classpath != null) { |
| for (JavaCompilationUnit.CP cp : cu.classpath) { |
| el = doc.createElementNS(data.getNamespaceURI(), "classpath"); // NOI18N |
| el.appendChild(doc.createTextNode(cp.classpath)); |
| el.setAttribute("mode", cp.mode); // NOI18N |
| cuEl.appendChild(el); |
| } |
| } |
| if (cu.output != null) { |
| for (String output : cu.output) { |
| el = doc.createElementNS(data.getNamespaceURI(), "built-to"); // NOI18N |
| el.appendChild(doc.createTextNode(output)); |
| cuEl.appendChild(el); |
| } |
| } |
| if (cu.javadoc != null) { |
| for (String javadoc : cu.javadoc) { |
| assert JavaProjectNature.namespaceAtLeast(namespace, JavaProjectNature.NS_JAVA_2); |
| el = doc.createElementNS(data.getNamespaceURI(), "javadoc-built-to"); // NOI18N |
| el.appendChild(doc.createTextNode(javadoc)); |
| cuEl.appendChild(el); |
| } |
| } |
| if (cu.sourceLevel != null) { |
| el = doc.createElementNS(data.getNamespaceURI(), "source-level"); // NOI18N |
| el.appendChild(doc.createTextNode(cu.sourceLevel)); |
| cuEl.appendChild(el); |
| } |
| if (cu.annotationPorocessing != null) { |
| el = doc.createElementNS(data.getNamespaceURI(), "annotation-processing"); // NOI18N |
| if (cu.annotationPorocessing.trigger.contains(AnnotationProcessingQuery.Trigger.ON_SCAN)) { |
| el.appendChild(doc.createElementNS(data.getNamespaceURI(), "scan-trigger")); //NOI18N |
| } |
| if (cu.annotationPorocessing.trigger.contains(AnnotationProcessingQuery.Trigger.IN_EDITOR)) { |
| el.appendChild(doc.createElementNS(data.getNamespaceURI(), "editor-trigger")); //NOI18N |
| } |
| if (cu.annotationPorocessing.sourceOutput != null) { |
| final Element soElm = doc.createElementNS(data.getNamespaceURI(), "source-output"); //NOI18N |
| soElm.appendChild(doc.createTextNode(cu.annotationPorocessing.sourceOutput)); |
| el.appendChild(soElm); |
| } |
| if (cu.annotationPorocessing.processorPath != null) { |
| final Element ppElm = doc.createElementNS(data.getNamespaceURI(), "processor-path"); //NOI18N |
| ppElm.appendChild(doc.createTextNode(cu.annotationPorocessing.processorPath)); |
| el.appendChild(ppElm); |
| } |
| for (String processor : cu.annotationPorocessing.processors) { |
| final Element pElm = doc.createElementNS(data.getNamespaceURI(), "processor"); //NOI18N |
| pElm.appendChild(doc.createTextNode(processor)); |
| el.appendChild(pElm); |
| } |
| for (Map.Entry<String,String> option : cu.annotationPorocessing.processorParams.entrySet()) { |
| final Element poElm = doc.createElementNS(data.getNamespaceURI(), "processor-option"); //NOI18N |
| final Element keyElm = doc.createElementNS(data.getNamespaceURI(),"key"); //NOI18N |
| final Element valueElm = doc.createElementNS(data.getNamespaceURI(), "value"); //NOI18N |
| keyElm.appendChild(doc.createTextNode(option.getKey())); |
| if (option.getValue() != null) { |
| valueElm.appendChild(doc.createTextNode(option.getValue())); |
| } |
| poElm.appendChild(keyElm); |
| poElm.appendChild(valueElm); |
| el.appendChild(poElm); |
| } |
| cuEl.appendChild(el); |
| } |
| } |
| aux.putConfigurationFragment(data, true); |
| } |
| |
| /** |
| * Structure describing compilation unit. |
| * Data in the struct are in the same format as they are stored in XML. |
| */ |
| public static final class JavaCompilationUnit { |
| public List<String> packageRoots; |
| public List<CP> classpath; |
| public List<String> output; |
| public List<String> javadoc; |
| public String sourceLevel; |
| public boolean isTests; |
| public AnnotationProcessing annotationPorocessing; |
| |
| public String toString() { |
| return "FPG.JCU[packageRoots=" + packageRoots + ", classpath=" + classpath + ", output=" + output + ", javadoc=" + javadoc + ", sourceLevel=" + sourceLevel + ",isTests=" + isTests + "]"; // NOI18N |
| } |
| |
| public static final class CP { |
| public String classpath; |
| public String mode; |
| |
| public String toString() { |
| return "FPG.JCU.CP:[classpath="+classpath+", mode="+mode+", this="+super.toString()+"]"; // NOI18N |
| } |
| |
| } |
| |
| //@NotThreadSafe |
| public static final class AnnotationProcessing { |
| public Set<AnnotationProcessingQuery.Trigger> trigger; |
| public String sourceOutput; |
| public String processorPath; |
| public List<String> processors; |
| public Map<String,String> processorParams; |
| |
| @Override |
| public String toString() { |
| return String.format( |
| "Processors run: %s, source output %s, processor path: %s, processors: %s, processor options: %s", //NOI18N |
| trigger, |
| sourceOutput, |
| processorPath, |
| processors, |
| processorParams); |
| } |
| |
| |
| } |
| } |
| |
| /** |
| * Structure describing one export record. |
| * Data in the struct are in the same format as they are stored in XML. |
| */ |
| public static final class Export { |
| public String type; |
| public String location; |
| public String script; // optional |
| public String buildTarget; |
| public String cleanTarget; // optional |
| } |
| |
| /** |
| * Try to guess project's exports. See issue #49221 for more details. |
| */ |
| public static List<Export> guessExports(PropertyEvaluator evaluator, File baseFolder, |
| List<TargetMapping> targetMappings, List<JavaCompilationUnit> javaCompilationUnits) { |
| //assert ProjectManager.mutex().isReadAccess() || ProjectManager.mutex().isWriteAccess(); |
| List<Export> exports = new ArrayList<Export>(); |
| String targetName = null; |
| String scriptName = null; |
| for (TargetMapping tm : targetMappings) { |
| if (tm.name.equals("build")) { // NOI18N |
| if (tm.targets.size() == 1) { |
| targetName = tm.targets.get(0); |
| scriptName = tm.script; |
| } else { |
| return new ArrayList<Export>(); |
| } |
| } |
| } |
| if (targetName == null) { |
| return new ArrayList<Export>(); |
| } |
| for (JavaCompilationUnit cu : javaCompilationUnits) { |
| if (cu.output != null) { |
| for (String output : cu.output) { |
| String output2 = evaluator.evaluate(output); |
| if (output2.endsWith(".jar")) { // NOI18N |
| Export e = new Export(); |
| e.type = JavaProjectConstants.ARTIFACT_TYPE_JAR; |
| e.location = output; |
| e.script = scriptName; |
| e.buildTarget = targetName; |
| exports.add(e); |
| } |
| else if (isFolder(evaluator, baseFolder, output2)) { |
| Export e = new Export(); |
| e.type = JavaProjectConstants.ARTIFACT_TYPE_FOLDER; |
| e.location = output; |
| e.script = scriptName; |
| e.buildTarget = targetName; |
| exports.add(e); |
| } |
| } |
| } |
| } |
| return exports; |
| } |
| |
| /** |
| * Update exports of the project. |
| * Project is left modified and you must save it explicitely. |
| * @param helper AntProjectHelper instance |
| * @param exports list of Export instances |
| */ |
| public static void putExports(AntProjectHelper helper, List<Export> exports) { |
| //assert ProjectManager.mutex().isWriteAccess(); |
| Element data = Util.getPrimaryConfigurationData(helper); |
| Document doc = data.getOwnerDocument(); |
| |
| for (Element exportEl : XMLUtil.findSubElements(data)) { |
| if (!exportEl.getLocalName().equals("export")) { // NOI18N |
| continue; |
| } |
| data.removeChild(exportEl); |
| } |
| |
| for (Export export : exports) { |
| Element exportEl = doc.createElementNS(Util.NAMESPACE, "export"); // NOI18N |
| Element el; |
| el = doc.createElementNS(Util.NAMESPACE, "type"); // NOI18N |
| el.appendChild(doc.createTextNode(export.type)); // NOI18N |
| exportEl.appendChild(el); |
| el = doc.createElementNS(Util.NAMESPACE, "location"); // NOI18N |
| el.appendChild(doc.createTextNode(export.location)); // NOI18N |
| exportEl.appendChild(el); |
| if (export.script != null) { |
| el = doc.createElementNS(Util.NAMESPACE, "script"); // NOI18N |
| el.appendChild(doc.createTextNode(export.script)); // NOI18N |
| exportEl.appendChild(el); |
| } |
| el = doc.createElementNS(Util.NAMESPACE, "build-target"); // NOI18N |
| el.appendChild(doc.createTextNode(export.buildTarget)); // NOI18N |
| exportEl.appendChild(el); |
| if (export.cleanTarget != null) { |
| el = doc.createElementNS(Util.NAMESPACE, "clean-target"); // NOI18N |
| el.appendChild(doc.createTextNode(export.cleanTarget)); // NOI18N |
| exportEl.appendChild(el); |
| } |
| XMLUtil.appendChildElement(data, exportEl, rootElementsOrder); |
| } |
| Util.putPrimaryConfigurationData(helper, data); |
| } |
| |
| /** |
| * Try to guess project's subprojects. See issue #49640 for more details. |
| */ |
| public static List<String> guessSubprojects(PropertyEvaluator evaluator, |
| List<JavaCompilationUnit> javaCompilationUnits, File projectBase, File freeformBase) { |
| //assert ProjectManager.mutex().isReadAccess() || ProjectManager.mutex().isWriteAccess(); |
| Set<String> subprojs = new HashSet<String>(); |
| for (JavaCompilationUnit cu : javaCompilationUnits) { |
| if (cu.classpath != null) { |
| for (JavaCompilationUnit.CP cp : cu.classpath) { |
| if (!"compile".equals(cp.mode)) { // NOI18N |
| continue; |
| } |
| String classpath = evaluator.evaluate(cp.classpath); |
| if (classpath == null) { |
| continue; |
| } |
| for (String s : PropertyUtils.tokenizePath(classpath)) { |
| File file = FileUtil.normalizeFile(new File(s)); |
| AntArtifact aa = AntArtifactQuery.findArtifactFromFile(file); |
| if (aa != null) { |
| File proj = FileUtil.toFile(aa.getProject().getProjectDirectory()); |
| String p = Util.relativizeLocation(projectBase, freeformBase, proj); |
| subprojs.add(p); |
| } |
| } |
| } |
| } |
| } |
| return new ArrayList<String>(subprojs); |
| } |
| |
| /** |
| * Update subprojects of the project. |
| * Project is left modified and you must save it explicitely. |
| * @param helper AntProjectHelper instance |
| * @param subprojects list of paths to subprojects |
| */ |
| public static void putSubprojects(AntProjectHelper helper, List<String> subprojects) { |
| //assert ProjectManager.mutex().isWriteAccess(); |
| Element data = Util.getPrimaryConfigurationData(helper); |
| Document doc = data.getOwnerDocument(); |
| Element subproject = XMLUtil.findElement(data, "subprojects", Util.NAMESPACE); // NOI18N |
| if (subproject != null) { |
| data.removeChild(subproject); |
| } |
| subproject = doc.createElementNS(Util.NAMESPACE, "subprojects"); // NOI18N |
| XMLUtil.appendChildElement(data, subproject, rootElementsOrder); |
| |
| for (String proj : subprojects) { |
| Element projEl = doc.createElementNS(Util.NAMESPACE, "project"); // NOI18N |
| projEl.appendChild(doc.createTextNode(proj)); |
| subproject.appendChild(projEl); |
| } |
| Util.putPrimaryConfigurationData(helper, data); |
| } |
| |
| /** |
| * Try to guess project's build folders. See issue #50934 for more details. |
| */ |
| public static List<String> guessBuildFolders(PropertyEvaluator evaluator, |
| List<JavaCompilationUnit> javaCompilationUnits, File projectBase, File freeformBase) { |
| |
| List<String> buildFolders = new ArrayList<String>(); |
| for (JavaCompilationUnit cu : javaCompilationUnits) { |
| if (cu.output != null) { |
| for (String output : cu.output) { |
| File f = Util.resolveFile(evaluator, freeformBase, output); |
| // include only directories |
| if (!f.isDirectory()) { |
| continue; |
| } |
| String absOutput = f.getAbsolutePath(); |
| if (!absOutput.endsWith(File.separator)) { |
| absOutput += File.separatorChar; |
| } |
| |
| if (absOutput.startsWith(projectBase.getAbsolutePath()+File.separatorChar) || |
| absOutput.startsWith(freeformBase.getAbsolutePath()+File.separatorChar)) { |
| // ignore output which lies below project base or freeform base |
| continue; |
| } |
| boolean add = true; |
| Iterator<String> it = buildFolders.iterator(); |
| while (it.hasNext()) { |
| String path = it.next(); |
| if (!path.endsWith(File.separator)) { |
| path += File.separatorChar; |
| } |
| if (path.equals(absOutput)) { |
| // such a path is already there |
| add = false; |
| break; |
| } else if (absOutput.startsWith(path)) { |
| // such a patch is already there |
| add = false; |
| break; |
| } else if (path.startsWith(absOutput)) { |
| it.remove(); |
| } |
| } |
| if (add) { |
| buildFolders.add(output); |
| } |
| } |
| } |
| } |
| return buildFolders; |
| } |
| |
| /** |
| * Update build folders of the project. |
| * Project is left modified and you must save it explicitely. |
| * @param helper AntProjectHelper instance |
| * @param buildFolders list of build folder locations |
| */ |
| public static void putBuildFolders(AntProjectHelper helper, List<String> buildFolders) { |
| putBuildElement(helper, buildFolders, "build-folder"); |
| } |
| |
| private static void putBuildElement(AntProjectHelper helper, List<String> buildFolders, String elemName) { |
| Element data = Util.getPrimaryConfigurationData(helper); |
| Document doc = data.getOwnerDocument(); |
| Element foldersEl = XMLUtil.findElement(data, "folders", Util.NAMESPACE); // NOI18N |
| if (foldersEl == null) { |
| foldersEl = doc.createElementNS(Util.NAMESPACE, "folders"); // NOI18N |
| XMLUtil.appendChildElement(data, foldersEl, rootElementsOrder); |
| } else { |
| List<Element> folders = XMLUtil.findSubElements(foldersEl); |
| for (Element buildFolderEl : folders) { |
| if (!buildFolderEl.getLocalName().equals(elemName)) { // NOI18N |
| continue; |
| } |
| foldersEl.removeChild(buildFolderEl); |
| } |
| } |
| |
| for (String location : buildFolders) { |
| Element buildFolderEl = doc.createElementNS(Util.NAMESPACE, elemName); // NOI18N |
| Element locationEl = doc.createElementNS(Util.NAMESPACE, "location"); // NOI18N |
| locationEl.appendChild(doc.createTextNode(location)); |
| buildFolderEl.appendChild(locationEl); |
| XMLUtil.appendChildElement(foldersEl, buildFolderEl, folderElementsOrder); |
| } |
| Util.putPrimaryConfigurationData(helper, data); |
| } |
| |
| public static List<String> getBuildFiles(PropertyEvaluator evaluator, |
| List<JavaCompilationUnit> compUnits, File projectBase, File freeformBase) { |
| |
| List<String> buildFiles = new ArrayList<String>(); |
| for (JavaCompilationUnit cu : compUnits) { |
| if (cu.output != null) { |
| for (String output : cu.output) { |
| File f = Util.resolveFile(evaluator, freeformBase, output); |
| try { |
| if (f.exists() && !FileUtil.isArchiveFile(Utilities.toURI(f).toURL())) { |
| continue; |
| } |
| } catch (MalformedURLException murle) { |
| Exceptions.printStackTrace(murle); |
| } |
| String absOutput = f.getAbsolutePath(); |
| if (absOutput.startsWith(projectBase.getAbsolutePath() + File.separatorChar) || |
| absOutput.startsWith(freeformBase.getAbsolutePath() + File.separatorChar)) { |
| // ignore output which lies below project base or freeform base |
| continue; |
| } |
| boolean add = true; |
| Iterator<String> it = buildFiles.iterator(); |
| while (it.hasNext()) { |
| String path = it.next(); |
| if (path.equals(absOutput)) { |
| // such a path is already there |
| add = false; |
| break; |
| } |
| } |
| if (add) { |
| buildFiles.add(output); |
| } |
| } |
| } |
| } |
| return buildFiles; |
| } |
| |
| public static void putBuildFiles(AntProjectHelper helper, List<String> buildFiles) { |
| putBuildElement(helper, buildFiles, "build-file"); |
| } |
| |
| // XXX: copy&pasted from FreeformProjectGenerator |
| /** |
| * Read target mappings from project. |
| * @param helper AntProjectHelper instance |
| * @return list of TargetMapping instances |
| */ |
| public static List<TargetMapping> getTargetMappings(AntProjectHelper helper) { |
| //assert ProjectManager.mutex().isReadAccess() || ProjectManager.mutex().isWriteAccess(); |
| List<TargetMapping> list = new ArrayList<TargetMapping>(); |
| Element genldata = Util.getPrimaryConfigurationData(helper); |
| Element actionsEl = XMLUtil.findElement(genldata, "ide-actions", Util.NAMESPACE); // NOI18N |
| if (actionsEl == null) { |
| return list; |
| } |
| for (Element actionEl : XMLUtil.findSubElements(actionsEl)) { |
| TargetMapping tm = new TargetMapping(); |
| tm.name = actionEl.getAttribute("name"); // NOI18N |
| List<String> targetNames = new ArrayList<String>(); |
| EditableProperties props = new EditableProperties(false); |
| for (Element subEl : XMLUtil.findSubElements(actionEl)) { |
| if (subEl.getLocalName().equals("target")) { // NOI18N |
| targetNames.add(XMLUtil.findText(subEl)); |
| continue; |
| } |
| if (subEl.getLocalName().equals("script")) { // NOI18N |
| tm.script = XMLUtil.findText(subEl); |
| continue; |
| } |
| if (subEl.getLocalName().equals("context")) { // NOI18N |
| TargetMapping.Context ctx = new TargetMapping.Context(); |
| for (Element contextSubEl : XMLUtil.findSubElements(subEl)) { |
| if (contextSubEl.getLocalName().equals("property")) { // NOI18N |
| ctx.property = XMLUtil.findText(contextSubEl); |
| continue; |
| } |
| if (contextSubEl.getLocalName().equals("format")) { // NOI18N |
| ctx.format = XMLUtil.findText(contextSubEl); |
| continue; |
| } |
| if (contextSubEl.getLocalName().equals("folder")) { // NOI18N |
| ctx.folder = XMLUtil.findText(contextSubEl); |
| continue; |
| } |
| if (contextSubEl.getLocalName().equals("pattern")) { // NOI18N |
| ctx.pattern = XMLUtil.findText(contextSubEl); |
| continue; |
| } |
| if (contextSubEl.getLocalName().equals("arity")) { // NOI18N |
| Element sepFilesEl = XMLUtil.findElement(contextSubEl, "separated-files", Util.NAMESPACE); // NOI18N |
| if (sepFilesEl != null) { |
| ctx.separator = XMLUtil.findText(sepFilesEl); |
| } |
| continue; |
| } |
| } |
| tm.context = ctx; |
| } |
| if (subEl.getLocalName().equals("property")) { // NOI18N |
| readProperty(subEl, props); |
| continue; |
| } |
| } |
| tm.targets = targetNames; |
| if (props.keySet().size() > 0) { |
| tm.properties = props; |
| } |
| list.add(tm); |
| } |
| return list; |
| } |
| |
| |
| /*package private*/ static boolean isFolder (PropertyEvaluator eval, File baseFolder, String folder) { |
| File f = Util.resolveFile(eval, baseFolder, folder); |
| if (f != null && f.isDirectory()) { |
| return true; |
| } |
| int dotIndex = folder.lastIndexOf('.'); //NOI18N |
| int slashIndex = folder.lastIndexOf('/'); //NOI18N |
| return dotIndex == -1 || (dotIndex < slashIndex) ; |
| } |
| |
| /** |
| * Structure describing target mapping. |
| * Data in the struct are in the same format as they are stored in XML. |
| */ |
| public static final class TargetMapping { |
| public String script; |
| public List<String> targets; |
| public String name; |
| public EditableProperties properties; |
| public Context context; // may be null |
| |
| public static final class Context { |
| public String property; |
| public String format; |
| public String folder; |
| public String pattern; // may be null |
| public String separator; // may be null |
| } |
| } |
| |
| private static void readProperty(Element propertyElement, EditableProperties props) { |
| String key = propertyElement.getAttribute("name"); // NOI18N |
| String value = XMLUtil.findText(propertyElement); |
| props.setProperty(key, value); |
| } |
| |
| private static int minimalNS(final JavaCompilationUnit unit) { |
| int min = 1; |
| if (unit.isTests || (unit.javadoc != null && !unit.javadoc.isEmpty())) { |
| min = 2; |
| } |
| if (unit.annotationPorocessing != null) { |
| min = 3; |
| } |
| if (unit.sourceLevel != null) { |
| final SpecificationVersion JAVA_6 = new SpecificationVersion("1.6"); //NOI18N |
| final SpecificationVersion JAVA_7 = new SpecificationVersion("1.7"); //NOI18N |
| final SpecificationVersion JAVA_8 = new SpecificationVersion("1.8"); //NOI18N |
| final SpecificationVersion JAVA_9 = new SpecificationVersion("9"); //NOI18N |
| final SpecificationVersion current = new SpecificationVersion(unit.sourceLevel); |
| if (JAVA_6.equals(current) || JAVA_7.equals(current)) { |
| min = 3; |
| } else if (JAVA_8.equals(current)) { |
| min = 4; |
| } else if (JAVA_9.compareTo(current) <= 0) { |
| min = 5; |
| } |
| } |
| return min; |
| } |
| } |