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.gsfret.source.usages;
0043:
0044: import java.beans.PropertyChangeEvent;
0045: import java.beans.PropertyChangeListener;
0046: import java.io.File;
0047: import java.io.IOException;
0048: import java.lang.ref.Reference;
0049: import java.lang.ref.WeakReference;
0050: import java.net.URI;
0051: import java.net.URL;
0052: import java.text.ParseException;
0053: import java.util.ArrayList;
0054: import java.util.Collections;
0055: import java.util.HashMap;
0056: import java.util.HashSet;
0057: import java.util.Iterator;
0058: import java.util.LinkedList;
0059: import java.util.List;
0060: import java.util.Map;
0061: import java.util.RandomAccess;
0062: import java.util.Set;
0063: import java.util.Stack;
0064: import java.util.StringTokenizer;
0065: import java.util.Timer;
0066: import java.util.TimerTask;
0067: import java.util.TooManyListenersException;
0068: import java.util.WeakHashMap;
0069: import java.util.concurrent.CountDownLatch;
0070: import java.util.concurrent.atomic.AtomicBoolean;
0071: import java.util.logging.Level;
0072: import java.util.logging.Logger;
0073: import javax.swing.event.ChangeEvent;
0074: import javax.swing.event.ChangeListener;
0075: import org.apache.lucene.document.DateTools;
0076: import org.netbeans.modules.gsf.api.Error;
0077: import org.netbeans.modules.gsf.api.Severity;
0078: import org.netbeans.modules.gsf.api.Indexer;
0079: import org.netbeans.modules.gsf.api.ParseEvent;
0080: import org.netbeans.modules.gsf.api.ParseListener;
0081: import org.netbeans.modules.gsf.api.ParserFile;
0082: import org.netbeans.modules.gsf.api.ParserResult;
0083: import org.netbeans.modules.gsf.api.CancellableTask;
0084: import org.netbeans.modules.gsfpath.api.classpath.ClassPath;
0085: import org.netbeans.modules.gsfpath.api.queries.SourceLevelQuery;
0086: import org.netbeans.api.progress.ProgressHandle;
0087: import org.netbeans.api.progress.ProgressHandleFactory;
0088: import org.netbeans.api.queries.VisibilityQuery;
0089: import org.netbeans.modules.gsf.GsfTaskProvider;
0090: import org.netbeans.napi.gsfret.source.ClasspathInfo;
0091: import org.netbeans.napi.gsfret.source.CompilationInfo;
0092: import org.netbeans.napi.gsfret.source.ParserTaskImpl;
0093: import org.netbeans.napi.gsfret.source.Source;
0094: import org.netbeans.modules.gsf.Language;
0095: import org.netbeans.modules.gsf.LanguageRegistry;
0096: import org.netbeans.modules.gsfret.source.GlobalSourcePath;
0097: import org.netbeans.modules.gsfret.source.SourceAccessor;
0098: import org.netbeans.modules.gsfret.source.parsing.FileObjects;
0099: import org.netbeans.modules.gsfret.source.util.LowMemoryEvent;
0100: import org.netbeans.modules.gsfret.source.util.LowMemoryListener;
0101: import org.netbeans.modules.gsfret.source.util.LowMemoryNotifier;
0102: import org.netbeans.modules.gsfpath.spi.classpath.ClassPathFactory;
0103: import org.netbeans.modules.gsfpath.spi.classpath.support.ClassPathSupport;
0104: import org.openide.filesystems.FileAttributeEvent;
0105: import org.openide.filesystems.FileChangeListener;
0106: import org.openide.filesystems.FileEvent;
0107: import org.openide.filesystems.FileObject;
0108: import org.openide.filesystems.FileRenameEvent;
0109: import org.openide.filesystems.FileStateInvalidException;
0110: import org.openide.filesystems.FileSystem;
0111: import org.openide.filesystems.FileUtil;
0112: import org.openide.filesystems.URLMapper;
0113: import org.openide.util.Exceptions;
0114: import org.openide.util.NbBundle;
0115: import org.openide.util.TopologicalSortException;
0116: import org.openide.util.Utilities;
0117:
0118: /**
0119: * RepositoryUpdater is in charge of maintaining indices of the various classes in
0120: * the system, for use by code completion, go to declaration, etc. The classes include
0121: * not only the user's source and test directories, but jars from the boot class path etc.
0122: * The RepositoryUpdater schedules indexing jobs, watches filesystems for modifications,
0123: * determines whether an index is out of date, etc.
0124: *
0125: * This class is originally from Retouche, under the java/source module. Since it's
0126: * an important and fairly complicated piece of logic, I am trying my best to keep
0127: * my copy in sync with the java one. Therefore, I have left the original formatting
0128: * in place as much as possible. Please don't make gratuitous formatting changes that
0129: * makes diffing harder.
0130: *
0131: * There are some important changes. Obviously, the various javac-specific setup code
0132: * has changed, and I also need to -iterate- over files to be indexed to let the
0133: * potentially multiple language indexers each have a chance to index the file.
0134: * I have also changed references to other parts that have been renamed, such
0135: * as JavaSource => Source, etc.
0136: *
0137: * @author Tomas Zezula
0138: * @author Tor Norbye
0139: */
0140: public class RepositoryUpdater implements PropertyChangeListener,
0141: FileChangeListener {
0142: // Flag for controlling last-minute workaround for issue #120231
0143: // private static final boolean CLOSE_INDICES = Boolean.getBoolean("gsf.closeindices");
0144:
0145: private static final boolean PREINDEXING = Boolean
0146: .getBoolean("gsf.preindexing");
0147:
0148: private static final Logger LOGGER = Logger
0149: .getLogger(RepositoryUpdater.class.getName());
0150: private static final Set<String> ignoredDirectories = parseSet(
0151: "org.netbeans.javacore.ignoreDirectories", "SCCS CVS .svn"); // NOI18N
0152: private static final boolean noscan = Boolean
0153: .getBoolean("netbeans.javacore.noscan"); //NOI18N
0154: private static final boolean PERF_TEST = Boolean
0155: .getBoolean("perf.refactoring.test");
0156: //private static final String PACKAGE_INFO = "package-info.java"; //NOI18N
0157:
0158: private static final int DELAY = Utilities.isWindows() ? 2000
0159: : 1000;
0160:
0161: private static RepositoryUpdater instance;
0162:
0163: private final GlobalSourcePath cpImpl;
0164: private final ClassPath cp;
0165: private final ClassPath ucp;
0166: private final ClassPath binCp;
0167: private Set<URL> scannedRoots;
0168: private Set<URL> scannedBinaries;
0169: private Map<URL, List<URL>> deps; //todo: may be shared with scannedRoots, may save some HashMap.Entry
0170: private Delay delay;
0171: private Work currentWork;
0172: private boolean dirty;
0173: private int noSubmited;
0174:
0175: //Preprocessor support
0176: //private final Map<URL, JavaFileFilterImplementation> filters = Collections.synchronizedMap(new HashMap<URL, JavaFileFilterImplementation>());
0177: private final FilterListener filterListener = new FilterListener();
0178:
0179: /** Creates a new instance of RepositoryUpdater */
0180: private RepositoryUpdater() {
0181: try {
0182: this .scannedRoots = Collections
0183: .synchronizedSet(new HashSet<URL>());
0184: this .scannedBinaries = Collections
0185: .synchronizedSet(new HashSet<URL>());
0186: this .deps = Collections
0187: .synchronizedMap(new HashMap<URL, List<URL>>());
0188: this .delay = new Delay();
0189: this .cpImpl = GlobalSourcePath.getDefault();
0190: this .cpImpl.setExcludesListener(this );
0191: this .cp = ClassPathFactory.createClassPath(this .cpImpl
0192: .getSourcePath());
0193: this .cp.addPropertyChangeListener(this );
0194: this .ucp = ClassPathFactory.createClassPath(this .cpImpl
0195: .getUnknownSourcePath());
0196: this .binCp = ClassPathFactory.createClassPath(this .cpImpl
0197: .getBinaryPath());
0198: this .registerFileSystemListener();
0199: submitBatch();
0200: } catch (TooManyListenersException e) {
0201: throw new IllegalStateException();
0202: }
0203: }
0204:
0205: public ClassPath getScannedSources() {
0206: return this .cp;
0207: }
0208:
0209: public ClassPath getScannedBinaries() {
0210: return this .binCp;
0211: }
0212:
0213: public Map<URL, List<URL>> getDependencies() {
0214: return new HashMap<URL, List<URL>>(this .deps);
0215: }
0216:
0217: public void close() {
0218: this .cp.removePropertyChangeListener(this );
0219: this .unregisterFileSystemListener();
0220: this .delay.cancel();
0221: }
0222:
0223: public void propertyChange(PropertyChangeEvent evt) {
0224: if (ClassPath.PROP_ROOTS.equals(evt.getPropertyName())) {
0225: submitBatch();
0226: } else if (GlobalSourcePath.PROP_INCLUDES.equals(evt
0227: .getPropertyName())) {
0228: ClassPath changedCp = (ClassPath) evt.getNewValue();
0229: assert changedCp != null;
0230: for (ClassPath.Entry e : changedCp.entries()) {
0231: URL root = e.getURL();
0232: scheduleCompilation(root, root, true);
0233: }
0234: }
0235: }
0236:
0237: private synchronized void submitBatch() {
0238: if (this .currentWork == null) {
0239: this .currentWork = Work.batch();
0240: submit(this .currentWork);
0241: } else {
0242: this .dirty = true;
0243: }
0244: }
0245:
0246: public synchronized boolean isScanInProgress() {
0247: return this .noSubmited > 0;
0248: }
0249:
0250: public synchronized void waitScanFinished()
0251: throws InterruptedException {
0252: while (this .noSubmited > 0) {
0253: this .wait();
0254: }
0255: }
0256:
0257: private synchronized boolean isDirty() {
0258: if (this .dirty) {
0259: this .dirty = false;
0260: return true;
0261: } else {
0262: this .currentWork = null;
0263: return false;
0264: }
0265: }
0266:
0267: private synchronized void resetDirty() {
0268: this .dirty = false;
0269: this .currentWork = null;
0270: }
0271:
0272: public void fileRenamed(FileRenameEvent fe) {
0273: final FileObject fo = fe.getFile();
0274: try {
0275: if ((isRelevantSource(fo) || fo.isFolder())
0276: && VisibilityQuery.getDefault().isVisible(fo)) {
0277: final URL root = getOwningSourceRoot(fo);
0278: if (root != null) {
0279: String originalName = fe.getName();
0280: final String originalExt = fe.getExt();
0281: if (originalExt.length() > 0) {
0282: originalName = originalName + '.' + originalExt; //NOI18N
0283: }
0284: final File parentFile = FileUtil.toFile(fo
0285: .getParent());
0286: if (parentFile != null) {
0287: final URL original = new File(parentFile,
0288: originalName).toURI().toURL();
0289: submit(Work.delete(original, root, fo
0290: .isFolder()));
0291: delay.post(Work.compile(fo, root));
0292: }
0293: }
0294: } else if (isBinary(fo)
0295: && VisibilityQuery.getDefault().isVisible(fo)) {
0296: final URL root = getOwningBinaryRoot(fo);
0297: if (root != null) {
0298: String originalName = fe.getName();
0299: final String originalExt = fe.getExt();
0300: if (originalExt.length() > 0) {
0301: originalName = originalName + '.' + originalExt; //NOI18N
0302: }
0303: final File parentFile = FileUtil.toFile(fo
0304: .getParent());
0305: if (parentFile != null) {
0306: final URL original = new File(parentFile,
0307: originalName).toURI().toURL();
0308: submit(Work.binary(original, root, fo
0309: .isFolder()));
0310: submit(Work.binary(fo, root));
0311: }
0312: }
0313: }
0314: } catch (IOException ioe) {
0315: Exceptions.printStackTrace(ioe);
0316: }
0317: }
0318:
0319: public void fileAttributeChanged(FileAttributeEvent fe) {
0320: //Not interesting, do nothing
0321: }
0322:
0323: public void fileFolderCreated(FileEvent fe) {
0324: //In ideal case this should do nothing,
0325: //but in Netbeans newly created folder may
0326: //already contain files
0327: final FileObject fo = fe.getFile();
0328: try {
0329: final URL root = getOwningSourceRoot(fo);
0330: if (root != null
0331: && VisibilityQuery.getDefault().isVisible(fo)) {
0332: scheduleCompilation(fo, root);
0333: }
0334: } catch (IOException ioe) {
0335: Exceptions.printStackTrace(ioe);
0336: }
0337: }
0338:
0339: public void fileDeleted(FileEvent fe) {
0340: final FileObject fo = fe.getFile();
0341: final boolean isFolder = fo.isFolder();
0342: try {
0343: boolean relevantSource = isRelevantSource(fo);
0344: if (!relevantSource
0345: && "content/unknown".equals(fo.getMIMEType())) { // NOI18N
0346: // When deleted, file objects lose their mimetypes...
0347: relevantSource = true;
0348: }
0349: if ((relevantSource || isFolder)
0350: && VisibilityQuery.getDefault().isVisible(fo)) {
0351: final URL root = getOwningSourceRoot(fo);
0352: if (root != null) {
0353: submit(Work.delete(fo, root, isFolder));
0354: }
0355: } else if ((isBinary(fo) || isFolder)
0356: && VisibilityQuery.getDefault().isVisible(fo)) {
0357: final URL root = getOwningBinaryRoot(fo);
0358: if (root != null) {
0359: submit(Work.binary(fo, root));
0360: }
0361: }
0362: } catch (IOException ioe) {
0363: Exceptions.printStackTrace(ioe);
0364: }
0365: }
0366:
0367: public void fileDataCreated(FileEvent fe) {
0368: final FileObject fo = fe.getFile();
0369: try {
0370: if (isRelevantSource(fo)
0371: && VisibilityQuery.getDefault().isVisible(fo)) {
0372: final URL root = getOwningSourceRoot(fo);
0373: if (root != null) {
0374: postCompilation(fo, root);
0375: }
0376: } else if (isBinary(fo)
0377: && VisibilityQuery.getDefault().isVisible(fo)) {
0378: final URL root = getOwningBinaryRoot(fo);
0379: if (root != null) {
0380: submit(Work.binary(fo, root));
0381: }
0382: }
0383: } catch (IOException ioe) {
0384: Exceptions.printStackTrace(ioe);
0385: }
0386: }
0387:
0388: public void fileChanged(FileEvent fe) {
0389: final FileObject fo = fe.getFile();
0390: try {
0391: if (isRelevantSource(fo)
0392: && VisibilityQuery.getDefault().isVisible(fo)) {
0393: final URL root = getOwningSourceRoot(fo);
0394: if (root != null) {
0395: postCompilation(fo, root);
0396: }
0397: } else if (isBinary(fo)
0398: && VisibilityQuery.getDefault().isVisible(fo)) {
0399: final URL root = getOwningBinaryRoot(fo);
0400: if (root != null) {
0401: submit(Work.binary(fo, root));
0402: }
0403: }
0404: } catch (IOException ioe) {
0405: Exceptions.printStackTrace(ioe);
0406: }
0407: }
0408:
0409: public final void scheduleCompilation(final FileObject fo,
0410: final FileObject root) throws IOException {
0411: URL foURL = fo.getURL();
0412: URL rootURL = root.getURL();
0413: assert "file".equals(foURL.getProtocol())
0414: && "file".equals(rootURL.getProtocol());
0415: scheduleCompilation(foURL, rootURL, fo.isFolder());
0416: }
0417:
0418: private final void scheduleCompilation(final FileObject fo,
0419: final URL root) throws IOException {
0420: scheduleCompilation(fo.getURL(), root, fo.isFolder());
0421: }
0422:
0423: private final void scheduleCompilation(final URL file,
0424: final URL root, boolean isFolder) {
0425: submit(Work.compile(file, root, isFolder));
0426: }
0427:
0428: private final void postCompilation(final FileObject file,
0429: final URL root) throws FileStateInvalidException {
0430: delay.post(Work.compile(file, root));
0431: }
0432:
0433: /**
0434: * This method is only for unit tests.
0435: * Test can schedule compilation and wait on the returned {@link CountDownLatch}
0436: * until the compilation is finished.
0437: * @param folder to be compiled
0438: * @param root the source root. The folder has to be either under the root or
0439: * equal to the root.
0440: * @return {@link CountDownLatch} to wait on.
0441: */
0442: public final CountDownLatch scheduleCompilationAndWait(
0443: final FileObject folder, final FileObject root)
0444: throws IOException {
0445: CountDownLatch[] latch = new CountDownLatch[1];
0446: submit(Work.compile(folder, root.getURL(), latch));
0447: return latch[0];
0448: }
0449:
0450: private void submit(final Work work) {
0451: if (!noscan) {
0452: synchronized (this ) {
0453: this .noSubmited++;
0454: }
0455: final CompileWorker cw = new CompileWorker(work);
0456: SourceAccessor.getINSTANCE().runSpecialTask(cw,
0457: Source.Priority.MAX);
0458: }
0459: }
0460:
0461: private void registerFileSystemListener() {
0462: FileUtil.addFileChangeListener(this );
0463: }
0464:
0465: private void unregisterFileSystemListener() {
0466: FileUtil.removeFileChangeListener(this );
0467: }
0468:
0469: private URL getOwningSourceRoot(final FileObject fo) {
0470: if (fo == null) {
0471: return null;
0472: }
0473: List<URL> clone = new ArrayList(this .scannedRoots);
0474: for (URL root : clone) {
0475: FileObject rootFo = URLMapper.findFileObject(root);
0476: if (rootFo != null && FileUtil.isParentOf(rootFo, fo)) {
0477: return root;
0478: }
0479: }
0480: return null;
0481: }
0482:
0483: private URL getOwningBinaryRoot(final FileObject fo) {
0484: if (fo == null) {
0485: return null;
0486: }
0487: try {
0488: synchronized (this .scannedBinaries) {
0489: URL foURL = fo.getURL();
0490: for (URL root : this .scannedBinaries) {
0491: URL fileURL = FileUtil.getArchiveFile(root);
0492: boolean archive = true;
0493: if (fileURL == null) {
0494: fileURL = root;
0495: archive = false;
0496: }
0497: String filePath = fileURL.getPath();
0498: String foPath = foURL.getPath();
0499: if (filePath.equals(foPath)) {
0500: return root;
0501: }
0502: if (!archive && foPath.startsWith(filePath)) {
0503: return root;
0504: }
0505: }
0506: }
0507: } catch (FileStateInvalidException fsi) {
0508: Exceptions.printStackTrace(fsi);
0509: }
0510: return null;
0511: }
0512:
0513: /**
0514: * Temporary implementation which does not care about
0515: * extended mime types like text/x-something+x-java
0516: */
0517: public static boolean isRelevantSource(final FileObject fo) {
0518: if (fo.isFolder()) {
0519: return false;
0520: }
0521:
0522: if (LanguageRegistry.getInstance()
0523: .isSupported(fo.getMIMEType())) {
0524: return true;
0525: }
0526:
0527: return false;
0528: }
0529:
0530: private static boolean isBinary(final FileObject fo) {
0531: return false;
0532: /* XXX TODO no support for binary persistence files yet, such as compiled ruby files etc.
0533: if (fo.isFolder()) {
0534: return false;
0535: }
0536: String ext = fo.getExt().toLowerCase();
0537: if (FileObjects.CLASS.equals(ext) ||
0538: FileObjects.JAR.equals(ext) ||
0539: FileObjects.ZIP.equals(ext)) {
0540: return true;
0541: }
0542: return false;
0543: */
0544: }
0545:
0546: private static enum WorkType {
0547: COMPILE_BATCH, COMPILE_CONT, COMPILE, DELETE, UPDATE_BINARY, FILTER_CHANGED
0548: };
0549:
0550: private static class Work {
0551: private final WorkType workType;
0552: private final CountDownLatch latch;
0553:
0554: protected Work(WorkType workType, CountDownLatch latch) {
0555: assert workType != null;
0556: this .workType = workType;
0557: this .latch = latch;
0558: }
0559:
0560: public WorkType getType() {
0561: return this .workType;
0562: }
0563:
0564: public void finished() {
0565: if (this .latch != null) {
0566: this .latch.countDown();
0567: }
0568: }
0569:
0570: public static Work batch() {
0571: return new Work(WorkType.COMPILE_BATCH, null);
0572: }
0573:
0574: public static Work compile(final FileObject file, final URL root)
0575: throws FileStateInvalidException {
0576: return compile(file.getURL(), root, file.isFolder());
0577: }
0578:
0579: public static Work compile(final URL file, final URL root,
0580: boolean isFolder) {
0581: assert file != null && root != null;
0582: return new SingleRootWork(WorkType.COMPILE, file, root,
0583: isFolder, null);
0584: }
0585:
0586: public static Work compile(final FileObject file,
0587: final URL root, CountDownLatch[] latch)
0588: throws FileStateInvalidException {
0589: assert file != null && root != null;
0590: assert latch != null && latch.length == 1
0591: && latch[0] == null;
0592: latch[0] = new CountDownLatch(1);
0593: return new SingleRootWork(WorkType.COMPILE, file.getURL(),
0594: root, file.isFolder(), latch[0]);
0595: }
0596:
0597: public static Work delete(final FileObject file,
0598: final URL root, final boolean isFolder)
0599: throws FileStateInvalidException {
0600: return delete(file.getURL(), root, file.isFolder());
0601: }
0602:
0603: public static Work delete(final URL file, final URL root,
0604: final boolean isFolder) {
0605: assert file != null && root != null;
0606: return new SingleRootWork(WorkType.DELETE, file, root,
0607: isFolder, null);
0608: }
0609:
0610: public static Work binary(final FileObject file, final URL root)
0611: throws FileStateInvalidException {
0612: return binary(file.getURL(), root, file.isFolder());
0613: }
0614:
0615: public static Work binary(final URL file, final URL root,
0616: boolean isFolder) {
0617: assert file != null && root != null;
0618: return new SingleRootWork(WorkType.UPDATE_BINARY, file,
0619: root, isFolder, null);
0620: }
0621:
0622: public static Work filterChange(final List<URL> roots) {
0623: assert roots != null;
0624: return new MultiRootsWork(WorkType.FILTER_CHANGED, roots,
0625: null);
0626: }
0627:
0628: }
0629:
0630: private static class SingleRootWork extends Work {
0631:
0632: private URL file;
0633: private URL root;
0634: private boolean isFolder;
0635:
0636: public SingleRootWork(WorkType type, URL file, URL root,
0637: boolean isFolder, CountDownLatch latch) {
0638: super (type, latch);
0639: this .file = file;
0640: this .root = root;
0641: this .isFolder = isFolder;
0642: }
0643:
0644: public URL getFile() {
0645: return this .file;
0646: }
0647:
0648: public URL getRoot() {
0649: return this .root;
0650: }
0651:
0652: public boolean isFolder() {
0653: return this .isFolder;
0654: }
0655:
0656: }
0657:
0658: private static class MultiRootsWork extends Work {
0659: private List<URL> roots;
0660:
0661: public MultiRootsWork(WorkType type, List<URL> roots,
0662: CountDownLatch latch) {
0663: super (type, latch);
0664: this .roots = roots;
0665: }
0666:
0667: public List<URL> getRoots() {
0668: return roots;
0669: }
0670: }
0671:
0672: private final class CompileWorker implements
0673: CancellableTask<CompilationInfo> {
0674:
0675: private Work work;
0676: private List<URL> state;
0677: private Set<URL> oldRoots;
0678: private Set<URL> oldBinaries;
0679: private Set<URL> newBinaries;
0680: private ProgressHandle handle;
0681: private final Set<URI> dirtyCrossFiles;
0682: private final Set<URL> ignoreExcludes;
0683: private final AtomicBoolean canceled;
0684:
0685: public CompileWorker(Work work) {
0686: assert work != null;
0687: this .work = work;
0688: this .canceled = new AtomicBoolean(false);
0689: this .dirtyCrossFiles = new HashSet<URI>();
0690: this .ignoreExcludes = new HashSet<URL>();
0691: }
0692:
0693: public void cancel() {
0694: this .canceled.set(true);
0695: }
0696:
0697: public void run(final CompilationInfo nullInfo)
0698: throws IOException {
0699: ClassIndexManager
0700: ./*getDefault().*/writeLock(new ClassIndexManager.ExceptionAction<Void>() {
0701:
0702: @SuppressWarnings("fallthrough")
0703: public Void run() throws IOException {
0704: boolean continuation = false;
0705: try {
0706: final WorkType type = work.getType();
0707: switch (type) {
0708: case FILTER_CHANGED:
0709: try {
0710: final MultiRootsWork mw = (MultiRootsWork) work;
0711: final List<URL> roots = mw
0712: .getRoots();
0713: final Map<URL, List<URL>> depGraph = new HashMap<URL, List<URL>>();
0714: for (URL root : roots) {
0715: findDependencies(root,
0716: new Stack<URL>(),
0717: depGraph, null,
0718: false);
0719: }
0720: state = Utilities
0721: .topologicalSort(roots,
0722: depGraph);
0723: for (java.util.ListIterator<URL> it = state
0724: .listIterator(state
0725: .size()); it
0726: .hasPrevious();) {
0727: final URL rootURL = it
0728: .previous();
0729: it.remove();
0730: updateFolder(rootURL,
0731: rootURL, true,
0732: handle);
0733: }
0734: } catch (final TopologicalSortException tse) {
0735: final IllegalStateException ise = new IllegalStateException();
0736: throw (IllegalStateException) ise
0737: .initCause(tse);
0738: }
0739: break;
0740: case COMPILE_BATCH: {
0741: assert handle == null;
0742: handle = ProgressHandleFactory
0743: .createHandle(NbBundle
0744: .getMessage(
0745: RepositoryUpdater.class,
0746: "MSG_BackgroundCompileStart"));
0747: handle.start();
0748: boolean completed = false;
0749: try {
0750: oldRoots = new HashSet<URL>(
0751: scannedRoots);
0752: oldBinaries = new HashSet<URL>(
0753: scannedBinaries);
0754: final List<ClassPath.Entry> entries = new LinkedList<ClassPath.Entry>();
0755: entries.addAll(cp.entries());
0756: entries.addAll(ucp.entries());
0757: final List<ClassPath.Entry> binaryEntries = binCp
0758: .entries();
0759: newBinaries = new HashSet<URL>();
0760: for (ClassPath.Entry entry : binaryEntries) {
0761: URL binRoot = entry
0762: .getURL();
0763: if (!oldBinaries
0764: .remove(binRoot)) {
0765: newBinaries
0766: .add(binRoot);
0767: }
0768: }
0769: final Map<URL, List<URL>> depGraph = new HashMap<URL, List<URL>>();
0770: for (ClassPath.Entry entry : entries) {
0771: final URL rootURL = entry
0772: .getURL();
0773: findDependencies(rootURL,
0774: new Stack<URL>(),
0775: depGraph,
0776: newBinaries, true);
0777: }
0778: CompileWorker.this .state = Utilities
0779: .topologicalSort(
0780: depGraph
0781: .keySet(),
0782: depGraph);
0783: deps.putAll(depGraph);
0784: completed = true;
0785: } catch (final TopologicalSortException tse) {
0786: final IllegalStateException ise = new IllegalStateException();
0787: throw (IllegalStateException) ise
0788: .initCause(tse);
0789: } finally {
0790: if (!completed) {
0791: resetDirty();
0792: }
0793: }
0794: }
0795: case COMPILE_CONT:
0796: boolean completed = false;
0797: try {
0798: if (!scanRoots()) {
0799: CompileWorker.this .work = new Work(
0800: WorkType.COMPILE_CONT,
0801: null);
0802: SourceAccessor
0803: .getINSTANCE()
0804: .runSpecialTask(
0805: CompileWorker.this ,
0806: Source.Priority.MAX);
0807: continuation = true;
0808: return null;
0809: }
0810: while (isDirty()) {
0811: assert CompileWorker.this .state
0812: .isEmpty();
0813: final List<ClassPath.Entry> entries = new LinkedList<ClassPath.Entry>();
0814: entries
0815: .addAll(cp
0816: .entries());
0817: entries.addAll(ucp
0818: .entries());
0819: final List<ClassPath.Entry> binaryEntries = binCp
0820: .entries();
0821: newBinaries = new HashSet<URL>();
0822: for (ClassPath.Entry entry : binaryEntries) {
0823: URL binRoot = entry
0824: .getURL();
0825: if (!scannedBinaries
0826: .contains(binRoot)) {
0827: newBinaries
0828: .add(binRoot);
0829: } else {
0830: oldBinaries
0831: .remove(binRoot);
0832: }
0833: }
0834: final Map<URL, List<URL>> depGraph = new HashMap<URL, List<URL>>();
0835: for (ClassPath.Entry entry : entries) {
0836: final URL rootURL = entry
0837: .getURL();
0838: findDependencies(
0839: rootURL,
0840: new Stack<URL>(),
0841: depGraph,
0842: newBinaries,
0843: true);
0844: }
0845: try {
0846: CompileWorker.this .state = Utilities
0847: .topologicalSort(
0848: depGraph
0849: .keySet(),
0850: depGraph);
0851: deps.putAll(depGraph);
0852: } catch (final TopologicalSortException tse) {
0853: final IllegalStateException ise = new IllegalStateException();
0854: throw (IllegalStateException) ise
0855: .initCause(tse);
0856: }
0857: if (!scanRoots()) {
0858: CompileWorker.this .work = new Work(
0859: WorkType.COMPILE_CONT,
0860: null);
0861: SourceAccessor
0862: .getINSTANCE()
0863: .runSpecialTask(
0864: CompileWorker.this ,
0865: Source.Priority.MAX);
0866: continuation = true;
0867: return null;
0868: }
0869: }
0870: completed = true;
0871: } finally {
0872: if (!completed && !continuation) {
0873: resetDirty();
0874: }
0875: }
0876: // final ClassIndexManager cim = ClassIndexManager.getDefault();
0877: scannedRoots.removeAll(oldRoots);
0878: deps.keySet().remove(oldRoots);
0879: //if (CLOSE_INDICES) { // HACK - see #120231
0880: // for (URL oldRoot : oldRoots) {
0881: // cim.removeRoot(oldRoot);
0882: //// JavaFileFilterImplementation filter = filters.remove(oldRoot);
0883: //// if (filter != null && !filters.values().contains(filter)) {
0884: //// filter.removeChangeListener(filterListener);
0885: //// }
0886: // }
0887: //}
0888: scannedBinaries
0889: .removeAll(oldBinaries);
0890: // final CachingArchiveProvider cap = CachingArchiveProvider.getDefault();
0891: //if (CLOSE_INDICES) {
0892: // for (URL oldRoot : oldBinaries) {
0893: // cim.removeRoot(oldRoot);
0894: //// cap.removeArchive(oldRoot);
0895: // }
0896: //}
0897: break;
0898: case COMPILE: {
0899: try {
0900: final SingleRootWork sw = (SingleRootWork) work;
0901: final URL file = sw.getFile();
0902: final URL root = sw.getRoot();
0903: if (sw.isFolder()) {
0904: handle = ProgressHandleFactory
0905: .createHandle(NbBundle
0906: .getMessage(
0907: RepositoryUpdater.class,
0908: "MSG_Updating"));
0909: handle.start();
0910: try {
0911: updateFolder(file,
0912: root, false,
0913: handle);
0914: } finally {
0915: handle.finish();
0916: }
0917: } else {
0918: updateFile(file, root);
0919: }
0920: //} catch (Abort abort) {
0921: } catch (Exception abort) {
0922: //Ignore abort
0923: }
0924: break;
0925: }
0926: case DELETE: {
0927: final SingleRootWork sw = (SingleRootWork) work;
0928: final URL file = sw.getFile();
0929: final URL root = sw.getRoot();
0930: delete(file, root, sw.isFolder());
0931: break;
0932: }
0933: case UPDATE_BINARY: {
0934: SingleRootWork sw = (SingleRootWork) work;
0935: final URL file = sw.getFile();
0936: final URL root = sw.getRoot();
0937: updateBinary(file, root);
0938: break;
0939: }
0940: }
0941: return null;
0942: } finally {
0943: if (!continuation) {
0944: synchronized (RepositoryUpdater.this ) {
0945: RepositoryUpdater.this .noSubmited--;
0946: if (RepositoryUpdater.this .noSubmited == 0) {
0947: RepositoryUpdater.this
0948: .notifyAll();
0949: }
0950: }
0951: work.finished();
0952: if (handle != null) {
0953: handle.finish();
0954: }
0955: }
0956: }
0957: }
0958: });
0959: }
0960:
0961: private Map<FileObject, WeakReference<ClassPath>> sourceClassPathsCache = new WeakHashMap<FileObject, WeakReference<ClassPath>>();
0962:
0963: private ClassPath getBootClassPaths(FileObject file, String type/*, FileObject systemRoot*/) {
0964: // Default provider - do this for things like Ruby library files
0965: synchronized (this ) {
0966: ClassPath cp = null;
0967: if (!file.isFolder()) {
0968: //file = systemRoot;
0969: assert false : file;
0970: }
0971: if (file.isFolder()) {
0972: Reference ref = (Reference) this .sourceClassPathsCache
0973: .get(file);
0974: if (ref == null
0975: || (cp = (ClassPath) ref.get()) == null) {
0976: cp = ClassPathSupport
0977: .createClassPath(new FileObject[] { file });
0978: this .sourceClassPathsCache.put(file,
0979: new WeakReference<ClassPath>(cp));
0980: }
0981: }
0982: return cp;
0983: }
0984: }
0985:
0986: private void findDependencies(final URL rootURL,
0987: final Stack<URL> cycleDetector,
0988: final Map<URL, List<URL>> depGraph,
0989: final Set<URL> binaries, final boolean useInitialState) {
0990: if (useInitialState
0991: && RepositoryUpdater.this .scannedRoots
0992: .contains(rootURL)) {
0993: this .oldRoots.remove(rootURL);
0994: return;
0995: }
0996: if (depGraph.containsKey(rootURL)) {
0997: return;
0998: }
0999: final FileObject rootFo = URLMapper.findFileObject(rootURL);
1000: if (rootFo == null) {
1001: return;
1002: }
1003:
1004: // BEGIN TOR MODIFICATIONS
1005: // I don't want to start asking for the ClassPath of directories in the libraries
1006: // since these start yielding Java jars etc.
1007: if (true) {
1008: //if (useInitialState) {
1009: depGraph.put(rootURL, new LinkedList<URL>());
1010:
1011: if (!RepositoryUpdater.this .scannedBinaries
1012: .contains(rootURL)) {
1013: binaries.add(rootURL);
1014: }
1015: oldBinaries.remove(rootURL);
1016: //}
1017: return;
1018: }
1019: // END TOR MODIFICATIONS
1020:
1021: cycleDetector.push(rootURL);
1022: final ClassPath bootPath = ClassPath.getClassPath(rootFo,
1023: ClassPath.BOOT);
1024: final ClassPath compilePath = ClassPath.getClassPath(
1025: rootFo, ClassPath.COMPILE);
1026: final ClassPath[] pathsToResolve = new ClassPath[] {
1027: bootPath, compilePath };
1028: // ClassPath libraryPath = LanguageRegistry.getInstance().getLibraryPaths();
1029: // final ClassPath[] pathsToResolve = libraryPath != null ?
1030: // new ClassPath[] {bootPath, compilePath, libraryPath} :
1031: // new ClassPath[] {bootPath,compilePath};
1032: final List<URL> deps = new LinkedList<URL>();
1033: for (int i = 0; i < pathsToResolve.length; i++) {
1034: final ClassPath pathToResolve = pathsToResolve[i];
1035: if (pathToResolve != null) {
1036: for (ClassPath.Entry entry : pathToResolve
1037: .entries()) {
1038: final URL url = entry.getURL();
1039: final URL[] sourceRoots = RepositoryUpdater.this .cpImpl
1040: .getSourceRootForBinaryRoot(url,
1041: pathToResolve, false);
1042: if (sourceRoots != null) {
1043: for (URL sourceRoot : sourceRoots) {
1044: if (sourceRoot.equals(rootURL)) {
1045: this .ignoreExcludes.add(rootURL);
1046: } else if (!cycleDetector
1047: .contains(sourceRoot)) {
1048: deps.add(sourceRoot);
1049: findDependencies(sourceRoot,
1050: cycleDetector, depGraph,
1051: binaries, useInitialState);
1052: }
1053: }
1054: } else {
1055: if (useInitialState) {
1056: if (!RepositoryUpdater.this .scannedBinaries
1057: .contains(url)) {
1058: binaries.add(url);
1059: }
1060: oldBinaries.remove(url);
1061: }
1062: }
1063: }
1064: }
1065: }
1066: depGraph.put(rootURL, deps);
1067: cycleDetector.pop();
1068: }
1069:
1070: private boolean scanRoots() {
1071:
1072: for (Iterator<URL> it = this .newBinaries.iterator(); it
1073: .hasNext();) {
1074: if (this .canceled.getAndSet(false)) {
1075: return false;
1076: }
1077: final URL rootURL = it.next();
1078: try {
1079: it.remove();
1080: for (IndexerEntry entry : getIndexers()) {
1081: Language language = entry.getLanguage();
1082: final ClassIndexImpl ci = ClassIndexManager
1083: .get(language).createUsagesQuery(
1084: rootURL, false);
1085: }
1086: RepositoryUpdater.this .scannedBinaries.add(rootURL);
1087: // long startT = System.currentTimeMillis();
1088: // ci.getBinaryAnalyser().analyse(rootURL, handle);
1089: // long endT = System.currentTimeMillis();
1090: // if (PERF_TEST) {
1091: // try {
1092: // Class c = Class.forName("org.netbeans.performance.test.utilities.LoggingScanClasspath",true,Thread.currentThread().getContextClassLoader()); // NOI18N
1093: // java.lang.reflect.Method m = c.getMethod("reportScanOfFile", new Class[] {String.class, Long.class}); // NOI18N
1094: // m.invoke(c.newInstance(), new Object[] {rootURL.toExternalForm(), new Long(endT - startT)});
1095: // } catch (Exception e) {
1096: // Exceptions.printStackTrace(e);
1097: // }
1098: // }
1099: } catch (Throwable e) {
1100: if (e instanceof ThreadDeath) {
1101: throw (ThreadDeath) e;
1102: } else {
1103: Exceptions.attachMessage(e, "While scanning: "
1104: + rootURL);
1105: Exceptions.printStackTrace(e);
1106: }
1107: }
1108: }
1109: for (java.util.ListIterator<URL> it = this .state
1110: .listIterator(this .state.size()); it.hasPrevious();) {
1111: if (this .canceled.getAndSet(false)) {
1112: return false;
1113: }
1114: try {
1115: final URL rootURL = it.previous();
1116: it.remove();
1117: if (!oldRoots.remove(rootURL)
1118: && !RepositoryUpdater.this .scannedRoots
1119: .contains(rootURL)) {
1120: long startT = System.currentTimeMillis();
1121: updateFolder(rootURL, rootURL, false, handle);
1122: long endT = System.currentTimeMillis();
1123: if (PERF_TEST) {
1124: try {
1125: Class c = Class
1126: .forName(
1127: "org.netbeans.performance.test.utilities.LoggingScanClasspath",
1128: true,
1129: Thread
1130: .currentThread()
1131: .getContextClassLoader()); // NOI18N
1132: java.lang.reflect.Method m = c
1133: .getMethod("reportScanOfFile",
1134: new Class[] {
1135: String.class,
1136: Long.class }); // NOI18N
1137: m.invoke(c.newInstance(), new Object[] {
1138: rootURL.toExternalForm(),
1139: new Long(endT - startT) });
1140: } catch (Exception e) {
1141: Exceptions.printStackTrace(e);
1142: }
1143: }
1144: if (PREINDEXING) {
1145: // How do I obtain the data folder for this puppy?
1146: for (Language language : LanguageRegistry
1147: .getInstance()) {
1148: if (language.getIndexer() != null) {
1149: Index.preindex(language, rootURL);
1150: }
1151: }
1152: }
1153: }
1154: } catch (Throwable e) {
1155: if (e instanceof ThreadDeath) {
1156: throw (ThreadDeath) e;
1157: } else {
1158: Exceptions.printStackTrace(e);
1159: }
1160: }
1161: }
1162: return true;
1163: }
1164:
1165: private void updateFolder(final URL folder, final URL root,
1166: boolean clean, final ProgressHandle handle)
1167: throws IOException {
1168: final FileObject rootFo = URLMapper.findFileObject(root);
1169: if (rootFo == null) {
1170: return;
1171: }
1172: if (!rootFo.isFolder()) {
1173: LOGGER.warning("Source root has to be a folder: "
1174: + FileUtil.getFileDisplayName(rootFo)); // NOI18N
1175: return;
1176: }
1177: ClassPath sourcePath = ClassPath.getClassPath(rootFo,
1178: ClassPath.SOURCE);
1179: ClassPath bootPath = ClassPath.getClassPath(rootFo,
1180: ClassPath.BOOT);
1181: ClassPath compilePath = ClassPath.getClassPath(rootFo,
1182: ClassPath.COMPILE);
1183: final boolean isInitialCompilation = folder.equals(root);
1184: if (sourcePath == null || bootPath == null
1185: || compilePath == null) {
1186: //LOGGER.warning("Ignoring root with no ClassPath: " + FileUtil.getFileDisplayName(rootFo)); // NOI18N
1187: //return;
1188: ClassPath cp = getBootClassPaths(rootFo,
1189: ClassPath.SOURCE);
1190: if (sourcePath == null) {
1191: sourcePath = cp;
1192: }
1193: if (bootPath == null) {
1194: bootPath = cp;
1195: }
1196: if (compilePath == null) {
1197: compilePath = cp;
1198: }
1199: }
1200: boolean isBoot = isInitialCompilation
1201: && ClassIndexManager.isBootRoot(root);
1202: if (!isBoot) {
1203: String urlString = root.toExternalForm();
1204: if (urlString.indexOf("/vendor/") != -1) {
1205: isBoot = true;
1206: }
1207: }
1208: /*
1209: // XXX This is suspicious
1210: if (!clean && isInitialCompilation) {
1211: //Initial compilation debug messages
1212: if (RepositoryUpdater.this.scannedRoots.contains(root)) {
1213: return;
1214: }
1215: LOGGER.fine("Scanning Root: " + FileUtil.getFileDisplayName(rootFo)); //NOI18N
1216: }
1217: */
1218: try {
1219: File rootFile = FileUtil.toFile(rootFo);
1220: if (rootFile == null) {
1221: FileObject jar = FileUtil.getArchiveFile(rootFo);
1222: rootFile = FileUtil.toFile(jar);
1223: if (rootFile == null) {
1224: // Probably a jar, which sometimes ends up in my updateFolder now because the
1225: // isBinary stuff etc. isn't working yet
1226: return;
1227: }
1228: }
1229: final File folderFile = isInitialCompilation ? rootFile
1230: : FileUtil.normalizeFile(new File(URI
1231: .create(folder.toExternalForm())));
1232: if (handle != null) {
1233: final String message = NbBundle.getMessage(
1234: RepositoryUpdater.class, "MSG_Scannig",
1235: rootFile.getAbsolutePath());
1236: handle.setDisplayName(message);
1237: }
1238: // //Preprocessor support
1239: Object filter = null;
1240: // JavaFileFilterImplementation filter = filters.get(root);
1241: // if (filter == null) {
1242: // filter = JavaFileFilterQuery.getFilter(rootFo);
1243: // if (filter != null) {
1244: // if (!filters.values().contains(filter)) {
1245: // filter.addChangeListener(filterListener);
1246: // }
1247: // filters.put(root, filter);
1248: // }
1249: // }
1250: List<ParserFile> toCompile = new LinkedList<ParserFile>();
1251: final Map<String, List<File>> resources = Collections
1252: .emptyMap();
1253: final LazyFileList children = new LazyFileList(
1254: folderFile);
1255:
1256: // If this is a boot class path, don't update it
1257: boolean isRootFolder = isBoot;
1258: // Known Rails user-project exceptions (not recorded as boot roots since
1259: // they are in the user's project directories)
1260: if (folderFile.getName().equals("vendor")
1261: || folderFile.getName().equals("lib")) { // NOI18N
1262: // lib? Won't that mess up my user projects?
1263: isRootFolder = true;
1264: }
1265:
1266: boolean checkUpToDate = false;
1267: if (isRootFolder) {
1268: if (folderFile.exists() && folderFile.canRead()) {
1269: checkUpToDate = true;
1270: }
1271: }
1272: boolean allUpToDate = checkUpToDate;
1273:
1274: Map<Language, Map<String, String>> timeStamps = new HashMap<Language, Map<String, String>>();
1275:
1276: boolean invalidIndex = false;
1277: for (IndexerEntry entry : getIndexers()) {
1278: Language language = entry.getLanguage();
1279: ClassIndexImpl uqImpl = ClassIndexManager.get(
1280: language).createUsagesQuery(root, true);
1281: assert uqImpl != null;
1282: SourceAnalyser sa = uqImpl.getSourceAnalyser();
1283: assert sa != null;
1284: if (checkUpToDate
1285: && !sa.isUpToDate(null, folderFile
1286: .lastModified())) {
1287: allUpToDate = false;
1288: } else if (isInitialCompilation) {
1289: if (!sa.isValid()) {
1290: invalidIndex = true;
1291: allUpToDate = false;
1292: } else { //if (!isBoot) {
1293: final ClassIndexImpl ci = ClassIndexManager
1294: .get(language).getUsagesQuery(root);
1295: if (ci != null) {
1296: Map<String, String> ts = ci
1297: .getTimeStamps();
1298: if (ts != null && ts.size() > 0) {
1299: timeStamps.put(language, ts);
1300: }
1301: }
1302: }
1303: }
1304: }
1305:
1306: if (allUpToDate) {
1307: return;
1308: }
1309:
1310: if (timeStamps.size() == 0) {
1311: timeStamps = null;
1312: }
1313:
1314: // Set<File> rs = new HashSet<File> ();
1315: ClassPath.Entry entry = null;
1316: final ClasspathInfo cpInfo;
1317: if (!this .ignoreExcludes.contains(root)) {
1318: entry = getClassPathEntry(sourcePath, root);
1319: cpInfo = ClasspathInfoAccessor.INSTANCE.create(
1320: bootPath, compilePath, sourcePath, filter,
1321: true, false);
1322: } else {
1323: cpInfo = ClasspathInfoAccessor.INSTANCE.create(
1324: bootPath, compilePath, sourcePath, filter,
1325: true, true);
1326: }
1327:
1328: // Set<ElementHandle<TypeElement>> removed = isInitialCompilation ? null : new HashSet<ElementHandle<TypeElement>> ();
1329: // Set<ElementHandle<TypeElement>> added = isInitialCompilation ? null : new HashSet<ElementHandle<TypeElement>> ();
1330: Set removed = null;
1331: Set added = null;
1332: for (File child : children) {
1333: String offset = FileObjects.getRelativePath(
1334: rootFile, child);
1335: if (entry == null
1336: || entry.includes(offset.replace(
1337: File.separatorChar, '/'))) {
1338: if (invalidIndex
1339: || clean
1340: || dirtyCrossFiles
1341: .remove(child.toURI())) {
1342: toCompile.add(FileObjects
1343: .fileFileObject(child, rootFile,
1344: isBoot, null/* filter*/));
1345: } else {
1346: final int index = offset.lastIndexOf('.'); //NOI18N
1347: if (index > -1) {
1348: offset = offset.substring(0, index);
1349: }
1350: List<File> files = resources.remove(offset);
1351: if (files == null) {
1352: toCompile
1353: .add(FileObjects
1354: .fileFileObject(child,
1355: rootFile,
1356: isBoot, null/*filter*/));
1357: } else {
1358: // boolean rsf = files.get(0).getName().endsWith(FileObjects.RS);
1359: if (files.get(0).lastModified() < child
1360: .lastModified()) {
1361: toCompile.add(FileObjects
1362: .fileFileObject(child,
1363: rootFile, isBoot,
1364: null/*filter*/));
1365: for (File toDelete : files) {
1366: toDelete.delete();
1367: // if (rsf) {
1368: // rsf = false;
1369: // }
1370: // else {
1371: // String className = FileObjects.getBinaryName(toDelete,classCache);
1372: // sa.delete(toDelete);
1373: // if (removed != null) {
1374: // removed.add(ElementHandleAccessor.INSTANCE.create(ElementKind.OTHER, className));
1375: // }
1376: }
1377: // }
1378: // }
1379: // else if (rsf) {
1380: // files.remove(0);
1381: // rs.addAll(files);
1382: }
1383: }
1384: }
1385: }
1386: }
1387: for (List<File> files : resources.values()) {
1388: for (File toDelete : files) {
1389: // if (!rs.contains(toDelete)) {
1390: toDelete.delete();
1391: if (toDelete.getName()
1392: .endsWith(FileObjects.SIG)) {
1393: //String className = FileObjects.getBinaryName(toDelete,classCache);
1394: //sa.delete(className);
1395: // sa.delete(toDelete);
1396: // if (removed != null) {
1397: // removed.add(ElementHandleAccessor.INSTANCE.create(ElementKind.OTHER, className));
1398: // }
1399: }
1400: // }
1401: }
1402: }
1403: if (!toCompile.isEmpty()) {
1404: if (handle != null) {
1405: // BEGIN TOR MODIFICATIONS
1406: // Show message for "indexing" rather than compiling since I'm not keeping trees around etc - it's
1407: // all used to populate Lucene at this point.
1408: //final String message = NbBundle.getMessage(RepositoryUpdater.class,"MSG_BackgroundCompile",rootFile.getAbsolutePath());
1409: String path = rootFile.getAbsolutePath();
1410: // Shorten path by prefix to ruby location if possible
1411: int rubyIndex = path.indexOf("jruby-1.1RC1");
1412: if (rubyIndex != -1) {
1413: path = path.substring(rubyIndex);
1414: }
1415: final String message = NbBundle.getMessage(
1416: RepositoryUpdater.class,
1417: "MSG_Analyzing", path);
1418: handle.setDisplayName(message);
1419: }
1420: batchCompile(toCompile, rootFo, cpInfo, /*sa,*/
1421: root, dirtyCrossFiles, added, handle,
1422: timeStamps);
1423: }
1424: // store is a noop anyway
1425: // sa.store();
1426:
1427: // if (added != null) {
1428: // assert removed != null;
1429: // Set<ElementHandle<TypeElement>> _at = new HashSet<ElementHandle<TypeElement>> (added); //Added
1430: // Set<ElementHandle<TypeElement>> _rt = new HashSet<ElementHandle<TypeElement>> (removed); //Removed
1431: // _at.removeAll(removed);
1432: // _rt.removeAll(added);
1433: // added.retainAll(removed); //Changed
1434: // uqImpl.typesEvent(_at.isEmpty() ? null : new ClassIndexImplEvent(uqImpl, _at),
1435: // _rt.isEmpty() ? null : new ClassIndexImplEvent (uqImpl,_rt),
1436: // added.isEmpty() ? null : new ClassIndexImplEvent (uqImpl,added));
1437: // }
1438: } finally {
1439: if (!clean && isInitialCompilation) {
1440: RepositoryUpdater.this .scannedRoots.add(root);
1441: }
1442: }
1443: }
1444:
1445: private List<Language> getApplicableIndexers(String mimeType) {
1446: List<Language> languages = null;
1447: for (Language language : LanguageRegistry.getInstance()
1448: .getApplicableLanguages(mimeType)) {
1449: if (language.getIndexer() == null) {
1450: continue;
1451: }
1452:
1453: if (languages == null) {
1454: languages = new ArrayList<Language>(5);
1455: }
1456: languages.add(language);
1457: }
1458:
1459: return languages != null ? languages : Collections
1460: .<Language> emptyList();
1461: }
1462:
1463: private void updateFile(final URL file, final URL root)
1464: throws IOException {
1465: final FileObject fo = URLMapper.findFileObject(file);
1466: if (fo == null) {
1467: return;
1468: }
1469:
1470: List<Language> languages = getApplicableIndexers(fo
1471: .getMIMEType());
1472: if (languages.size() > 0) {
1473: final File rootFile = FileUtil.normalizeFile(new File(
1474: URI.create(root.toExternalForm())));
1475: final File fileFile = FileUtil.toFile(fo);
1476: ParserFile active = FileObjects.fileFileObject(
1477: fileFile, rootFile, false, null/*filter*/);
1478: ParserFile[] activeList = new ParserFile[] { active };
1479: ClasspathInfo cpInfo = ClasspathInfoAccessor.INSTANCE
1480: .create(fo, null/*filter*/, true, false);
1481: ClassPath.Entry entry = getClassPathEntry(cpInfo
1482: .getClassPath(ClasspathInfo.PathKind.SOURCE),
1483: root);
1484: boolean scan = (entry == null || entry.includes(fo));
1485: String sourceLevel = scan ? SourceLevelQuery
1486: .getSourceLevel(fo) : null;
1487: for (Language language : languages) {
1488: assert "file".equals(root.getProtocol()) : "Unexpected protocol of URL: "
1489: + root; //NOI18N
1490: final ClassIndexImpl uqImpl = ClassIndexManager
1491: .get(language)
1492: .createUsagesQuery(root, true);
1493: if (uqImpl != null) {
1494: uqImpl.setDirty(null);
1495: // final JavaFileFilterImplementation filter = JavaFileFilterQuery.getFilter(fo);
1496: //final File classCache = Index.getClassFolder (rootFile);
1497: //final Map <String,List<File>> resources = getAllClassFiles (classCache, FileObjects.getRelativePath(rootFile, fileFile.getParentFile()),false);
1498: // String offset = FileObjects.getRelativePath (rootFile,fileFile);
1499: // final int index = offset.lastIndexOf('.'); //NOI18N
1500: // if (index > -1) {
1501: // offset = offset.substring(0,index);
1502: // }
1503: //List<File> files = resources.remove (offset);
1504: SourceAnalyser sa = uqImpl.getSourceAnalyser();
1505: assert sa != null;
1506: //final Set<ElementHandle<TypeElement>> added = new HashSet<ElementHandle<TypeElement>>();
1507: //final Set<ElementHandle<TypeElement>> removed = new HashSet <ElementHandle<TypeElement>> ();
1508: // TODO: Handle deletions; is this only used for th sig files?
1509: // if (files != null) {
1510: // for (File toDelete : files) {
1511: // toDelete.delete();
1512: // if (toDelete.getName().endsWith(FileObjects.SIG)) {
1513: // String className = FileObjects.getBinaryName (toDelete,classCache);
1514: // sa.delete (className);
1515: // removed.add (ElementHandleAccessor.INSTANCE.create(ElementKind.OTHER, className));
1516: // }
1517: // }
1518: // }
1519: // else {
1520: // sa.delete(FileObjects.convertFolder2Package(offset, '/')); //NOI18N
1521: // }
1522: if (scan) {
1523: final CompilerListener listener = new CompilerListener();
1524: //final JavaFileManager fm = ClasspathInfoAccessor.INSTANCE.getFileManager(cpInfo);
1525: //JavaFileObject active = FileObjects.fileFileObject(fileFile, rootFile, filter);
1526: //JavacTaskImpl jt = JavaSourceAccessor.getINSTANCE().createJavacTask(cpInfo, listener, sourceLevel);
1527: ParserTaskImpl jt = SourceAccessor
1528: .getINSTANCE().createParserTask(
1529: language, cpInfo,
1530: sourceLevel);
1531: //jt.setTaskListener(listener);
1532: jt.setParseListener(listener);
1533: //Iterable<? extends CompilationUnitTree> trees = jt.parse(new JavaFileObject[] {active});
1534: Iterable<ParserResult> trees = jt
1535: .parse(activeList);
1536: //jt.enter();
1537: //jt.analyze ();
1538: //dumpClasses(listener.getEnteredTypes(), fm, root.toExternalForm(), null, ...
1539: //sa.analyse (trees, jt, fm, active, added);
1540: sa.analyse(language, trees, jt, /*fm,*/
1541: active);
1542:
1543: listener.cleanDiagnostics();
1544: }
1545: // sa.store();
1546: // Set<ElementHandle<TypeElement>> _at = new HashSet<ElementHandle<TypeElement>> (added); //Added
1547: // Set<ElementHandle<TypeElement>> _rt = new HashSet<ElementHandle<TypeElement>> (removed); //Removed
1548: // _at.removeAll(removed);
1549: // _rt.removeAll(added);
1550: // added.retainAll(removed); //Changed
1551: // uqImpl.typesEvent(_at.isEmpty() ? null : new ClassIndexImplEvent(uqImpl, _at),
1552: // _rt.isEmpty() ? null : new ClassIndexImplEvent(uqImpl,_rt),
1553: // added.isEmpty() ? null : new ClassIndexImplEvent(uqImpl,added));
1554: }
1555: }
1556:
1557: GsfTaskProvider.refresh(fo);
1558: }
1559: }
1560:
1561: private void delete(final URL file, final URL root,
1562: final boolean folder) throws IOException {
1563: assert "file".equals(root.getProtocol()) : "Unexpected protocol of URL: "
1564: + root; //NOI18N
1565: final File rootFile = FileUtil.normalizeFile(new File(URI
1566: .create(root.toExternalForm())));
1567: assert "file".equals(file.getProtocol()) : "Unexpected protocol of URL: "
1568: + file; //NOI18N
1569: final File fileFile = FileUtil.normalizeFile(new File(URI
1570: .create(file.toExternalForm())));
1571: final String offset = FileObjects.getRelativePath(rootFile,
1572: fileFile);
1573: assert offset != null && offset.length() > 0 : String
1574: .format("File %s not under root %s ", fileFile
1575: .getAbsolutePath(), rootFile
1576: .getAbsolutePath()); // NOI18N
1577:
1578: boolean platform = false;
1579: ParserFile parserFile = FileObjects.fileFileObject(
1580: fileFile, rootFile, platform, null);
1581:
1582: for (Language language : LanguageRegistry.getInstance()) {
1583: // I have to iterate over all indexer languages here - I can't call
1584: // getApplicableIndexers() since I don't have the mime type for
1585: // deleted files (and asking for it just returns content/unknown)
1586: if (language.getIndexer() == null) {
1587: continue;
1588: }
1589: final ClassIndexImpl uqImpl = ClassIndexManager.get(
1590: language).createUsagesQuery(root, true);
1591: assert uqImpl != null;
1592: final SourceAnalyser sa = uqImpl.getSourceAnalyser();
1593: assert sa != null;
1594: sa.delete(parserFile);
1595: }
1596: //
1597: //
1598: // final File classCache = Index.getClassFolder (rootFile);
1599: // File[] affectedFiles = null;
1600: // if (folder) {
1601: // final File container = new File (classCache, offset);
1602: // affectedFiles = container.listFiles();
1603: // }
1604: // else {
1605: // int slashIndex = offset.lastIndexOf (File.separatorChar);
1606: // int dotIndex = offset.lastIndexOf('.'); //NOI18N
1607: // final File container = slashIndex == -1 ? classCache : new File (classCache,offset.substring(0,slashIndex));
1608: // final String name = offset.substring(slashIndex+1, dotIndex);
1609: // final String[] patterns = new String[] {
1610: // name + '.',
1611: // name + '$'
1612: // };
1613: // final File[] content = container.listFiles();
1614: // if (content != null) {
1615: // final List<File> result = new ArrayList<File>(content.length);
1616: // for (File f : content) {
1617: // final String fname = f.getName();
1618: // if (fname.startsWith(patterns[0]) || fname.startsWith(patterns[1])) {
1619: // result.add(f);
1620: // }
1621: // }
1622: // affectedFiles = result.toArray(new File[result.size()]);
1623: // }
1624: // }
1625: // if (affectedFiles != null && affectedFiles.length > 0) {
1626: //// Set<ElementHandle<TypeElement>> removed = new HashSet<ElementHandle<TypeElement>>();
1627: // final ClassIndexImpl uqImpl = ClassIndexManager.getDefault().createUsagesQuery(root, true);
1628: // assert uqImpl != null;
1629: // final SourceAnalyser sa = uqImpl.getSourceAnalyser();
1630: // assert sa != null;
1631: // for (File f : affectedFiles) {
1632: //// if (f.getName().endsWith(FileObjects.RS)) {
1633: //// List<File> rsFiles = new LinkedList<File>();
1634: //// readRSFile(f, classCache, rsFiles);
1635: //// for (File rsf : rsFiles) {
1636: //// String className = FileObjects.getBinaryName (rsf,classCache);
1637: //// sa.delete (className);
1638: //// removed.add(ElementHandleAccessor.INSTANCE.create(ElementKind.OTHER, className));
1639: //// rsf.delete();
1640: //// }
1641: //// }
1642: //// else {
1643: //// String className = FileObjects.getBinaryName (f,classCache);
1644: //// sa.delete (className);
1645: // sa.delete(f);
1646: //// removed.add(ElementHandleAccessor.INSTANCE.create(ElementKind.OTHER, className));
1647: //// }
1648: // f.delete();
1649: // }
1650: // sa.store();
1651: //// uqImpl.typesEvent(null,new ClassIndexImplEvent(uqImpl, removed), null);
1652: // }
1653: }
1654:
1655: private void updateBinary(final URL file, final URL root)
1656: throws IOException {
1657: //// CachingArchiveProvider.getDefault().clearArchive(root);
1658: // File cacheFolder = Index.getClassFolder(root);
1659: // FileObjects.deleteRecursively(cacheFolder);
1660: // final BinaryAnalyser ba = ClassIndexManager.getDefault().createUsagesQuery(root, false).getBinaryAnalyser();
1661: // ba.analyse(root, handle);
1662: }
1663: }
1664:
1665: static class LazyFileList implements Iterable<File> {
1666:
1667: private File root;
1668:
1669: public LazyFileList(final File root) {
1670: assert root != null;
1671: this .root = root;
1672: }
1673:
1674: public Iterator<File> iterator() {
1675: if (!root.exists()) {
1676: return Collections.<File> emptySet().iterator();
1677: }
1678: return new It(this .root);
1679: }
1680:
1681: private class It implements Iterator<File> {
1682:
1683: private final List<File> toDo = new LinkedList<File>();
1684:
1685: public It(File root) {
1686:
1687: // // Special case: Rails on some systems (such as debian)
1688: // // creates symbolic links in the vendor directory back into Rails;
1689: // // I don't want to visit these links.
1690: // // Arguably I should do this for all dirs - but it's a performance issue,
1691: // // and one I want to investigate more closely before making large changes;
1692: // // this specific fix addresses a serious recorded issue (93019)
1693: // if (root.getName().equals("vendor")) { // NOI18N
1694: // ArrayList<File> list = new ArrayList<File>();
1695: // for (File f : root.listFiles()) {
1696: //
1697: // // Skip vendor/rails - this is really a library which should
1698: // // be matching other installations.
1699: // // Ugh... I shouldn't skip it if I don't have it in the gems!
1700: // if (skipVendorRails && f.getName().equals("rails")) { // NOI18N
1701: // continue;
1702: // }
1703: //
1704: // try {
1705: // // See JDK issue 4042001 - need symbolic link support.
1706: // // Workaround which will work in this scenario is a
1707: // // to compare canonical paths with absolute paths
1708: // if (f.getAbsolutePath().equals(f.getCanonicalPath())) {
1709: // list.add(f);
1710: // }
1711: // } catch (IOException ioe) {
1712: // Exceptions.printStackTrace(ioe);
1713: // }
1714: // }
1715: // this.toDo.addAll(list);
1716: // } else {
1717: // Normal path
1718:
1719: this .toDo.addAll(java.util.Arrays.asList(root
1720: .listFiles()));
1721: // }
1722: }
1723:
1724: public boolean hasNext() {
1725: while (!toDo.isEmpty()) {
1726: File f = toDo.remove(0);
1727: final String name = f.getName();
1728: if (f.isDirectory()
1729: && !ignoredDirectories.contains(name)/* && Utilities.isJavaIdentifier(name)*/) {
1730: File[] content = f.listFiles();
1731: for (int i = 0, j = 0; i < content.length; i++) {
1732: f = content[i];
1733: if (f.isFile()) {
1734: this .toDo.add(j++, f);
1735: } else {
1736: this .toDo.add(f);
1737: }
1738: }
1739: } else { // XXX How do I decide if it's a reasonable name?
1740: // System.out.println("Should we scan " + name + "?");
1741: // else if (name.endsWith('.'+JavaDataLoader.JAVA_EXTENSION) && !PACKAGE_INFO.equals(name) && f.length()>0) { //NOI18N
1742: toDo.add(0, f);
1743: return true;
1744: }
1745: }
1746: return false;
1747: }
1748:
1749: public File next() {
1750: return toDo.remove(0);
1751: }
1752:
1753: public void remove() {
1754: throw new UnsupportedOperationException();
1755: }
1756:
1757: }
1758: }
1759:
1760: private final class Delay {
1761:
1762: private final Timer timer;
1763: private final List<Work> tasks;
1764:
1765: public Delay() {
1766: this .timer = new Timer(RepositoryUpdater.class.getName());
1767: this .tasks = new LinkedList<Work>();
1768: }
1769:
1770: public synchronized void post(final Work work) {
1771: assert work != null;
1772: this .tasks.add(work);
1773: this .timer.schedule(new DelayTask(work), DELAY);
1774: }
1775:
1776: public void cancel() {
1777: Work[] toCancel;
1778: synchronized (this ) {
1779: toCancel = this .tasks.toArray(new Work[this .tasks
1780: .size()]);
1781: }
1782: for (Work w : toCancel) {
1783: if (w.workType == WorkType.COMPILE) {
1784: w = new SingleRootWork(WorkType.DELETE,
1785: ((SingleRootWork) w).file,
1786: ((SingleRootWork) w).root,
1787: ((SingleRootWork) w).isFolder, w.latch);
1788: }
1789: CompileWorker cw = new CompileWorker(w);
1790: try {
1791: cw.run(null);
1792: } catch (IOException ioe) {
1793: Exceptions.printStackTrace(ioe);
1794: }
1795: }
1796: }
1797:
1798: private class DelayTask extends TimerTask {
1799:
1800: final Work work;
1801:
1802: public DelayTask(final Work work) {
1803: this .work = work;
1804: }
1805:
1806: public void run() {
1807: submit(work);
1808: synchronized (Delay.this ) {
1809: Delay.this .tasks.remove(work);
1810: }
1811: }
1812:
1813: public @Override
1814: boolean cancel() {
1815: boolean retValue = super .cancel();
1816: if (retValue) {
1817: synchronized (Delay.this ) {
1818: Delay.this .tasks.remove(work);
1819: }
1820: }
1821: return retValue;
1822: }
1823: }
1824: }
1825:
1826: private static class CompilerListener implements
1827: /*DiagnosticListener<JavaFileObject>,*/LowMemoryListener,
1828: ParseListener {
1829:
1830: final List<Error> errors = new LinkedList<Error>();
1831: final List<Error> warnings = new LinkedList<Error>();
1832: // final List<ClassSymbol> justEntered = new LinkedList<ClassSymbol> ();
1833: final List<ParserResult> justEntered = new LinkedList<ParserResult>();
1834: final AtomicBoolean lowMemory = new AtomicBoolean();
1835:
1836: //
1837: void cleanDiagnostics() {
1838: if (!this .errors.isEmpty()) {
1839: if (LOGGER.isLoggable(Level.FINE)) {
1840: for (Error msg : this .errors) {
1841: LOGGER.fine(msg.toString()); //NOI18N
1842: }
1843: }
1844: this .errors.clear();
1845: }
1846: if (!this .warnings.isEmpty()) {
1847: if (LOGGER.isLoggable(Level.FINE)) {
1848: for (Error msg : this .warnings) {
1849: LOGGER.fine(msg.toString()); //NOI18N
1850: }
1851: }
1852: this .warnings.clear();
1853: }
1854: this .justEntered.clear();
1855: }
1856:
1857: List<ParserResult> getEnteredTypes() {
1858: List<ParserResult> result = new ArrayList<ParserResult>(
1859: this .justEntered);
1860: this .justEntered.clear();
1861: return result;
1862: }
1863:
1864: public void error(Error error) {
1865: if (error.getSeverity() == Severity.ERROR) {
1866: this .errors.add(error);
1867: } else {
1868: this .warnings.add(error);
1869: }
1870: }
1871:
1872: public void exception(Exception exception) {
1873: }
1874:
1875: //
1876: // public void report(final Diagnostic diagnosticMessage) {
1877: // Diagnostic.Kind kind = diagnosticMessage.getKind();
1878: // if ( kind == Diagnostic.Kind.ERROR) {
1879: // this.errors.add (diagnosticMessage);
1880: // }
1881: // else if (kind == Diagnostic.Kind.WARNING
1882: // ||kind == Diagnostic.Kind.MANDATORY_WARNING) {
1883: // this.warnings.add (diagnosticMessage);
1884: // }
1885: // }
1886: //
1887: public void started(ParseEvent e) {
1888:
1889: }
1890:
1891: public void finished(ParseEvent event) {
1892: if (event.getKind() == ParseEvent.Kind.PARSE /*ENTER*/) {
1893: //final CompilationUnitTree unit = event.getCompilationUnit();
1894: final ParserResult result = event.getResult();
1895: if (result != null) {
1896: this .justEntered.add(result);
1897: }
1898: //
1899: // for (Tree typeTree : unit.getTypeDecls()) {
1900: // if (typeTree instanceof JCTree.JCClassDecl) { //May be a JCTree.JCExpressionStatement in case of an error
1901: // ClassSymbol sym = ((JCTree.JCClassDecl)typeTree).sym;
1902: // if (sym != null) {
1903: // if (sym.sourcefile == null) {
1904: // sym.sourcefile = event.getSourceFile();
1905: // }
1906: // this.justEntered.add(sym);
1907: // }
1908: // }
1909: // }
1910: }
1911: }
1912:
1913: public void lowMemory(final LowMemoryEvent event) {
1914: this .lowMemory.set(true);
1915: }
1916: }
1917:
1918: public static void batchCompile(final List<ParserFile> toCompile,
1919: final FileObject rootFo, ClasspathInfo cpInfo,
1920: /*final ClasspathInfo cpInfo,*/URL root, /*final SourceAnalyser sa,*/
1921: final Set<URI> dirtyFiles,
1922: final Set/*<? super ElementHandle<TypeElement>>*/added,
1923: ProgressHandle handle,
1924: Map<Language, Map<String, String>> timeStamps)
1925: throws IOException {
1926: assert toCompile != null;
1927: assert rootFo != null;
1928: assert cpInfo != null;
1929: ParserFile active = null;
1930: //final JavaFileManager fileManager = ClasspathInfoAccessor.INSTANCE.getFileManager(cpInfo);
1931: final CompilerListener listener = new CompilerListener();
1932: LowMemoryNotifier.getDefault().addLowMemoryListener(listener);
1933: try {
1934: ParserTaskImpl jt = null;
1935:
1936: try {
1937: List<ParserFile> bigFiles = new LinkedList<ParserFile>();
1938: int state = 0; // TODO: Document what these states mean
1939: boolean isBigFile = false;
1940: final String sourceLevel = SourceLevelQuery
1941: .getSourceLevel(rootFo);
1942: int fileNumber = 0;
1943: int fileCount = toCompile.size();
1944: if (fileCount > 0) {
1945: handle.switchToDeterminate(fileCount);
1946: }
1947: allFiles: while (!toCompile.isEmpty()
1948: || !bigFiles.isEmpty() || active != null) {
1949: try {
1950: if (listener.lowMemory.getAndSet(false)) {
1951: if (jt != null) {
1952: jt.finish();
1953: }
1954: jt = null;
1955: if (state == 1) {
1956: break;
1957: } else {
1958: state = 1;
1959: }
1960: System.gc();
1961: continue;
1962: }
1963: if (active == null) {
1964: if (!toCompile.isEmpty()) {
1965: active = toCompile.remove(0);
1966: isBigFile = false;
1967: } else {
1968: active = bigFiles.remove(0);
1969: isBigFile = true;
1970: }
1971: }
1972:
1973: if (handle != null && active != null) {
1974: if (fileCount > 0
1975: && fileNumber <= fileCount) {
1976: handle.progress(fileNumber);
1977: }
1978: fileNumber++;
1979: }
1980:
1981: // Change from what's going on in the Retouche updater:
1982: // We could have many language implementations that want to index this root;
1983: // we need to iterate through them and let each one of them index if they
1984: // want to.
1985: List<IndexerEntry> indexers = getIndexers();
1986: assert indexers instanceof RandomAccess;
1987: // We're gonna do this for every file in the filesystem - do cheaper iteration
1988: // using indices rather than iterators
1989: Language language = null;
1990: for (int in = 0; in < indexers.size(); in++) {
1991: IndexerEntry entry = indexers.get(in);
1992: Indexer indexer = entry.getIndexer();
1993: if (!indexer.isIndexable(active)) {
1994: continue;
1995: }
1996:
1997: language = entry.getLanguage();
1998: if (timeStamps != null) {
1999: Map<String, String> ts = timeStamps
2000: .get(language);
2001: if (ts != null) {
2002: File file = active.getFile();
2003: String url = indexer
2004: .getPersistentUrl(file);
2005: String timeStampString = ts
2006: .get(url);
2007: if (timeStampString != null) {
2008: try {
2009: long timeStamp = DateTools
2010: .stringToTime(timeStampString);
2011: if (file.lastModified() <= timeStamp) {
2012: state = 0;
2013: active = null;
2014: listener
2015: .cleanDiagnostics();
2016: continue allFiles;
2017: }
2018: } catch (ParseException ex) {
2019: Exceptions
2020: .printStackTrace(ex);
2021: }
2022: }
2023: }
2024: }
2025:
2026: // Cache parser tasks per indexer
2027: jt = entry.getParserTask();
2028:
2029: if (jt == null) {
2030: jt = SourceAccessor.getINSTANCE()
2031: .createParserTask(language,
2032: cpInfo/*, listener*/,
2033: sourceLevel);
2034: jt.setParseListener(listener);
2035: entry.setParserTask(jt);
2036: LOGGER
2037: .fine("Created new ParserTask for: "
2038: + FileUtil
2039: .getFileDisplayName(rootFo)); //NOI18N
2040: }
2041: }
2042:
2043: // Not an interesting source - such as a .zip file, a .gif file etc.
2044: if (language == null) {
2045: state = 0;
2046: active = null;
2047: listener.cleanDiagnostics();
2048: continue;
2049: }
2050:
2051: Iterable<ParserResult> trees = jt
2052: .parse(new ParserFile[] { active });
2053: if (listener.lowMemory.getAndSet(false)) {
2054: jt.finish();
2055: jt = null;
2056: listener.cleanDiagnostics();
2057: trees = null;
2058: if (state == 1) {
2059: if (isBigFile) {
2060: break;
2061: } else {
2062: bigFiles.add(active);
2063: active = null;
2064: state = 0;
2065: }
2066: } else {
2067: state = 1;
2068: }
2069: System.gc();
2070: continue;
2071: }
2072: // Iterable<? extends TypeElement> types = jt.enterTrees(trees);
2073: // dumpClasses (listener.getEnteredTypes(),fileManager,
2074: // rootFo.getURL().toExternalForm(), dirtyFiles,
2075: // com.sun.tools.javac.code.Types.instance(jt.getContext()),
2076: // com.sun.tools.javac.util.Name.Table.instance(jt.getContext()));
2077: // if (listener.lowMemory.getAndSet(false)) {
2078: // jt.finish();
2079: // jt = null;
2080: // listener.cleanDiagnostics();
2081: // trees = null;
2082: // types = null;
2083: // if (state == 1) {
2084: // if (isBigFile) {
2085: // break;
2086: // } else {
2087: // bigFiles.add(active);
2088: // active = null;
2089: // state = 0;
2090: // }
2091: // } else {
2092: // state = 1;
2093: // }
2094: // System.gc();
2095: // continue;
2096: // }
2097: // final JavaCompiler jc = JavaCompiler.instance(jt.getContext());
2098: // final JavaFileObject finalActive = active;
2099: // Filter f = new Filter() {
2100: // public void process(Env<AttrContext> env) {
2101: // try {
2102: // jc.attribute(env);
2103: // } catch (Throwable t) {
2104: // if (finalActive.toUri().getPath().contains("org/openide/loaders/OpenSupport.java")) {
2105: // Exceptions.printStackTrace(t);
2106: // }
2107: // }
2108: // }
2109: // };
2110: // f.run(jc.todo, types);
2111: // dumpClasses (listener.getEnteredTypes(), fileManager,
2112: // rootFo.getURL().toExternalForm(), dirtyFiles,
2113: // com.sun.tools.javac.code.Types.instance(jt.getContext()),
2114: // com.sun.tools.javac.util.Name.Table.instance(jt.getContext()));
2115: // if (listener.lowMemory.getAndSet(false)) {
2116: // jt.finish();
2117: // jt = null;
2118: // listener.cleanDiagnostics();
2119: // trees = null;
2120: // types = null;
2121: // if (state == 1) {
2122: // if (isBigFile) {
2123: // break;
2124: // } else {
2125: // bigFiles.add(active);
2126: // active = null;
2127: // state = 0;
2128: // }
2129: // } else {
2130: // state = 1;
2131: // }
2132: // System.gc();
2133: // continue;
2134: // }
2135: if (trees != null) {
2136: ClassIndexImpl uqImpl = ClassIndexManager
2137: .get(language).createUsagesQuery(
2138: root, true);
2139: assert uqImpl != null;
2140: SourceAnalyser sa = uqImpl
2141: .getSourceAnalyser();
2142: if (sa != null) {
2143: sa.analyse(language, trees, jt,/* ClasspathInfoAccessor.INSTANCE.getFileManager(cpInfo),*/
2144: active);
2145: }
2146: }
2147: if (!listener.errors.isEmpty()) {
2148: //Log.instance(jt.getContext()).nerrors = 0;
2149: listener.cleanDiagnostics();
2150: }
2151: active = null;
2152: state = 0;
2153: } catch (Exception a) {
2154: //coupling error
2155: //TODO: check if the source sig file ~ the source java file:
2156: //couplingAbort(a, active);
2157: if (jt != null) {
2158: jt.finish();
2159: }
2160: jt = null;
2161: listener.cleanDiagnostics();
2162: active = null;
2163: state = 0;
2164: } catch (Throwable t) {
2165: if (t instanceof ThreadDeath) {
2166: throw (ThreadDeath) t;
2167: } else {
2168: if (jt != null) {
2169: jt.finish();
2170: }
2171: String activeURI;
2172: if (active != null) {
2173: activeURI = active.getNameExt();
2174: } else {
2175: activeURI = "unknown";
2176: }
2177: jt = null;
2178: active = null;
2179: listener.cleanDiagnostics();
2180: //if (!(t instanceof Abort)) { // a javac Throwable
2181: final ClassPath bootPath = cpInfo
2182: .getClassPath(ClasspathInfo.PathKind.BOOT);
2183: final ClassPath classPath = cpInfo
2184: .getClassPath(ClasspathInfo.PathKind.COMPILE);
2185: final ClassPath sourcePath = cpInfo
2186: .getClassPath(ClasspathInfo.PathKind.SOURCE);
2187: t = Exceptions
2188: .attachMessage(
2189: t,
2190: String
2191: .format(
2192: "Root: %s File: %s Bootpath: %s Classpath: %s Sourcepath: %s",
2193: FileUtil
2194: .getFileDisplayName(rootFo),
2195: activeURI
2196: .toString(),
2197: bootPath == null ? null
2198: : bootPath
2199: .toString(),
2200: classPath == null ? null
2201: : classPath
2202: .toString(),
2203: sourcePath == null ? null
2204: : sourcePath
2205: .toString()));
2206: Exceptions.printStackTrace(t);
2207: //}
2208: }
2209: }
2210: }
2211: if (state == 1) {
2212: LOGGER
2213: .warning("Not enough memory to compile folder: "
2214: + FileUtil
2215: .getFileDisplayName(rootFo)); // NOI18N
2216: }
2217: } finally {
2218: if (jt != null) {
2219: jt.finish();
2220: }
2221: }
2222: } finally {
2223: clearIndexerParserTasks();
2224: LowMemoryNotifier.getDefault().removeLowMemoryListener(
2225: listener);
2226: }
2227: }
2228:
2229: private static Set<String> parseSet(String propertyName,
2230: String defaultValue) {
2231: StringTokenizer st = new StringTokenizer(System.getProperty(
2232: propertyName, defaultValue), " \t\n\r\f,-:+!");
2233: Set<String> result = new HashSet<String>();
2234: while (st.hasMoreTokens()) {
2235: result.add(st.nextToken());
2236: }
2237: return result;
2238: }
2239:
2240: private static ClassPath.Entry getClassPathEntry(
2241: final ClassPath cp, final URL root) {
2242: assert cp != null;
2243: assert root != null;
2244: for (ClassPath.Entry e : cp.entries()) {
2245: if (root.equals(e.getURL())) {
2246: return e;
2247: }
2248: }
2249: return null;
2250: }
2251:
2252: private class FilterListener implements ChangeListener {
2253:
2254: public void stateChanged(ChangeEvent event) {
2255: // Object source = event.getSource();
2256: // if (source instanceof JavaFileFilterImplementation) {
2257: // List<URL> dirtyRoots = new LinkedList<URL> ();
2258: // synchronized (filters) {
2259: // for (Map.Entry<URL,JavaFileFilterImplementation> e : filters.entrySet()) {
2260: // if (e.getValue() == source) {
2261: // dirtyRoots.add(e.getKey());
2262: // }
2263: // }
2264: // }
2265: // submit(Work.filterChange(dirtyRoots));
2266: // }
2267: }
2268: }
2269:
2270: public static synchronized RepositoryUpdater getDefault() {
2271: if (instance == null) {
2272: instance = new RepositoryUpdater();
2273: }
2274: return instance;
2275: }
2276:
2277: // BEGIN TOR MODIFICATIONS
2278: // There could be multiple indexers (for different languages) that want
2279: // to index a given file. I will iterate over the indexers and let each
2280: // indexer have a chance to index every file. To do this I compute
2281: // a list of indexers in advance - and provide a place where we can
2282: // cache the parser tasks such that only one is created per indexer.
2283:
2284: private static List<IndexerEntry> indexers;
2285:
2286: private static List<IndexerEntry> getIndexers() {
2287: if (indexers == null) {
2288: indexers = new ArrayList<IndexerEntry>();
2289: for (Language language : LanguageRegistry.getInstance()) {
2290: Indexer indexer = language.getIndexer();
2291: if (indexer != null) {
2292: IndexerEntry entry = new IndexerEntry(language,
2293: indexer);
2294: indexers.add(entry);
2295: }
2296: }
2297: }
2298:
2299: return indexers;
2300: }
2301:
2302: private static void clearIndexerParserTasks() {
2303: for (IndexerEntry entry : getIndexers()) {
2304: entry.setParserTask(null);
2305: }
2306: }
2307:
2308: private static class IndexerEntry {
2309: private Language language;
2310: private Indexer indexer;
2311: private ParserTaskImpl task;
2312:
2313: IndexerEntry(Language language, Indexer indexer) {
2314: this .language = language;
2315: this .indexer = indexer;
2316: }
2317:
2318: Indexer getIndexer() {
2319: return indexer;
2320: }
2321:
2322: Language getLanguage() {
2323: return language;
2324: }
2325:
2326: ParserTaskImpl getParserTask() {
2327: return task;
2328: }
2329:
2330: void setParserTask(ParserTaskImpl task) {
2331: this .task = task;
2332: }
2333: }
2334:
2335: // END TOR MODIFICATIONS
2336: }
|