0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.apisupport.project;
0043:
0044: import java.io.File;
0045: import java.io.IOException;
0046: import java.io.OutputStream;
0047: import java.util.Collection;
0048: import java.util.Collections;
0049: import java.util.HashMap;
0050: import java.util.HashSet;
0051: import java.util.Iterator;
0052: import java.util.List;
0053: import java.util.Map;
0054: import java.util.Set;
0055: import java.util.SortedSet;
0056: import java.util.TreeSet;
0057: import org.netbeans.api.project.ProjectManager;
0058: import org.netbeans.modules.apisupport.project.spi.NbModuleProvider;
0059: import org.netbeans.modules.apisupport.project.spi.NbModuleProvider.NbModuleType;
0060: import org.netbeans.modules.apisupport.project.suite.SuiteProjectType;
0061: import org.netbeans.modules.apisupport.project.ui.customizer.ModuleDependency;
0062: import org.netbeans.modules.apisupport.project.universe.ModuleEntry;
0063: import org.openide.ErrorManager;
0064: import org.openide.filesystems.FileLock;
0065: import org.openide.filesystems.FileObject;
0066: import org.openide.filesystems.FileUtil;
0067: import org.openide.xml.XMLUtil;
0068: import org.w3c.dom.Document;
0069: import org.w3c.dom.Element;
0070: import org.netbeans.modules.apisupport.project.universe.ModuleList;
0071: import org.netbeans.modules.apisupport.project.universe.NbPlatform;
0072: import org.netbeans.modules.apisupport.project.universe.TestModuleDependency;
0073: import org.w3c.dom.Node;
0074: import org.w3c.dom.NodeList;
0075:
0076: /**
0077: * Convenience class for managing project's <em>project.xml</em> file. You
0078: * should explicitly enclose a <em>complete</em> operation within write access
0079: * to prevent race conditions. Use {@link ProjectManager#saveProject} to apply
0080: * changes <em>physically</em>.
0081: */
0082: public final class ProjectXMLManager {
0083:
0084: /** Equal to AntProjectHelper.PROJECT_NS which is package private. */
0085: // XXX is there a better way? (impact of imposibility to use ProjectGenerator)
0086: private static final String PROJECT_NS = "http://www.netbeans.org/ns/project/1"; // NOI18N
0087:
0088: // elements constants
0089: private static final String BINARY_ORIGIN = "binary-origin"; // NOI18N
0090: private static final String BUILD_PREREQUISITE = "build-prerequisite"; // NOI18N
0091: private static final String CLASS_PATH_BINARY_ORIGIN = "binary-origin"; //NOI18N
0092: private static final String CLASS_PATH_EXTENSION = "class-path-extension"; // NOI18N
0093: private static final String CLASS_PATH_RUNTIME_PATH = "runtime-relative-path"; //NOI18N
0094: private static final String CODE_NAME_BASE = "code-name-base"; // NOI18N
0095: private static final String COMPILE_DEPENDENCY = "compile-dependency"; // NOI18N
0096: private static final String DATA = "data"; // NOI18N
0097: private static final String DEPENDENCY = "dependency"; // NOI18N
0098: private static final String EXTRA_COMPILATION_UNIT = "extra-compilation-unit"; // NOI18N
0099: private static final String FRIEND = "friend"; // NOI18N
0100: private static final String FRIEND_PACKAGES = "friend-packages"; // NOI18N
0101: private static final String IMPLEMENTATION_VERSION = "implementation-version"; // NOI18N
0102: private static final String MODULE_DEPENDENCIES = "module-dependencies"; // NOI18N
0103: private static final String PACKAGE = "package"; // NOI18N
0104: private static final String PUBLIC_PACKAGES = "public-packages"; // NOI18N
0105: private static final String RELEASE_VERSION = "release-version"; // NOI18N
0106: private static final String RUN_DEPENDENCY = "run-dependency"; // NOI18N
0107: private static final String SPECIFICATION_VERSION = "specification-version"; // NOI18N
0108: private static final String STANDALONE = "standalone"; // NOI18N
0109: private static final String SUBPACKAGES = "subpackages"; // NOI18N
0110: private static final String SUITE_COMPONENT = "suite-component"; // NOI18N
0111: private static final String TEST_DEPENDENCIES = "test-dependencies"; // NOI18N
0112: private static final String TEST_TYPE_NAME = "name"; // NOI18N
0113: private static final String TEST_DEPENDENCY = "test-dependency"; // NOI18N
0114: private static final String TEST_DEPENDENCY_CNB = "code-name-base"; // NOI18N
0115: private static final String TEST_DEPENDENCY_RECURSIVE = "recursive"; // NOI18N
0116: private static final String TEST_DEPENDENCY_COMPILE = "compile-dependency"; // NOI18N
0117: private static final String TEST_DEPENDENCY_TEST = "test"; // NOI18N
0118: private static final String TEST_TYPE = "test-type"; //NOI18N
0119:
0120: private final NbModuleProject project;
0121: private NbPlatform customPlaf;
0122:
0123: private String cnb;
0124: private SortedSet<ModuleDependency> directDeps;
0125: private ManifestManager.PackageExport[] publicPackages;
0126: private String[] cpExtensions;
0127: private String[] friends;
0128:
0129: // cached confData element for easy access with getConfData
0130: private Element confData;
0131:
0132: /** Creates a new instance of {@link ProjectXMLManager}. */
0133: public ProjectXMLManager(final NbModuleProject project) {
0134: this .project = project;
0135: }
0136:
0137: /**
0138: * Utility mehtod for getting the {@link ProjectXMLManager instance}
0139: * associated with a project in the given directory.
0140: *
0141: * @throws IOException if the project under a given <code>projectDir</code>
0142: * was recognized but could not be loaded (see {@link ProjectManager#findProject}).
0143: */
0144: public static ProjectXMLManager getInstance(final File projectDir)
0145: throws IOException {
0146: FileObject dir = FileUtil.toFileObject(projectDir);
0147: NbModuleProject p = (NbModuleProject) ProjectManager
0148: .getDefault().findProject(dir);
0149: return new ProjectXMLManager(p);
0150: }
0151:
0152: public void setModuleType(NbModuleType moduleType) {
0153: Element confData = getConfData();
0154: Document doc = confData.getOwnerDocument();
0155:
0156: Element standaloneEl = findElement(confData,
0157: ProjectXMLManager.STANDALONE);
0158: if (standaloneEl != null
0159: && moduleType == NbModuleProvider.STANDALONE) {
0160: // nothing needs to be done - standalone is already set
0161: return;
0162: }
0163:
0164: Element suiteCompEl = findElement(confData,
0165: ProjectXMLManager.SUITE_COMPONENT);
0166: if (suiteCompEl != null
0167: && moduleType == NbModuleProvider.SUITE_COMPONENT) {
0168: // nothing needs to be done - suiteCompEl is already set
0169: return;
0170: }
0171:
0172: if (suiteCompEl == null && standaloneEl == null
0173: && moduleType == NbModuleProvider.NETBEANS_ORG) {
0174: // nothing needs to be done - nb.org modules don't have any element
0175: return;
0176: }
0177:
0178: // Ok, we get here. So clean up....
0179: if (suiteCompEl != null) {
0180: confData.removeChild(suiteCompEl);
0181: }
0182: if (standaloneEl != null) {
0183: confData.removeChild(standaloneEl);
0184: }
0185:
0186: // ....and create element for new module type.
0187: Element newModuleType = createTypeElement(doc, moduleType);
0188: if (newModuleType != null) {
0189: confData.insertBefore(newModuleType,
0190: findModuleDependencies(confData));
0191: }
0192: project.putPrimaryConfigurationData(confData);
0193: }
0194:
0195: /**
0196: * Returns direct module dependencies using default module's platform. See
0197: * {@link #getDirectDependencies(NbPlatform)} for more details to which
0198: * this method delegates.
0199: */
0200: public SortedSet<ModuleDependency> getDirectDependencies()
0201: throws IOException {
0202: return getDirectDependencies(null);
0203: }
0204:
0205: /**
0206: * Returns sorted direct module dependencies using {@link
0207: * ModuleDependency#CNB_COMPARATOR} allowing to pass a custom platform.
0208: * Since no two modules with the same code name base may be set as a
0209: * dependency. Also this is ordering used in the <em>project.xml</em>.
0210: */
0211: public SortedSet<ModuleDependency> getDirectDependencies(
0212: final NbPlatform customPlaf) throws IOException {
0213: if (this .customPlaf == customPlaf && this .directDeps != null) {
0214: return this .directDeps;
0215: }
0216: this .customPlaf = customPlaf;
0217: SortedSet<ModuleDependency> directDeps = new TreeSet<ModuleDependency>(
0218: ModuleDependency.CNB_COMPARATOR);
0219: Element moduleDependencies = findModuleDependencies(getConfData());
0220: assert moduleDependencies != null : "Cannot find <module-dependencies> for: "
0221: + project;
0222: File prjDirF = project.getProjectDirectoryFile();
0223: ModuleList ml;
0224: if (customPlaf != null) {
0225: ml = ModuleList.getModuleList(prjDirF, customPlaf
0226: .getDestDir());
0227: } else {
0228: ml = ModuleList.getModuleList(prjDirF);
0229: }
0230: for (Element depEl : Util.findSubElements(moduleDependencies)) {
0231: Element cnbEl = findElement(depEl,
0232: ProjectXMLManager.CODE_NAME_BASE);
0233: String cnb = Util.findText(cnbEl);
0234: ModuleEntry me = ml.getEntry(cnb);
0235: if (me == null) {
0236: // XXX might be e.g. shown in nb.errorForreground and "disabled"
0237: Util.err.log(ErrorManager.WARNING,
0238: "Detected dependency on module which cannot be found in "
0239: + // NOI18N
0240: "the current module's universe (platform, suite): "
0241: + // NOI18N
0242: cnb + " (skipping)"); // NOI18N
0243: continue;
0244: }
0245:
0246: Element runDepEl = findElement(depEl,
0247: ProjectXMLManager.RUN_DEPENDENCY);
0248: if (runDepEl == null) {
0249: if (!directDeps.add(new ModuleDependency(me))) {
0250: String errMessage = "Corrupted project metadata (project.xml). "
0251: + // NOI18N
0252: "Duplicate dependency entry found: " + me; // NOI18N
0253: Util.err.log(ErrorManager.WARNING, errMessage);
0254: throw new IllegalStateException(errMessage);
0255: }
0256: continue;
0257: }
0258:
0259: Element relVerEl = findElement(runDepEl,
0260: ProjectXMLManager.RELEASE_VERSION);
0261: String relVer = null;
0262: if (relVerEl != null) {
0263: relVer = Util.findText(relVerEl);
0264: }
0265:
0266: Element specVerEl = findElement(runDepEl,
0267: ProjectXMLManager.SPECIFICATION_VERSION);
0268: String specVer = null;
0269: if (specVerEl != null) {
0270: specVer = Util.findText(specVerEl);
0271: }
0272:
0273: Element compDepEl = findElement(depEl,
0274: ProjectXMLManager.COMPILE_DEPENDENCY);
0275: Element impleVerEl = findElement(runDepEl,
0276: ProjectXMLManager.IMPLEMENTATION_VERSION);
0277:
0278: ModuleDependency depToAdd = new ModuleDependency(me,
0279: relVer, specVer, compDepEl != null,
0280: impleVerEl != null);
0281: if (!directDeps.add(depToAdd)) {
0282: String errMessage = "Corrupted project metadata (project.xml). "
0283: + // NOI18N
0284: "Duplicate dependency entry found: " + depToAdd; // NOI18N
0285: Util.err.log(ErrorManager.WARNING, errMessage);
0286: throw new IllegalStateException(errMessage);
0287: }
0288: }
0289: this .directDeps = Collections.unmodifiableSortedSet(directDeps);
0290: return this .directDeps;
0291: }
0292:
0293: /** Remove given dependency from the configuration data. */
0294: public void removeDependency(String cnbToRemove) {
0295: Element confData = getConfData();
0296: Element moduleDependencies = findModuleDependencies(confData);
0297: List<Element> currentDeps = Util
0298: .findSubElements(moduleDependencies);
0299: for (Iterator it = currentDeps.iterator(); it.hasNext();) {
0300: Element dep = (Element) it.next();
0301: Element cnbEl = findElement(dep,
0302: ProjectXMLManager.CODE_NAME_BASE);
0303: String cnb = Util.findText(cnbEl);
0304: if (cnbToRemove.equals(cnb)) {
0305: moduleDependencies.removeChild(dep);
0306: }
0307: }
0308: project.putPrimaryConfigurationData(confData);
0309: }
0310:
0311: /**
0312: * Use this for removing more than one dependencies. It's faster then
0313: * iterating and using <code>removeDependency</code> for every entry.
0314: */
0315: public void removeDependencies(
0316: Collection<ModuleDependency> depsToDelete) {
0317: Set<String> cnbsToDelete = new HashSet<String>(depsToDelete
0318: .size());
0319: for (ModuleDependency dep : depsToDelete) {
0320: cnbsToDelete.add(dep.getModuleEntry().getCodeNameBase());
0321: }
0322: removeDependenciesByCNB(cnbsToDelete);
0323: }
0324:
0325: /**
0326: * Use this for removing more than one dependencies. It's faster then
0327: * iterating and using <code>removeDependency</code> for every entry.
0328: */
0329: public void removeDependenciesByCNB(Collection<String> cnbsToDelete) {
0330: Element confData = getConfData();
0331: Element moduleDependencies = findModuleDependencies(confData);
0332: for (Element dep : Util.findSubElements(moduleDependencies)) {
0333: Element cnbEl = findElement(dep,
0334: ProjectXMLManager.CODE_NAME_BASE);
0335: String cnb = Util.findText(cnbEl);
0336: if (cnbsToDelete.remove(cnb)) {
0337: moduleDependencies.removeChild(dep);
0338: }
0339: if (cnbsToDelete.size() == 0) {
0340: break; // everything was deleted
0341: }
0342: }
0343: if (cnbsToDelete.size() != 0) {
0344: Util.err.log(ErrorManager.WARNING,
0345: "Some modules weren't deleted: " + cnbsToDelete); // NOI18N
0346: }
0347: project.putPrimaryConfigurationData(confData);
0348: }
0349:
0350: public void editDependency(ModuleDependency origDep,
0351: ModuleDependency newDep) {
0352: Element confData = getConfData();
0353: Element moduleDependencies = findModuleDependencies(confData);
0354: List<Element> currentDeps = Util
0355: .findSubElements(moduleDependencies);
0356: for (Iterator<Element> it = currentDeps.iterator(); it
0357: .hasNext();) {
0358: Element dep = it.next();
0359: Element cnbEl = findElement(dep,
0360: ProjectXMLManager.CODE_NAME_BASE);
0361: String cnb = Util.findText(cnbEl);
0362: if (cnb.equals(origDep.getModuleEntry().getCodeNameBase())) {
0363: moduleDependencies.removeChild(dep);
0364: Element nextDep = it.hasNext() ? it.next() : null;
0365: createModuleDependencyElement(moduleDependencies,
0366: newDep, nextDep);
0367: break;
0368: }
0369: }
0370: project.putPrimaryConfigurationData(confData);
0371: }
0372:
0373: /**
0374: * Adds given dependency.
0375: */
0376: public void addDependency(ModuleDependency md) throws IOException {
0377: addDependencies(Collections.singleton(md));
0378: }
0379:
0380: /**
0381: * Adds given modules as module-dependencies for the project.
0382: */
0383: public void addDependencies(final Set<ModuleDependency> toAdd)
0384: throws IOException {
0385: SortedSet<ModuleDependency> deps = new TreeSet<ModuleDependency>(
0386: getDirectDependencies());
0387: if (deps.addAll(toAdd)) {
0388: replaceDependencies(deps);
0389: }
0390: }
0391:
0392: /**
0393: * Replaces all original dependencies with the given <code>newDeps</code>.
0394: */
0395: public void replaceDependencies(final Set<ModuleDependency> newDeps) {
0396: Element confData = getConfData();
0397: Document doc = confData.getOwnerDocument();
0398: Element moduleDependencies = findModuleDependencies(confData);
0399: confData.removeChild(moduleDependencies);
0400: moduleDependencies = createModuleElement(doc,
0401: ProjectXMLManager.MODULE_DEPENDENCIES);
0402: Element before = findTestDependenciesElement(confData);
0403: if (before == null) {
0404: before = findPublicPackagesElement(confData);
0405: }
0406: if (before == null) {
0407: before = findFriendsElement(confData);
0408: }
0409: assert before != null : "There must be " + PUBLIC_PACKAGES
0410: + " or " // NOI18N
0411: + FRIEND_PACKAGES + " element according to XSD"; // NOI18N
0412: confData.insertBefore(moduleDependencies, before);
0413: SortedSet<ModuleDependency> sortedDeps = new TreeSet<ModuleDependency>(
0414: newDeps);
0415: for (Iterator it = sortedDeps.iterator(); it.hasNext();) {
0416: ModuleDependency md = (ModuleDependency) it.next();
0417: createModuleDependencyElement(moduleDependencies, md, null);
0418: }
0419: project.putPrimaryConfigurationData(confData);
0420: this .directDeps = sortedDeps;
0421: }
0422:
0423: public void removeClassPathExtensions() {
0424: Element confData = getConfData();
0425: NodeList nl = confData.getElementsByTagNameNS(
0426: NbModuleProjectType.NAMESPACE_SHARED,
0427: ProjectXMLManager.CLASS_PATH_EXTENSION);
0428: for (int i = 0; i < nl.getLength(); i++) {
0429: confData.removeChild(nl.item(i));
0430: }
0431: project.putPrimaryConfigurationData(confData);
0432: }
0433:
0434: /**
0435: * Removes test dependency under type <code>testType</code>, indentified
0436: * by <code>cnbToRemove</code>. Does not remove whole 610test type even if
0437: * removed test dependency was the last one.
0438: */
0439: public boolean removeTestDependency(String testType,
0440: String cnbToRemove) {
0441: boolean wasRemoved = false;
0442: Element confData = getConfData();
0443: Element testModuleDependenciesEl = findTestDependenciesElement(confData);
0444: Element testTypeRemoveEl = null;
0445: for (Element type : Util
0446: .findSubElements(testModuleDependenciesEl)) {
0447: Element nameEl = findElement(type, TEST_TYPE_NAME);
0448: String nameOfType = Util.findText(nameEl);
0449: if (testType.equals(nameOfType)) {
0450: testTypeRemoveEl = type;
0451: }
0452: }
0453: //found such a test type
0454: if (testTypeRemoveEl != null) {
0455: for (Element el : Util.findSubElements(testTypeRemoveEl)) {
0456: Element cnbEl = findElement(el, TEST_DEPENDENCY_CNB);
0457: if (cnbEl == null) {
0458: continue; //name node, continue
0459: }
0460: String cnb = Util.findText(cnbEl);
0461: if (cnbToRemove.equals(cnb)) {
0462: // found test dependency with desired CNB
0463: testTypeRemoveEl.removeChild(el);
0464: wasRemoved = true;
0465: project.putPrimaryConfigurationData(confData);
0466: }
0467: }
0468: }
0469: return wasRemoved;
0470: }
0471:
0472: /**
0473: * Adds new test dependency to <code>project.xml</code>. Currently only two test types are
0474: * supported - <code>UNIT</code> and <code>QA_FUNCTIONAL</code>. Test dependencies under
0475: * test types are sorted by CNB.
0476: */
0477: public void addTestDependency(String testType,
0478: TestModuleDependency newTestDep) throws IOException {
0479: final String UNIT = TestModuleDependency.UNIT;
0480: final String QA_FUNCTIONAL = TestModuleDependency.QA_FUNCTIONAL;
0481: assert (UNIT.equals(testType) || QA_FUNCTIONAL.equals(testType)) : "Current impl.supports only "
0482: + QA_FUNCTIONAL + " or " + UNIT + " tests"; // NOI18N
0483: File projectDir = FileUtil
0484: .toFile(project.getProjectDirectory());
0485: ModuleList ml = ModuleList.getModuleList(projectDir);
0486: Map<String, Set<TestModuleDependency>> map = new HashMap<String, Set<TestModuleDependency>>(
0487: getTestDependencies(ml));
0488: Set<TestModuleDependency> testDependenciesSet = map
0489: .get(testType);
0490: if (testDependenciesSet == null) {
0491: testDependenciesSet = new TreeSet<TestModuleDependency>();
0492: map.put(testType, testDependenciesSet);
0493: } else {
0494: // XXX necessary to clone?
0495: testDependenciesSet = new TreeSet<TestModuleDependency>(
0496: testDependenciesSet);
0497: }
0498: if (!testDependenciesSet.add(newTestDep)) {
0499: return; //nothing new to add, dep is already there, finished
0500: }
0501: Element confData = getConfData();
0502: Document doc = confData.getOwnerDocument();
0503: Element testModuleDependenciesEl = findTestDependenciesElement(confData);
0504: if (testModuleDependenciesEl == null) { // test dependencies element does not exist, create it
0505: Element before = findPublicPackagesElement(confData);
0506: if (before == null) {
0507: before = findFriendsElement(confData);
0508: }
0509: assert before != null : "There must be " + PUBLIC_PACKAGES
0510: + " or " // NOI18N
0511: + FRIEND_PACKAGES + " element according to XSD"; // NOI18N
0512: testModuleDependenciesEl = createModuleElement(doc,
0513: TEST_DEPENDENCIES);
0514: confData.insertBefore(testModuleDependenciesEl, before);
0515: }
0516: Element testTypeEl = null;
0517: //iterate through test types to determine if testType exist
0518: for (Element tt : Util
0519: .findSubElements(testModuleDependenciesEl)) {
0520: Node nameNode = findElement(tt, "name"); // NOI18N
0521: assert nameNode != null : "should be some child with name";
0522: //Node nameNode = tt.getFirstChild();
0523: //nameNode.getNodeName()
0524: assert (TEST_TYPE_NAME.equals(nameNode.getLocalName())) : "name node should be first child, but was:"
0525: + nameNode.getLocalName()
0526: + "or"
0527: + nameNode.getNodeName(); //NOI18N
0528: //equals
0529: if (nameNode.getTextContent().equals(testType)) {
0530: testTypeEl = tt;
0531: }
0532: }
0533: //? new or existing test type?
0534: if (testTypeEl == null) {
0535: //this test type, does not exist, create it, and add new test dependency
0536: Element newTestTypeEl = createNewTestTypeElement(doc,
0537: testType);
0538: testModuleDependenciesEl.appendChild(newTestTypeEl);
0539: createTestModuleDependencyElement(newTestTypeEl, newTestDep);
0540: project.putPrimaryConfigurationData(confData);
0541: return;
0542: } else {
0543: //testtype exists, refresh it
0544: Node beforeWhat = testTypeEl.getNextSibling();
0545: testModuleDependenciesEl.removeChild(testTypeEl);
0546: Element refreshedTestTypeEl = createNewTestTypeElement(doc,
0547: testType);
0548: if (beforeWhat == null) {
0549: testModuleDependenciesEl
0550: .appendChild(refreshedTestTypeEl);
0551: } else {
0552: testModuleDependenciesEl.insertBefore(
0553: refreshedTestTypeEl, beforeWhat);
0554: }
0555: for (Iterator it = testDependenciesSet.iterator(); it
0556: .hasNext();) {
0557: TestModuleDependency tmd = (TestModuleDependency) it
0558: .next();
0559: createTestModuleDependencyElement(refreshedTestTypeEl,
0560: tmd);
0561: project.putPrimaryConfigurationData(confData);
0562: }
0563: }
0564: }
0565:
0566: private Element createNewTestTypeElement(Document doc,
0567: String testTypeName) {
0568: Element newTestTypeEl = createModuleElement(doc, TEST_TYPE);
0569: Element nameOfTestTypeEl = createModuleElement(doc,
0570: TEST_TYPE_NAME, testTypeName);
0571: newTestTypeEl.appendChild(nameOfTestTypeEl);
0572: return newTestTypeEl;
0573: }
0574:
0575: private void createTestModuleDependencyElement(
0576: Element testTypeElement, TestModuleDependency tmd) {
0577: Document doc = testTypeElement.getOwnerDocument();
0578: Element tde = createModuleElement(doc, TEST_DEPENDENCY);
0579: testTypeElement.appendChild(tde);
0580: tde.appendChild(createModuleElement(doc, TEST_DEPENDENCY_CNB,
0581: tmd.getModule().getCodeNameBase()));
0582: if (tmd.isRecursive()) {
0583: tde.appendChild(createModuleElement(doc,
0584: TEST_DEPENDENCY_RECURSIVE));
0585: }
0586: if (tmd.isCompile()) {
0587: tde.appendChild(createModuleElement(doc,
0588: TEST_DEPENDENCY_COMPILE));
0589: }
0590: if (tmd.isTest()) {
0591: tde.appendChild(createModuleElement(doc,
0592: TEST_DEPENDENCY_TEST));
0593: }
0594: }
0595:
0596: /**
0597: * Gives a map from test type (e.g. <em>unit</em> or <em>qa-functional</em>)
0598: * to the set of {@link TestModuleDependency dependencies} belonging to it.
0599: */
0600: public Map<String, Set<TestModuleDependency>> getTestDependencies(
0601: final ModuleList ml) {
0602: Element testDepsEl = findTestDependenciesElement(getConfData());
0603:
0604: Map<String, Set<TestModuleDependency>> testDeps = new HashMap<String, Set<TestModuleDependency>>();
0605:
0606: if (testDepsEl != null) {
0607: for (Element typeEl : Util.findSubElements(testDepsEl)) {
0608: Element testTypeEl = findElement(typeEl, TEST_TYPE_NAME);
0609: String testType = null;
0610: if (testTypeEl != null) {
0611: testType = Util.findText(testTypeEl);
0612: }
0613: if (testType == null) {
0614: testType = TestModuleDependency.UNIT; // default variant
0615: }
0616: Set<TestModuleDependency> directTestDeps = new TreeSet<TestModuleDependency>();
0617: for (Element depEl : Util.findSubElements(typeEl)) {
0618: if (depEl.getTagName().equals(TEST_DEPENDENCY)) {
0619: // parse test dep
0620: Element cnbEl = findElement(depEl,
0621: TEST_DEPENDENCY_CNB);
0622: boolean test = findElement(depEl,
0623: TEST_DEPENDENCY_TEST) != null;
0624: String cnb = null;
0625: if (cnbEl != null) {
0626: cnb = Util.findText(cnbEl);
0627: }
0628: boolean recursive = findElement(depEl,
0629: TEST_DEPENDENCY_RECURSIVE) != null;
0630: boolean compile = findElement(depEl,
0631: TEST_DEPENDENCY_COMPILE) != null;
0632: if (cnb != null) {
0633: ModuleEntry me = ml.getEntry(cnb);
0634: if (me != null) {
0635: TestModuleDependency tmd = new TestModuleDependency(
0636: me, test, recursive, compile);
0637: if (!directTestDeps.add(tmd)) {
0638: // testdependency already exists
0639: String path = project
0640: .getPathWithinNetBeansOrg();
0641: if (path == null) {
0642: path = project
0643: .getProjectDirectoryFile()
0644: .getAbsolutePath();
0645: }
0646: String msg = "Invalid project.xml ("
0647: + path
0648: + "); testdependency " // NOI18N
0649: + tmd.getModule()
0650: .getCodeNameBase()
0651: + " is duplicated!"; // NOI18N
0652: Util.err.log(ErrorManager.WARNING,
0653: msg);
0654: }
0655: }
0656: }
0657: }
0658: }
0659: testDeps.put(testType, directTestDeps);
0660: }
0661: }
0662: return testDeps;
0663: }
0664:
0665: /**
0666: * Replace existing classpath extensions with new values.
0667: * @param newValues <key=runtime-path(String), value=binary-path(String)>
0668: */
0669: public void replaceClassPathExtensions(final Map newValues) {
0670: removeClassPathExtensions();
0671: if (newValues != null && newValues.size() > 0) {
0672: Element confData = getConfData();
0673: Document doc = confData.getOwnerDocument();
0674: Iterator it = newValues.entrySet().iterator();
0675: while (it.hasNext()) {
0676: Map.Entry entry = (Map.Entry) it.next();
0677: Element cpel = createModuleElement(doc,
0678: ProjectXMLManager.CLASS_PATH_EXTENSION);
0679: Element runtime = createModuleElement(doc,
0680: ProjectXMLManager.CLASS_PATH_RUNTIME_PATH,
0681: (String) entry.getKey());
0682: Element binary = createModuleElement(doc,
0683: ProjectXMLManager.CLASS_PATH_BINARY_ORIGIN,
0684: (String) entry.getValue());
0685: cpel.appendChild(runtime);
0686: cpel.appendChild(binary);
0687: confData.appendChild(cpel);
0688:
0689: }
0690: project.putPrimaryConfigurationData(confData);
0691: }
0692: }
0693:
0694: /**
0695: * Replaces all original public packages with the given
0696: * <code>newPackages</code>. Also removes friend packages if there are any
0697: * since those two mutually exclusive.
0698: */
0699: public void replacePublicPackages(String[] newPackages) {
0700: removePublicAndFriends();
0701: Element confData = getConfData();
0702: Document doc = confData.getOwnerDocument();
0703: Element publicPackagesEl = createModuleElement(doc,
0704: ProjectXMLManager.PUBLIC_PACKAGES);
0705:
0706: insertPublicOrFriend(publicPackagesEl);
0707:
0708: for (int i = 0; i < newPackages.length; i++) {
0709: publicPackagesEl.appendChild(createModuleElement(doc,
0710: ProjectXMLManager.PACKAGE, newPackages[i]));
0711: }
0712: project.putPrimaryConfigurationData(confData);
0713: publicPackages = null; // XXX cleaner would be to listen on changes in helper
0714: }
0715:
0716: /** Position public-packages or friend-packages according to XSD. */
0717: private void insertPublicOrFriend(Element packagesEl) {
0718: Element beforeEl = findElement(getConfData(),
0719: ProjectXMLManager.CLASS_PATH_EXTENSION);
0720: if (beforeEl == null) {
0721: beforeEl = findElement(getConfData(),
0722: ProjectXMLManager.EXTRA_COMPILATION_UNIT);
0723: }
0724: getConfData().insertBefore(packagesEl, beforeEl);
0725: }
0726:
0727: /**
0728: * Replaces all original friends with the given <code>friends</code> with
0729: * <code>packagesToExpose</code> as exposed packages to those friends. Also
0730: * removes public packages if there are any since those two are mutually
0731: * exclusive.
0732: */
0733: public void replaceFriends(String[] friends,
0734: String[] packagesToExpose) {
0735: removePublicAndFriends();
0736: Element confData = getConfData();
0737: Document doc = confData.getOwnerDocument();
0738: Element friendPackages = createModuleElement(doc,
0739: ProjectXMLManager.FRIEND_PACKAGES);
0740: insertPublicOrFriend(friendPackages);
0741: for (int i = 0; i < friends.length; i++) {
0742: friendPackages.appendChild(createModuleElement(doc,
0743: ProjectXMLManager.FRIEND, friends[i]));
0744: }
0745: for (int i = 0; i < packagesToExpose.length; i++) {
0746: friendPackages.appendChild(createModuleElement(doc,
0747: ProjectXMLManager.PACKAGE, packagesToExpose[i]));
0748: }
0749: project.putPrimaryConfigurationData(confData);
0750: publicPackages = null;
0751: }
0752:
0753: /**
0754: * Returns an array of {@link ManifestManager.PackageExport}s of all
0755: * exposed public packages. Method considers both <em>package</em> and
0756: * <em>subpackages</em> elements with the recursivity flag set
0757: * appropriately for returned entries.
0758: *
0759: * @return array of {@link ManifestManager.PackageExport}. May be empty but
0760: * not <code>null</code>.
0761: */
0762: public ManifestManager.PackageExport[] getPublicPackages() {
0763: if (publicPackages == null) {
0764: publicPackages = ProjectXMLManager
0765: .findPublicPackages(getConfData());
0766: }
0767: return publicPackages;
0768: }
0769:
0770: /** Returns all friends or <code>null</code> if there are none. */
0771: public String[] getFriends() {
0772: if (friends == null) {
0773: friends = ProjectXMLManager.findFriends(getConfData());
0774: }
0775: return friends;
0776: }
0777:
0778: /**
0779: * Returns paths of all libraries bundled within a project this
0780: * <em>manager</em> manage. So the result should be an array of
0781: * <code>String</code>s each representing a relative path to the project's
0782: * external library (jar/zip).
0783: * @return an array of strings (may be empty)
0784: */
0785: public String[] getBinaryOrigins() {
0786: if (cpExtensions != null) {
0787: return cpExtensions;
0788: }
0789: Set<String> binaryOrigs = new TreeSet<String>();
0790: for (Element cpExtEl : Util.findSubElements(getConfData())) {
0791: if (CLASS_PATH_EXTENSION.equals(cpExtEl.getTagName())) {
0792: Element binOrigEl = findElement(cpExtEl, BINARY_ORIGIN);
0793: if (binOrigEl != null) {
0794: binaryOrigs.add(Util.findText(binOrigEl));
0795: }
0796: }
0797: }
0798: return cpExtensions = binaryOrigs
0799: .toArray(new String[binaryOrigs.size()]);
0800: }
0801:
0802: /** Returns code-name-base. */
0803: public String getCodeNameBase() {
0804: if (cnb == null) {
0805: Element cnbEl = findElement(getConfData(),
0806: ProjectXMLManager.CODE_NAME_BASE);
0807: cnb = Util.findText(cnbEl);
0808: }
0809: return cnb;
0810: }
0811:
0812: /** Package-private for unit tests only. */
0813: static void createModuleDependencyElement(
0814: Element moduleDependencies, ModuleDependency md,
0815: Element nextSibling) {
0816:
0817: Document doc = moduleDependencies.getOwnerDocument();
0818: Element modDepEl = createModuleElement(doc,
0819: ProjectXMLManager.DEPENDENCY);
0820: moduleDependencies.insertBefore(modDepEl, nextSibling);
0821:
0822: modDepEl.appendChild(createModuleElement(doc,
0823: ProjectXMLManager.CODE_NAME_BASE, md.getModuleEntry()
0824: .getCodeNameBase()));
0825: if (md.hasCompileDependency()) {
0826: modDepEl.appendChild(createModuleElement(doc,
0827: ProjectXMLManager.BUILD_PREREQUISITE));
0828: modDepEl.appendChild(createModuleElement(doc,
0829: ProjectXMLManager.COMPILE_DEPENDENCY));
0830: }
0831:
0832: Element runDepEl = createModuleElement(doc,
0833: ProjectXMLManager.RUN_DEPENDENCY);
0834: modDepEl.appendChild(runDepEl);
0835:
0836: String rv = md.getReleaseVersion();
0837: if (rv != null && !rv.trim().equals("")) {
0838: runDepEl.appendChild(createModuleElement(doc,
0839: ProjectXMLManager.RELEASE_VERSION, rv));
0840: }
0841: if (md.hasImplementationDepedendency()) {
0842: runDepEl.appendChild(createModuleElement(doc,
0843: ProjectXMLManager.IMPLEMENTATION_VERSION));
0844: } else {
0845: String sv = md.getSpecificationVersion();
0846: if (sv != null && !"".equals(sv)) { // NOI18N
0847: runDepEl.appendChild(createModuleElement(doc,
0848: ProjectXMLManager.SPECIFICATION_VERSION, sv));
0849: }
0850: }
0851: }
0852:
0853: /** Removes public-packages and friend-packages elements. */
0854: private void removePublicAndFriends() {
0855: Element friendPackages = findFriendsElement(getConfData());
0856: if (friendPackages != null) {
0857: getConfData().removeChild(friendPackages);
0858: }
0859: Element publicPackages = findPublicPackagesElement(getConfData());
0860: if (publicPackages != null) {
0861: getConfData().removeChild(publicPackages);
0862: }
0863: }
0864:
0865: private static Element findElement(Element parentEl,
0866: String elementName) {
0867: return Util.findElement(parentEl, elementName,
0868: NbModuleProjectType.NAMESPACE_SHARED);
0869: }
0870:
0871: /** Package-private for unit tests only. */
0872: static Element findModuleDependencies(Element parentEl) {
0873: return findElement(parentEl,
0874: ProjectXMLManager.MODULE_DEPENDENCIES);
0875: }
0876:
0877: private static Element findTestDependenciesElement(Element parentEl) {
0878: return findElement(parentEl,
0879: ProjectXMLManager.TEST_DEPENDENCIES);
0880: }
0881:
0882: private static Element findPublicPackagesElement(Element parentEl) {
0883: return findElement(parentEl, ProjectXMLManager.PUBLIC_PACKAGES);
0884: }
0885:
0886: private static Element findFriendsElement(Element parentEl) {
0887: return findElement(parentEl, ProjectXMLManager.FRIEND_PACKAGES);
0888: }
0889:
0890: private static Element createModuleElement(Document doc, String name) {
0891: return doc.createElementNS(
0892: NbModuleProjectType.NAMESPACE_SHARED, name);
0893: }
0894:
0895: private static Element createModuleElement(Document doc,
0896: String name, String innerText) {
0897: Element el = createModuleElement(doc, name);
0898: el.appendChild(doc.createTextNode(innerText));
0899: return el;
0900: }
0901:
0902: private static Element createSuiteElement(Document doc, String name) {
0903: return doc.createElementNS(SuiteProjectType.NAMESPACE_SHARED,
0904: name);
0905: }
0906:
0907: private static Element createSuiteElement(Document doc,
0908: String name, String innerText) {
0909: Element el = createSuiteElement(doc, name);
0910: el.appendChild(doc.createTextNode(innerText));
0911: return el;
0912: }
0913:
0914: /**
0915: * Find packages in public-packages or friend-packages section. Method
0916: * considers both <em>package</em> and <em>subpackages</em> elements with
0917: * the recursivity flag set appropriately for returned entries.
0918: */
0919: private static Set<ManifestManager.PackageExport> findAllPackages(
0920: Element parent) {
0921: Set<ManifestManager.PackageExport> packages = new HashSet<ManifestManager.PackageExport>();
0922: for (Element pkgEl : Util.findSubElements(parent)) {
0923: if (PACKAGE.equals(pkgEl.getTagName())) {
0924: packages.add(new ManifestManager.PackageExport(Util
0925: .findText(pkgEl), false));
0926: } else if (SUBPACKAGES.equals(pkgEl.getTagName())) {
0927: packages.add(new ManifestManager.PackageExport(Util
0928: .findText(pkgEl), true));
0929: }
0930: }
0931: return packages;
0932: }
0933:
0934: /**
0935: * Utility method for finding public packages. Method considers both
0936: * <em>package</em> and <em>subpackages</em> elements with the recursivity
0937: * flag set appropriately for returned entries.
0938: *
0939: * @return array of {@link ManifestManager.PackageExport}. May be empty but
0940: * not <code>null</code>.
0941: */
0942: public static ManifestManager.PackageExport[] findPublicPackages(
0943: final Element confData) {
0944: Element ppEl = findPublicPackagesElement(confData);
0945: Set<ManifestManager.PackageExport> pps = new HashSet<ManifestManager.PackageExport>();
0946: if (ppEl != null) {
0947: pps.addAll(findAllPackages(ppEl));
0948: }
0949: ppEl = findFriendsElement(confData);
0950: if (ppEl != null) {
0951: pps.addAll(findAllPackages(ppEl));
0952: }
0953: return pps.isEmpty() ? ManifestManager.EMPTY_EXPORTED_PACKAGES
0954: : pps.toArray(new ManifestManager.PackageExport[pps
0955: .size()]);
0956: }
0957:
0958: /** Utility method for finding friend. */
0959: public static String[] findFriends(final Element confData) {
0960: Element friendsEl = findFriendsElement(confData);
0961: if (friendsEl != null) {
0962: Set<String> friends = new TreeSet<String>();
0963: for (Element friendEl : Util.findSubElements(friendsEl)) {
0964: if (FRIEND.equals(friendEl.getTagName())) {
0965: friends.add(Util.findText(friendEl));
0966: }
0967: }
0968: return friends.toArray(new String[friends.size()]);
0969: }
0970: return null;
0971: }
0972:
0973: /**
0974: * Generates a basic <em>project.xml</em> templates into the given
0975: * <code>projectXml</code> for <em>standalone</em> or <em>module in
0976: * suite</em> module.
0977: */
0978: static void generateEmptyModuleTemplate(FileObject projectXml,
0979: String cnb, NbModuleType moduleType) throws IOException {
0980:
0981: Document prjDoc = XMLUtil.createDocument("project", PROJECT_NS,
0982: null, null); // NOI18N
0983:
0984: // generate general project elements
0985: Element typeEl = prjDoc.createElementNS(PROJECT_NS, "type"); // NOI18N
0986: typeEl.appendChild(prjDoc
0987: .createTextNode(NbModuleProjectType.TYPE));
0988: prjDoc.getDocumentElement().appendChild(typeEl);
0989: Element confEl = prjDoc.createElementNS(PROJECT_NS,
0990: "configuration"); // NOI18N
0991: prjDoc.getDocumentElement().appendChild(confEl);
0992:
0993: // generate NB Module project type specific elements
0994: Element dataEl = createModuleElement(confEl.getOwnerDocument(),
0995: DATA);
0996: confEl.appendChild(dataEl);
0997: Document dataDoc = dataEl.getOwnerDocument();
0998: dataEl.appendChild(createModuleElement(dataDoc, CODE_NAME_BASE,
0999: cnb));
1000: Element moduleTypeEl = createTypeElement(dataDoc, moduleType);
1001: if (moduleTypeEl != null) {
1002: dataEl.appendChild(moduleTypeEl);
1003: }
1004: dataEl.appendChild(createModuleElement(dataDoc,
1005: MODULE_DEPENDENCIES));
1006: dataEl
1007: .appendChild(createModuleElement(dataDoc,
1008: PUBLIC_PACKAGES));
1009:
1010: // store document to disk
1011: ProjectXMLManager.safelyWrite(projectXml, prjDoc);
1012: }
1013:
1014: /**
1015: * Create a library wrapper project.xml.
1016: *
1017: * @param publicPackages set of <code>String</code>s representing the packages
1018: * @param extensions <key=runtime path(String), value=binary path (String)>
1019: */
1020: static void generateLibraryModuleTemplate(FileObject projectXml,
1021: String cnb, NbModuleType moduleType, Set publicPackages,
1022: Map extensions) throws IOException {
1023:
1024: Document prjDoc = XMLUtil.createDocument("project", PROJECT_NS,
1025: null, null); // NOI18N
1026:
1027: // generate general project elements
1028: Element typeEl = prjDoc.createElementNS(PROJECT_NS, "type"); // NOI18N
1029: typeEl.appendChild(prjDoc
1030: .createTextNode(NbModuleProjectType.TYPE));
1031: prjDoc.getDocumentElement().appendChild(typeEl);
1032: Element confEl = prjDoc.createElementNS(PROJECT_NS,
1033: "configuration"); // NOI18N
1034: prjDoc.getDocumentElement().appendChild(confEl);
1035:
1036: // generate NB Module project type specific elements
1037: Element dataEl = createModuleElement(confEl.getOwnerDocument(),
1038: DATA);
1039: confEl.appendChild(dataEl);
1040: Document dataDoc = dataEl.getOwnerDocument();
1041: dataEl.appendChild(createModuleElement(dataDoc, CODE_NAME_BASE,
1042: cnb));
1043: Element moduleTypeEl = createTypeElement(dataDoc, moduleType);
1044: if (moduleTypeEl != null) {
1045: dataEl.appendChild(moduleTypeEl);
1046: }
1047: dataEl.appendChild(createModuleElement(dataDoc,
1048: MODULE_DEPENDENCIES));
1049: Element packages = createModuleElement(dataDoc, PUBLIC_PACKAGES);
1050: dataEl.appendChild(packages);
1051: Iterator it = publicPackages.iterator();
1052: while (it.hasNext()) {
1053: packages.appendChild(createModuleElement(dataDoc, PACKAGE,
1054: (String) it.next()));
1055: }
1056: it = extensions.entrySet().iterator();
1057: while (it.hasNext()) {
1058: Element cp = createModuleElement(dataDoc,
1059: CLASS_PATH_EXTENSION);
1060: dataEl.appendChild(cp);
1061: Map.Entry entry = (Map.Entry) it.next();
1062: cp.appendChild(createModuleElement(dataDoc,
1063: CLASS_PATH_RUNTIME_PATH, (String) entry.getKey()));
1064: cp
1065: .appendChild(createModuleElement(dataDoc,
1066: CLASS_PATH_BINARY_ORIGIN, (String) entry
1067: .getValue()));
1068: }
1069:
1070: // store document to disk
1071: ProjectXMLManager.safelyWrite(projectXml, prjDoc);
1072: }
1073:
1074: private static Element createTypeElement(Document dataDoc,
1075: NbModuleType type) {
1076: Element result = null;
1077: if (type == NbModuleProvider.STANDALONE) {
1078: result = createModuleElement(dataDoc, STANDALONE);
1079: } else if (type == NbModuleProvider.SUITE_COMPONENT) {
1080: result = createModuleElement(dataDoc, SUITE_COMPONENT);
1081: }
1082: return result;
1083: }
1084:
1085: /**
1086: * Generates a basic <em>project.xml</em> templates into the given
1087: * <code>projectXml</code> for <em>Suite</em>.
1088: */
1089: public static void generateEmptySuiteTemplate(
1090: FileObject projectXml, String name) throws IOException {
1091: // XXX this method could be moved in a future (depends on how complex
1092: // suite's project.xml will be) to the .suite package dedicated class
1093: Document prjDoc = XMLUtil.createDocument("project", PROJECT_NS,
1094: null, null); // NOI18N
1095:
1096: // generate general project elements
1097: Element typeEl = prjDoc.createElementNS(PROJECT_NS, "type"); // NOI18N
1098: typeEl
1099: .appendChild(prjDoc
1100: .createTextNode(SuiteProjectType.TYPE));
1101: prjDoc.getDocumentElement().appendChild(typeEl);
1102: Element confEl = prjDoc.createElementNS(PROJECT_NS,
1103: "configuration"); // NOI18N
1104: prjDoc.getDocumentElement().appendChild(confEl);
1105:
1106: // generate NB Suite project type specific elements
1107: Element dataEl = createSuiteElement(confEl.getOwnerDocument(),
1108: DATA);
1109: confEl.appendChild(dataEl);
1110: Document dataDoc = dataEl.getOwnerDocument();
1111: dataEl.appendChild(createSuiteElement(dataDoc, "name", name)); // NOI18N
1112:
1113: // store document to disk
1114: ProjectXMLManager.safelyWrite(projectXml, prjDoc);
1115: }
1116:
1117: private static void safelyWrite(FileObject projectXml,
1118: Document prjDoc) throws IOException {
1119: FileLock lock = projectXml.lock();
1120: try {
1121: OutputStream os = projectXml.getOutputStream(lock);
1122: try {
1123: XMLUtil.write(prjDoc, os, "UTF-8"); // NOI18N
1124: } finally {
1125: os.close();
1126: }
1127: } finally {
1128: lock.releaseLock();
1129: }
1130: }
1131:
1132: private Element getConfData() {
1133: if (confData == null) {
1134: confData = project.getPrimaryConfigurationData();
1135: }
1136: return confData;
1137: }
1138:
1139: }
|