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-2007 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.modules.java.source.tasklist;
042:
043: import java.awt.Image;
044: import java.io.File;
045: import java.io.IOException;
046: import java.net.URL;
047: import java.util.Collection;
048: import java.util.Collections;
049: import java.util.HashSet;
050: import java.util.Iterator;
051: import java.util.LinkedList;
052: import java.util.Map;
053: import java.util.Set;
054: import java.util.WeakHashMap;
055: import java.util.logging.Level;
056: import java.util.logging.Logger;
057: import javax.swing.Action;
058: import org.netbeans.api.fileinfo.NonRecursiveFolder;
059: import org.netbeans.api.project.FileOwnerQuery;
060: import org.netbeans.api.project.Project;
061: import org.netbeans.api.project.ProjectUtils;
062: import org.netbeans.api.project.SourceGroup;
063: import org.netbeans.modules.masterfs.providers.AnnotationProvider;
064: import org.netbeans.modules.masterfs.providers.InterceptionListener;
065: import org.openide.ErrorManager;
066: import org.openide.filesystems.FileObject;
067: import org.openide.filesystems.FileStateInvalidException;
068: import org.openide.filesystems.FileStatusEvent;
069: import org.openide.filesystems.FileUtil;
070: import org.openide.util.Lookup;
071: import org.openide.util.RequestProcessor;
072: import org.openide.util.Utilities;
073:
074: /**
075: *
076: * @author Jan Lahoda
077: */
078: public class ErrorAnnotator extends AnnotationProvider /*implements FileStatusListener*/{
079:
080: public ErrorAnnotator() {
081: }
082:
083: public String annotateName(String name, Set files) {
084: return null;
085: }
086:
087: @Override
088: public Image annotateIcon(Image icon, int iconType, Set files) {
089: if (!TasklistSettings.isTasklistEnabled()
090: || !TasklistSettings.isBadgesEnabled())
091: return null;
092:
093: boolean inError = false;
094:
095: if (files instanceof NonRecursiveFolder) {
096: FileObject folder = ((NonRecursiveFolder) files)
097: .getFolder();
098: inError = isInError(folder, false, true);
099: } else {
100: for (Object o : files) {
101: if (o instanceof FileObject) {
102: FileObject f = (FileObject) o;
103:
104: if (f.isFolder()) {
105: if (isInError(f, true, !inError)) {
106: inError = true;
107: continue;
108: }
109: if (inError)
110: continue;
111: } else {
112: if (f.isData() && "java".equals(f.getExt())) {
113: if (isInError(f, true, !inError)) {
114: inError = true;
115: }
116: }
117: }
118: }
119: }
120: }
121:
122: Logger.getLogger(ErrorAnnotator.class.getName()).log(
123: Level.FINE, "files={0}, in error={1}",
124: new Object[] { files, inError });
125:
126: if (inError) {
127: //badge:
128: Image i = Utilities
129: .mergeImages(
130: icon,
131: Utilities
132: .loadImage("org/netbeans/modules/java/source/resources/icons/error-badge.gif"),
133: 0, 8);
134: Iterator<? extends AnnotationProvider> it = Lookup
135: .getDefault().lookupAll(AnnotationProvider.class)
136: .iterator();
137: boolean found = false;
138:
139: while (it.hasNext()) {
140: AnnotationProvider p = it.next();
141:
142: if (found) {
143: Image res = p.annotateIcon(i, iconType, files);
144:
145: if (res != null) {
146: return res;
147: }
148: } else {
149: found = p == this ;
150: }
151: }
152:
153: return i;
154: }
155:
156: return null;
157: }
158:
159: public String annotateNameHtml(String name, Set files) {
160: return null;
161: }
162:
163: public Action[] actions(Set files) {
164: return null;
165: }
166:
167: public InterceptionListener getInterceptionListener() {
168: return null;
169: }
170:
171: public void updateAllInError() {
172: try {
173: File[] roots = File.listRoots();
174: for (File root : roots) {
175: FileObject rootFO = FileUtil.toFileObject(root);
176:
177: if (rootFO != null) {
178: fireFileStatusChanged(new FileStatusEvent(rootFO
179: .getFileSystem(), true, false));
180: }
181: }
182: } catch (FileStateInvalidException ex) {
183: ErrorManager.getDefault().notify(
184: ErrorManager.INFORMATIONAL, ex);
185: }
186: }
187:
188: public synchronized void updateInError(Set<URL> urls) {
189: Set<FileObject> toRefresh = new HashSet<FileObject>();
190: for (Iterator<FileObject> it = knownFiles2Error.keySet()
191: .iterator(); it.hasNext();) {
192: FileObject f = it.next();
193: try {
194: if (urls.contains(f.getURL())) {
195: toRefresh.add(f);
196: Integer i = knownFiles2Error.get(f);
197:
198: if (i != null) {
199: knownFiles2Error.put(f, i | INVALID);
200:
201: enqueue(f);
202: }
203: }
204: } catch (IOException e) {
205: ErrorManager.getDefault().notify(
206: ErrorManager.INFORMATIONAL, e);
207: }
208: }
209: }
210:
211: public void fireFileStatusChanged(Set<FileObject> fos) {
212: if (fos.isEmpty())
213: return;
214: try {
215: fireFileStatusChanged(new FileStatusEvent(fos.iterator()
216: .next().getFileSystem(), fos, true, false));
217: } catch (FileStateInvalidException ex) {
218: ErrorManager.getDefault().notify(
219: ErrorManager.INFORMATIONAL, ex);
220: }
221: }
222:
223: public static ErrorAnnotator getAnnotator() {
224: for (AnnotationProvider ap : Lookup.getDefault().lookupAll(
225: AnnotationProvider.class)) {
226: if (ap.getClass() == ErrorAnnotator.class) {
227: return (ErrorAnnotator) ap;
228: }
229: }
230:
231: return null;
232: }
233:
234: private static final int IN_ERROR_REC = 1;
235: private static final int IN_ERROR_NONREC = 2;
236: private static final int INVALID = 4;
237:
238: private Map<FileObject, Integer> knownFiles2Error = new WeakHashMap<FileObject, Integer>();
239:
240: private void enqueue(FileObject file) {
241: if (toProcess == null) {
242: toProcess = new LinkedList<FileObject>();
243: WORKER.schedule(50);
244: }
245:
246: toProcess.add(file);
247: }
248:
249: private synchronized boolean isInError(FileObject file,
250: boolean recursive, boolean forceValue) {
251: boolean result = false;
252: Integer i = knownFiles2Error.get(file);
253:
254: if (i != null) {
255: result = (i & (recursive ? IN_ERROR_REC : IN_ERROR_NONREC)) != 0;
256:
257: if ((i & INVALID) == 0)
258: return result;
259: }
260:
261: if (!forceValue) {
262: if (i == null) {
263: knownFiles2Error.put(file, null);
264: }
265: return result;
266: }
267:
268: enqueue(file);
269: return result;
270: }
271:
272: private long cumulativeTime;
273: private Collection<FileObject> toProcess = null;
274:
275: private final RequestProcessor.Task WORKER = new RequestProcessor(
276: "ErrorAnnotator worker", 1).create(new Runnable() {
277: public void run() {
278: long startTime = System.currentTimeMillis();
279: Collection<FileObject> toProcess;
280:
281: synchronized (ErrorAnnotator.this ) {
282: toProcess = ErrorAnnotator.this .toProcess;
283: ErrorAnnotator.this .toProcess = null;
284: }
285:
286: for (FileObject f : toProcess) {
287: boolean recError = false;
288: boolean nonRecError = false;
289: if (f.isData()) {
290: recError = nonRecError = TaskCache.getDefault()
291: .isInError(f, true);
292: } else {
293: boolean handled = false;
294: Project p = FileOwnerQuery.getOwner(f);
295:
296: if (p != null) {
297: for (SourceGroup sg : ProjectUtils
298: .getSources(p)
299: .getSourceGroups("java"/*JavaProjectConstants.SOURCES_TYPE_JAVA*/)) {
300: FileObject sgRoot = sg.getRootFolder();
301:
302: if ((FileUtil.isParentOf(f, sgRoot) || f == sgRoot)
303: && TaskCache.getDefault()
304: .isInError(sgRoot, true)) {
305: recError = true;
306: nonRecError = false;
307: handled = true;
308: break;
309: }
310: }
311: }
312:
313: if (!handled) {
314: recError = TaskCache.getDefault().isInError(f,
315: true);
316: nonRecError = TaskCache.getDefault().isInError(
317: f, false);
318: }
319: }
320:
321: Integer value = (recError ? IN_ERROR_REC : 0)
322: | (nonRecError ? IN_ERROR_NONREC : 0);
323:
324: synchronized (ErrorAnnotator.this ) {
325: knownFiles2Error.put(f, value);
326: }
327:
328: fireFileStatusChanged(Collections.singleton(f));
329: }
330:
331: long endTime = System.currentTimeMillis();
332:
333: Logger
334: .getLogger(ErrorAnnotator.class.getName())
335: .log(
336: Level.FINE,
337: "time spent in error annotations computation: {0}, cumulative time: {1}",
338: new Object[] {
339: (endTime - startTime),
340: (cumulativeTime += (endTime - startTime)) });
341: }
342: });
343: }
|