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: package org.netbeans.api.java.source;
042:
043: import com.sun.source.util.JavacTask;
044: import java.beans.PropertyVetoException;
045: import java.io.File;
046: import java.io.IOException;
047: import java.net.URL;
048: import java.util.ArrayList;
049: import java.util.Arrays;
050: import java.util.LinkedList;
051: import java.util.List;
052: import java.util.Map;
053: import java.util.WeakHashMap;
054: import java.util.concurrent.CountDownLatch;
055: import java.util.concurrent.TimeUnit;
056: import java.util.regex.Pattern;
057: import javax.swing.event.ChangeListener;
058: import junit.framework.Assert;
059: import org.netbeans.api.java.classpath.ClassPath;
060: import org.netbeans.api.java.queries.SourceForBinaryQuery;
061: import org.netbeans.api.java.source.JavaSource.Phase;
062: import org.netbeans.junit.NbTestCase;
063: import org.netbeans.modules.java.JavaDataLoader;
064: import org.netbeans.modules.java.source.ActivatedDocumentListener;
065: import org.netbeans.modules.java.source.usages.IndexUtil;
066: import org.netbeans.modules.java.source.usages.RepositoryUpdater;
067: import org.netbeans.spi.java.classpath.ClassPathProvider;
068: import org.netbeans.spi.java.classpath.support.ClassPathSupport;
069: import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation;
070: import org.netbeans.spi.java.queries.SourceLevelQueryImplementation;
071: import org.openide.ErrorManager;
072: import org.openide.filesystems.FileObject;
073: import org.openide.filesystems.FileStateInvalidException;
074: import org.openide.filesystems.FileSystem;
075: import org.openide.filesystems.FileUtil;
076: import org.openide.filesystems.LocalFileSystem;
077: import org.openide.filesystems.MultiFileSystem;
078: import org.openide.filesystems.Repository;
079: import org.openide.filesystems.URLMapper;
080: import org.openide.filesystems.XMLFileSystem;
081: import org.openide.util.Lookup;
082: import org.openide.util.Utilities;
083: import org.openide.util.lookup.Lookups;
084: import org.openide.util.lookup.ProxyLookup;
085: import org.xml.sax.SAXException;
086:
087: /**
088: *
089: * @author Jan Lahoda
090: */
091: public final class SourceUtilsTestUtil extends ProxyLookup {
092:
093: private static SourceUtilsTestUtil DEFAULT_LOOKUP = null;
094:
095: public SourceUtilsTestUtil() {
096: Assert.assertNull(DEFAULT_LOOKUP);
097: DEFAULT_LOOKUP = this ;
098: }
099:
100: /**
101: * Set the global default lookup with some fixed instances including META-INF/services/*.
102: */
103: /**
104: * Set the global default lookup with some fixed instances including META-INF/services/*.
105: */
106: public static void setLookup(Object[] instances, ClassLoader cl) {
107: DEFAULT_LOOKUP.setLookups(new Lookup[] {
108: Lookups.fixed(instances), Lookups.metaInfServices(cl),
109: Lookups.singleton(cl), });
110: }
111:
112: private static Object[] extraLookupContent = null;
113:
114: public static void prepareTest(String[] additionalLayers,
115: Object[] additionalLookupContent) throws IOException,
116: SAXException, PropertyVetoException {
117: URL[] layers = new URL[additionalLayers.length];
118:
119: for (int cntr = 0; cntr < additionalLayers.length; cntr++) {
120: layers[cntr] = Thread.currentThread()
121: .getContextClassLoader().getResource(
122: additionalLayers[cntr]);
123: }
124:
125: XMLFileSystem xmlFS = new XMLFileSystem();
126: xmlFS.setXmlUrls(layers);
127:
128: FileSystem system = new MultiFileSystem(new FileSystem[] {
129: FileUtil.createMemoryFileSystem(), xmlFS });
130:
131: Repository repository = new Repository(system);
132: extraLookupContent = new Object[additionalLookupContent.length + 1];
133:
134: System.arraycopy(additionalLookupContent, 0,
135: extraLookupContent, 1, additionalLookupContent.length);
136:
137: extraLookupContent[0] = repository;
138:
139: DEFAULT_LOOKUP.setLookup(extraLookupContent,
140: SourceUtilsTestUtil.class.getClassLoader());
141:
142: SourceUtilsTestUtil2.disableLocks();
143: }
144:
145: static {
146: SourceUtilsTestUtil.class.getClassLoader()
147: .setDefaultAssertionStatus(true);
148: System.setProperty("org.openide.util.Lookup",
149: SourceUtilsTestUtil.class.getName());
150: Assert.assertEquals(SourceUtilsTestUtil.class, Lookup
151: .getDefault().getClass());
152: }
153:
154: public static void prepareTest(FileObject sourceRoot,
155: FileObject buildRoot, FileObject cache) throws Exception {
156: prepareTest(sourceRoot, buildRoot, cache, new FileObject[0]);
157: }
158:
159: public static void prepareTest(FileObject sourceRoot,
160: FileObject buildRoot, FileObject cache,
161: FileObject[] classPathElements) throws Exception {
162: if (extraLookupContent == null)
163: prepareTest(new String[0], new Object[0]);
164:
165: Object[] lookupContent = new Object[extraLookupContent.length + 4];
166:
167: System.arraycopy(extraLookupContent, 0, lookupContent, 4,
168: extraLookupContent.length);
169:
170: lookupContent[0] = new TestProxyClassPathProvider(sourceRoot,
171: buildRoot, classPathElements);
172: lookupContent[1] = new TestSourceForBinaryQuery(sourceRoot,
173: classPathElements);
174: lookupContent[2] = new TestSourceLevelQueryImplementation();
175: lookupContent[3] = JavaDataLoader.findObject(
176: JavaDataLoader.class, true);
177:
178: setLookup(lookupContent, SourceUtilsTestUtil.class
179: .getClassLoader());
180:
181: IndexUtil.setCacheFolder(FileUtil.toFile(cache));
182: }
183:
184: private static Map<FileObject, String> file2SourceLevel = new WeakHashMap<FileObject, String>();
185:
186: public static void setSourceLevel(FileObject file, String level) {
187: file2SourceLevel.put(file, level);
188: }
189:
190: /**This method assures that all java classes under sourceRoot are compiled,
191: * and the caches are created for them.
192: */
193: public static void compileRecursively(FileObject sourceRoot)
194: throws Exception {
195: List<FileObject> queue = new LinkedList();
196:
197: queue.add(sourceRoot);
198:
199: while (!queue.isEmpty()) {
200: FileObject file = queue.remove(0);
201:
202: if (file.isData()) {
203: CountDownLatch l = RepositoryUpdater.getDefault()
204: .scheduleCompilationAndWait(file, sourceRoot);
205:
206: l.await(60, TimeUnit.SECONDS);
207: } else {
208: queue.addAll(Arrays.asList(file.getChildren()));
209: }
210: }
211: }
212:
213: private static List<URL> bootClassPath;
214:
215: public static synchronized List<URL> getBootClassPath() {
216: if (bootClassPath == null) {
217: try {
218: String cp = System.getProperty("sun.boot.class.path");
219: List<URL> urls = new ArrayList<URL>();
220: String[] paths = cp.split(Pattern.quote(System
221: .getProperty("path.separator")));
222:
223: for (String path : paths) {
224: File f = new File(path);
225:
226: if (!f.canRead())
227: continue;
228:
229: FileObject fo = FileUtil.toFileObject(f);
230:
231: if (FileUtil.isArchiveFile(fo)) {
232: fo = FileUtil.getArchiveRoot(fo);
233: }
234:
235: if (fo != null) {
236: urls.add(fo.getURL());
237: }
238: }
239:
240: bootClassPath = urls;
241: } catch (FileStateInvalidException e) {
242: ErrorManager.getDefault().notify(e);
243: }
244: }
245:
246: return bootClassPath;
247: }
248:
249: private static class TestSourceForBinaryQuery implements
250: SourceForBinaryQueryImplementation {
251:
252: private FileObject sourceRoot;
253: private List<FileObject> classPathElements;
254:
255: public TestSourceForBinaryQuery(FileObject sourceRoot,
256: FileObject[] classPathElements) {
257: this .sourceRoot = sourceRoot;
258: this .classPathElements = Arrays.asList(classPathElements);
259: }
260:
261: public SourceForBinaryQuery.Result findSourceRoots(
262: URL binaryRoot) {
263: if (getBootClassPath().contains(binaryRoot))
264: return null;
265:
266: if (classPathElements.contains(URLMapper
267: .findFileObject(binaryRoot)))
268: return null;
269:
270: return new SourceForBinaryQuery.Result() {
271: public FileObject[] getRoots() {
272: return new FileObject[] { sourceRoot, };
273: }
274:
275: public void addChangeListener(ChangeListener l) {
276: }
277:
278: public void removeChangeListener(ChangeListener l) {
279: }
280: };
281: }
282:
283: }
284:
285: private static class TestProxyClassPathProvider implements
286: ClassPathProvider {
287:
288: private FileObject sourceRoot;
289: private FileObject buildRoot;
290: private FileObject[] classPathElements;
291:
292: public TestProxyClassPathProvider(FileObject sourceRoot,
293: FileObject buildRoot, FileObject[] classPathElements) {
294: this .sourceRoot = sourceRoot;
295: this .buildRoot = buildRoot;
296: this .classPathElements = classPathElements;
297: }
298:
299: public ClassPath findClassPath(FileObject file, String type) {
300: try {
301: if (ClassPath.BOOT == type) {
302: return ClassPathSupport
303: .createClassPath(getBootClassPath()
304: .toArray(new URL[0]));
305: }
306:
307: if (ClassPath.SOURCE == type) {
308: return ClassPathSupport
309: .createClassPath(new FileObject[] { sourceRoot });
310: }
311:
312: if (ClassPath.COMPILE == type) {
313: return ClassPathSupport
314: .createClassPath(classPathElements);
315: }
316:
317: if (ClassPath.EXECUTE == type) {
318: return ClassPathSupport
319: .createClassPath(new FileObject[] { buildRoot });
320: }
321: } catch (Exception e) {
322: e.printStackTrace();
323: }
324: return null;
325: }
326:
327: }
328:
329: public static class TestSourceLevelQueryImplementation implements
330: SourceLevelQueryImplementation {
331:
332: public String getSourceLevel(FileObject javaFile) {
333: String level = file2SourceLevel.get(javaFile);
334:
335: if (level == null) {
336: if (javaFile.isFolder()) {
337: for (FileObject data : file2SourceLevel.keySet()) {
338: if (FileUtil.isParentOf(javaFile, data)) {
339: return file2SourceLevel.get(data);
340: }
341: }
342: }
343: return "1.5";
344: } else
345: return level;
346: }
347:
348: }
349:
350: /**Copied from org.netbeans.api.project.
351: * Create a scratch directory for tests.
352: * Will be in /tmp or whatever, and will be empty.
353: * If you just need a java.io.File use clearWorkDir + getWorkDir.
354: */
355: public static FileObject makeScratchDir(NbTestCase test)
356: throws IOException {
357: test.clearWorkDir();
358: File root = test.getWorkDir();
359: assert root.isDirectory() && root.list().length == 0;
360: FileObject fo = FileUtil.toFileObject(root);
361: if (fo != null) {
362: // Presumably using masterfs.
363: return fo;
364: } else {
365: // For the benefit of those not using masterfs.
366: LocalFileSystem lfs = new LocalFileSystem();
367: try {
368: lfs.setRootDirectory(root);
369: } catch (PropertyVetoException e) {
370: assert false : e;
371: }
372: Repository.getDefault().addFileSystem(lfs);
373: return lfs.getRoot();
374: }
375: }
376:
377: public static JavacTask getJavacTaskFor(CompilationInfo info) {
378: return info.impl.getJavacTask();
379: }
380:
381: /** Blocking call for CompilationInfo after given phase is reached.
382: * @param phase to be reached
383: * @return CompilationInfo or null
384: * XXX: REMOVE ME!!!!!!!
385: */
386: public static CompilationInfo getCompilationInfo(JavaSource js,
387: Phase phase) throws IOException {
388: if (phase == null || phase == Phase.MODIFIED) {
389: throw new IllegalArgumentException(String.format(
390: "The %s is not a legal value of phase", phase)); //NOI18N
391: }
392: final DeadlockTask bt = new DeadlockTask(phase);
393: js.runUserActionTask(bt, true);
394: return bt.info;
395: }
396:
397: private static class DeadlockTask implements
398: Task<CompilationController> {
399:
400: private final Phase phase;
401: private CompilationInfo info;
402:
403: public DeadlockTask(Phase phase) {
404: assert phase != null;
405: this .phase = phase;
406: }
407:
408: public void run(CompilationController info) {
409: try {
410: info.toPhase(this .phase);
411: this .info = info;
412: } catch (IOException ioe) {
413: ErrorManager.getDefault().notify(ioe);
414: }
415: }
416:
417: }
418:
419: }
|