001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.nbbuild;
043:
044: import java.io.BufferedReader;
045: import java.io.File;
046: import java.io.FileInputStream;
047: import java.io.FileReader;
048: import java.io.IOException;
049: import java.io.PrintStream;
050: import java.io.PrintWriter;
051: import java.io.StringWriter;
052: import java.util.ArrayList;
053: import java.util.HashSet;
054: import java.util.List;
055: import java.util.Properties;
056: import java.util.Set;
057: import java.util.StringTokenizer;
058: import java.util.TreeSet;
059: import org.apache.tools.ant.BuildException;
060: import org.apache.tools.ant.Task;
061:
062: // XXX should use DOM, not text manipulation
063:
064: /**
065: * Moves classpath from properties to project.xml
066: * @author pzajac
067: */
068: public class FixTestDependencies extends Task {
069: /** entries for unit testing in order to avoid scanning modules
070: */
071: Set<ModuleListParser.Entry> cachedEntries;
072:
073: /** Creates a new instance of FixTestClassPath */
074: public FixTestDependencies() {
075: }
076:
077: String cnb;
078:
079: File projectXmlFile;
080:
081: public void setProjectXml(File projectXml) {
082: projectXmlFile = projectXml;
083:
084: }
085:
086: File propertiesFile;
087:
088: public void setPropertiesFile(File properties) {
089: this .propertiesFile = properties;
090: }
091:
092: public void execute() throws BuildException {
093: // read project xml
094: try {
095: // test mode doesn't override project.xml
096: boolean testFix = getProject().getProperty(
097: "test.fix.dependencies") != null;
098: if (projectXmlFile == null || !projectXmlFile.isFile()) {
099: throw new BuildException(
100: "project.xml file doesn't exist.");
101: }
102: byte xmlBytes[] = new byte[(int) projectXmlFile.length()];
103: FileInputStream prjFis = new FileInputStream(projectXmlFile);
104: try {
105: prjFis.read(xmlBytes);
106: } finally {
107: prjFis.close();
108: }
109: String xml = new String(xmlBytes);
110: String oldXsd = "<data xmlns=\"http://www.netbeans.org/ns/nb-module-project/2";
111: int xsdIndex = xml.indexOf(oldXsd);
112: if (xsdIndex != -1 || testFix) {
113: // increase schema version
114: String part1 = xml.substring(0, xsdIndex
115: + oldXsd.length() - 1);
116: String part2 = xml.substring(
117: xsdIndex + oldXsd.length(), xml.length());
118: xml = part1 + "3" + part2;
119:
120: int projectType = ParseProjectXml.TYPE_NB_ORG;
121: if (xml.contains("<suite-component/>")) {
122: projectType = ParseProjectXml.TYPE_SUITE;
123: } else if (xml.contains("<standalone/>")) {
124: projectType = ParseProjectXml.TYPE_STANDALONE;
125: }
126: //grrr
127: int typeStart = xml.indexOf("<code-name-base>");
128: int typeEnd = xml.indexOf("</code-name-base>");
129: if (typeStart <= 0 || typeEnd <= 0
130: || typeEnd <= typeStart) {
131: throw new BuildException(
132: "Parsing of project.xml failed.");
133: }
134: cnb = xml.substring(
135: typeStart + "<code-name-base>".length(),
136: typeEnd).trim();
137: if (cnb.length() <= 0) {
138: throw new BuildException("Invalid codename base:"
139: + cnb);
140: }
141: // test if project.xml contains test-deps
142: if (xml.contains("<test-dependencies>") && !testFix) {
143: // yes -> exit
144: log("<test-dependencies> already exists.");
145: log("update only schema version");
146: PrintStream ps = new PrintStream(projectXmlFile);
147: ps.print(xml);
148: ps.close();
149: return;
150: }
151: Set<ModuleListParser.Entry> entries = getModuleList(projectType);
152: Set<String> allCnbs = getCNBsFromEntries(entries);
153: // read properties
154:
155: // remove modules and test from properties and put it to project.xml
156:
157: // unittest
158: //
159: Set<String> compileCNB = new TreeSet<String>();
160: Set<String> compileTestCNB = new TreeSet<String>();
161: Set<String> runtimeCNB = new TreeSet<String>();
162: Set<String> runtimeTestCNB = new TreeSet<String>();
163:
164: Properties projectProperties = getTestProperties();
165: readCodeNameBases(compileCNB, compileTestCNB,
166: projectProperties, "test.unit.cp", allCnbs,
167: entries);
168: readCodeNameBases(compileCNB, compileTestCNB,
169: projectProperties, "test.unit.cp.extra",
170: allCnbs, entries);
171: readCodeNameBases(runtimeCNB, runtimeTestCNB,
172: projectProperties, "test.unit.run.cp", allCnbs,
173: entries);
174: readCodeNameBases(runtimeCNB, runtimeTestCNB,
175: projectProperties, "test.unit.run.cp.extra",
176: allCnbs, entries);
177: updateProperties(projectProperties, new String[] {
178: "test.unit.cp", "test.unit.cp.extra",
179: "test.unit.run.cp", "test.unit.run.cp.extra" });
180:
181: StringWriter writer = new StringWriter();
182: PrintWriter buffer = new PrintWriter(writer);
183: buffer.println("");
184: buffer.println(" <test-dependencies>");
185: buffer.println(" <test-type>");
186: buffer.println(" <name>unit</name>");
187: addDependency(buffer, cnb, true, true, false);
188:
189: runtimeCNB.removeAll(compileCNB);
190: //compileCNB.removeAll(runtimeCNB);
191: compileCNB.addAll(compileTestCNB);
192: runtimeTestCNB.removeAll(compileTestCNB);
193: runtimeCNB.addAll(runtimeTestCNB);
194: addDependencies(buffer, compileCNB, compileTestCNB,
195: true, false);
196: addDependencies(buffer, runtimeCNB, runtimeTestCNB,
197: false, false);
198: buffer.println(" </test-type>");
199:
200: // qa functional tests
201: compileCNB.clear();
202: runtimeCNB.clear();
203: compileTestCNB.clear();
204: runtimeTestCNB.clear();
205:
206: readCodeNameBases(compileCNB, compileTestCNB,
207: projectProperties, "test.qa-functional.cp",
208: allCnbs, entries);
209: readCodeNameBases(compileCNB, compileTestCNB,
210: projectProperties,
211: "test.qa-functional.cp.extra", allCnbs, entries);
212:
213: readCodeNameBases(runtimeCNB, runtimeTestCNB,
214: projectProperties,
215: "test.qa-functional.runtime.cp", allCnbs,
216: entries);
217: readCodeNameBases(runtimeCNB, runtimeTestCNB,
218: projectProperties,
219: "test.qa-functional.runtime.extra", allCnbs,
220: entries);
221: if (!compileTestCNB.isEmpty() || !compileCNB.isEmpty()
222: || !runtimeTestCNB.isEmpty()
223: || !runtimeCNB.isEmpty()) {
224: buffer.println(" <test-type>");
225: buffer
226: .println(" <name>qa-functional</name>");
227:
228: addDependencies(buffer, compileCNB, compileTestCNB,
229: true, false);
230: addDependencies(buffer, runtimeCNB, runtimeTestCNB,
231: false, false);
232: buffer.println(" </test-type>");
233: }
234:
235: buffer.println(" </test-dependencies>");
236: updateProperties(projectProperties, new String[] {
237: "test.qa-functional.cp",
238: "test.qa-functional.cp",
239: "test.qa-functional.runtime.cp",
240: "test.qa-functional.runtime.extra" });
241:
242: // merge project properties
243: String MODULE_DEP_END = "</module-dependencies>";
244: int moduleDepEnd = xml.indexOf(MODULE_DEP_END);
245: if (moduleDepEnd == -1) {
246: MODULE_DEP_END = "<module-dependencies/>";
247: moduleDepEnd = xml.indexOf(MODULE_DEP_END);
248: }
249: if (moduleDepEnd == -1) {
250: throw new BuildException(
251: "No module dependency found.");
252: }
253: moduleDepEnd += MODULE_DEP_END.length();
254: StringBuffer resultXml = new StringBuffer();
255: resultXml.append(xml.substring(0, moduleDepEnd));
256: resultXml.append(writer.toString());
257: // windows
258: if (xml.charAt(moduleDepEnd) == '\r') {
259: moduleDepEnd++;
260: }
261: resultXml.append(xml.substring(moduleDepEnd + 1, xml
262: .length()));
263: if (!testFix) {
264: PrintStream ps = new PrintStream(projectXmlFile);
265: ps.print(resultXml);
266: ps.close();
267: } else {
268: System.out.println(resultXml);
269: }
270: }
271:
272: } catch (IOException ex) {
273: throw new BuildException(ex);
274: }
275:
276: // store project.properties and project.xml
277: }
278:
279: private Set<ModuleListParser.Entry> getModuleList(
280: final int projectType) throws IOException {
281: if (cachedEntries == null) {
282: // scan for all modules
283: @SuppressWarnings("unchecked")
284: ModuleListParser listParser = new ModuleListParser(
285: getProject().getProperties(), projectType,
286: getProject());
287: return listParser.findAll();
288: } else {
289: // used by FixTestDependenciesTest
290: return cachedEntries;
291: }
292: }
293:
294: private Set<String> getCNBsFromEntries(
295: Set<ModuleListParser.Entry> entries) {
296: Set<String> cnbs = new HashSet<String>();
297: for (ModuleListParser.Entry e : entries) {
298: cnbs.add(e.getCnb());
299: }
300: return cnbs;
301: }
302:
303: /** parses all codenamebases from path
304: */
305: void readCodeNameBases(Set<String> compileCNB,
306: Set<String> testsCNB, Properties projectPropertis,
307: String property, Set<String> allCnbs,
308: Set<ModuleListParser.Entry> entries) {
309: String prop = projectPropertis.getProperty(property);
310: StringBuffer newProp = new StringBuffer();
311: if (prop != null) {
312: StringTokenizer tokenizer = new StringTokenizer(prop,
313: ";:\\");
314: while (tokenizer.hasMoreTokens()) {
315: String token = tokenizer.nextToken().trim();
316: boolean found = false;
317: if (token.length() > 1) {
318: int lastSlash = token.lastIndexOf("/");
319: int lastDot = token.lastIndexOf(".");
320: // check if the file is module
321: //
322: // the boot.jar is org.netbeans.bootstrap
323: if (token.endsWith("lib/boot.jar")) {
324: compileCNB.add("org.netbeans.bootstrap");
325: found = true;
326: } else if (token.endsWith("core/core.jar")) {
327: compileCNB.add("org.netbeans.core.startup");
328: found = true;
329: } else if (lastSlash != -1 && lastDot != -1
330: && lastSlash + 1 < lastDot) {
331: String codeBaseName = token.substring(
332: lastSlash + 1, lastDot);
333: codeBaseName = codeBaseName.replace('-', '.');
334: if (allCnbs.contains(codeBaseName)) {
335: compileCNB.add(codeBaseName);
336: found = true;
337: } else {
338: String name = token.substring(
339: lastSlash + 1, token.length());
340: // check if the file is wrapped library
341: String wrapCNB = null;
342: for (ModuleListParser.Entry entry : entries) {
343: File extensions[] = entry
344: .getClassPathExtensions();
345: if (extensions != null) {
346: for (File f : extensions) {
347: if (f.getPath().endsWith(name)) {
348: if (wrapCNB != null) {
349: // collision
350: found = false;
351: System.out
352: .println("wrapped? "
353: + entry
354: .getCnb()
355: + " -> "
356: + token
357: + " = "
358: + f);
359: } else {
360: wrapCNB = entry
361: .getCnb();
362: found = true;
363: }
364: }
365: }
366: }
367:
368: }
369: if (found && wrapCNB != null
370: && allCnbs.contains(wrapCNB)) {
371: compileCNB.add(wrapCNB);
372: }
373: }
374: // check if the dependency is dependency on test
375: } else {
376: int prjEnd = token
377: .indexOf("/build/test/unit/class");
378: if (prjEnd != -1) {
379: // get the project folder
380: int firstSlash = token.indexOf("/");
381: if (firstSlash != -1
382: && firstSlash + 1 < prjEnd) {
383: String prjFolder = token.substring(
384: firstSlash + 1, prjEnd);
385: String codebaseName = getCNBForFolder(
386: prjFolder, entries);
387: if (codebaseName == null) {
388: log("No code name base found for file "
389: + token);
390: } else {
391: testsCNB.add(codebaseName);
392: found = true;
393: }
394: }
395: }
396: }
397: // check if the file is file
398: //
399: if (found == false) {
400: if (newProp.length() > 0) {
401: newProp.append(":");
402: }
403: // windows platform
404: token = token.replace(File.separatorChar, '/');
405: newProp.append(token);
406: }
407: }
408: } // while
409: projectPropertis.setProperty(property, newProp.toString());
410: }
411: }
412:
413: private void addDependencies(PrintWriter buffer,
414: Set<String> moduleCNB, Set<String> testCNB,
415: boolean compile, boolean recursive) {
416: for (String cnb : moduleCNB) {
417: addDependency(buffer, cnb, compile, recursive, testCNB
418: .contains(cnb));
419: }
420: }
421:
422: private void addDependency(PrintWriter buffer, String cnb,
423: boolean compile, boolean recursive, boolean test) {
424: buffer.println(" <test-dependency>");
425: buffer.println(" <code-name-base>" + cnb
426: + "</code-name-base>");
427: if (recursive) {
428: buffer.println(" <recursive/>");
429: }
430: if (compile) {
431: buffer
432: .println(" <compile-dependency/>");
433: }
434: if (test) {
435: buffer.println(" <test/>");
436: }
437: buffer.println(" </test-dependency>");
438:
439: }
440:
441: private Properties getTestProperties() throws IOException {
442: if (propertiesFile == null || !propertiesFile.isFile()) {
443: throw new BuildException("Property file doesn't exist");
444: }
445: Properties props = new Properties();
446: FileInputStream fis = new FileInputStream(propertiesFile);
447: try {
448: props.load(fis);
449: } finally {
450: fis.close();
451: }
452: return props;
453: }
454:
455: /** @return codeNameBase of project for relative folder prjFolder
456: */
457: private String getCNBForFolder(String prjFolder,
458: Set<ModuleListParser.Entry> entries) {
459: for (ModuleListParser.Entry elem : entries) {
460: if (prjFolder.equals(elem.getNetbeansOrgPath())) {
461: return elem.getCnb();
462: }
463: }
464: return null;
465: }
466:
467: private void updateProperties(Properties projectProperties,
468: String names[]) {
469: try {
470:
471: // read properties
472: BufferedReader reader = new BufferedReader(new FileReader(
473: propertiesFile));
474: List<String> lines = new ArrayList<String>();
475: String line = null;
476: while ((line = reader.readLine()) != null) {
477: lines.add(line);
478: }
479: reader.close();
480:
481: // merge properties
482: for (String propName : names) {
483: String value = projectProperties.getProperty(propName);
484: lines = replaceProperty(propName, value, lines);
485: }
486: // store properties
487: PrintStream ps = new PrintStream(propertiesFile);
488: for (String l : lines) {
489: ps.println(l);
490: }
491: ps.close();
492:
493: } catch (IOException ioe) {
494: throw new BuildException(ioe);
495: }
496:
497: }
498:
499: private List<String> replaceProperty(String name, String value,
500: List<String> lines) {
501: List<String> retLines = new ArrayList<String>();
502: for (int i = 0; i < lines.size(); i++) {
503: String line = lines.get(i);
504: String trimmedLine = line.trim();
505: int eqIdx = trimmedLine.indexOf("=");
506: if (eqIdx != -1) {
507: String pName = line.substring(0, eqIdx).trim();
508: if (pName.equals(name)) {
509: // skip property
510: for (; i < lines.size()
511: && lines.get(i).trim().endsWith("\\"); i++)
512: ;
513: // append new property
514: if (value != null && !value.trim().equals("")) {
515: retLines.add(name + "=" + value);
516: }
517: continue;
518: }
519: }
520: // either empty line, comment or other property
521: retLines.add(line);
522: }
523: return retLines;
524: }
525:
526: }
|