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.cnd.model.tasks;
042:
043: import java.util.ArrayList;
044: import java.util.Collection;
045: import java.util.HashMap;
046: import java.util.List;
047: import java.util.Map;
048: import java.util.Map.Entry;
049: import org.netbeans.modules.cnd.api.model.CsmChangeEvent;
050: import org.netbeans.modules.cnd.api.model.CsmFile;
051: import org.netbeans.modules.cnd.api.model.CsmListeners;
052: import org.netbeans.modules.cnd.api.model.CsmModelListener;
053: import org.netbeans.modules.cnd.api.model.CsmProgressAdapter;
054: import org.netbeans.modules.cnd.api.model.CsmProject;
055: import org.netbeans.modules.cnd.modelutil.CsmUtilities;
056: import org.openide.filesystems.FileObject;
057: import org.openide.util.RequestProcessor;
058:
059: /**
060: * CsmFile analogue of CsmFileTaskFactory
061: *
062: * This factory should be registered in the global lookup by listing its fully qualified
063: * name in file <code>META-INF/services/org.netbeans.modules.cnd.modelimpl.csm.scheduling.CsmFileTaskFactory</code>.
064: *
065: * @author Sergey Grinev
066: */
067: public abstract class CsmFileTaskFactory {
068: private final Map<FileObject, CsmFile> fobj2csm = new HashMap<FileObject, CsmFile>();
069: private final Map<CsmFile, PhaseRunner> csm2task = new HashMap<CsmFile, PhaseRunner>();
070: private final ProgressListener progressListener = new ProgressListener();
071: private final ModelListener modelListener = new ModelListener();
072:
073: protected CsmFileTaskFactory() {
074: CsmListeners.getDefault().addProgressListener(progressListener);
075: CsmListeners.getDefault().addModelListener(modelListener);
076: }
077:
078: protected abstract PhaseRunner createTask(FileObject fo);
079:
080: protected abstract Collection<FileObject> getFileObjects();
081:
082: protected final void fileObjectsChanged() {
083: final List<FileObject> currentFiles = new ArrayList<FileObject>(
084: getFileObjects());
085:
086: WORKER.post(new Runnable() {
087:
088: public void run() {
089: stateChangedImpl(currentFiles);
090: }
091: });
092: }
093:
094: private void stateChangedImpl(List<FileObject> currentFiles) {
095: //System.err.println("stateChangedImpl, newFiles: " + currentFiles.size() + ", file2Tasks: " + fobj2csm.size());
096: Map<CsmFile, PhaseRunner> toRemove = new HashMap<CsmFile, PhaseRunner>();
097: Map<CsmFile, PhaseRunner> toAdd = new HashMap<CsmFile, PhaseRunner>();
098:
099: synchronized (this ) {
100: List<FileObject> addedFiles = new ArrayList<FileObject>(
101: currentFiles);
102: List<FileObject> removedFiles = new ArrayList<FileObject>(
103: fobj2csm.keySet());
104:
105: addedFiles.removeAll(fobj2csm.keySet());
106: removedFiles.removeAll(currentFiles);
107:
108: //remove old tasks:
109: for (FileObject r : removedFiles) {
110: CsmFile csmFile = fobj2csm.remove(r);
111:
112: if (csmFile == null) {
113: //TODO: log
114: continue;
115: }
116:
117: toRemove.put(csmFile, csm2task.remove(csmFile));
118: }
119:
120: //add new tasks:
121: for (FileObject fileObject : addedFiles) {
122: if (fileObject == null) {
123: continue;
124: }
125: if (!fileObject.isValid()) {
126: continue;
127: }
128: CsmFile csmFile = CsmUtilities.getCsmFile(fileObject,
129: false);
130:
131: if (csmFile != null) {
132: PhaseRunner task = createTask(fileObject);
133:
134: toAdd.put(csmFile, task);
135:
136: fobj2csm.put(fileObject, csmFile);
137: csm2task.put(csmFile, task);
138: }
139: }
140: }
141:
142: for (Entry<CsmFile, PhaseRunner> e : toRemove.entrySet()) {
143: //System.err.println("CFTF: removing " + e.getKey().getAbsolutePath());
144: if (e != null && e.getValue() != null) {
145: post(e.getValue(), e.getKey(),
146: PhaseRunner.Phase.CLEANUP, IMMEDIATELY);
147: }
148: }
149:
150: for (Entry<CsmFile, PhaseRunner> e : toAdd.entrySet()) {
151: //System.err.println("CFTF: adding " + e.getKey().getAbsolutePath());
152: post(e.getValue(), e.getKey(),
153: e.getKey().isParsed() ? PhaseRunner.Phase.PARSED
154: : PhaseRunner.Phase.INIT, DELAY);
155: }
156: }
157:
158: private static final int DELAY = 500;
159: private static final int IMMEDIATELY = 0;
160:
161: public final synchronized void reschedule(FileObject file)
162: throws IllegalArgumentException {
163: CsmFile source = fobj2csm.get(file);
164:
165: if (source == null) {
166: return;
167: }
168:
169: runTask(source, PhaseRunner.Phase.PARSED, DELAY);
170: }
171:
172: private final void runTask(CsmFile file, PhaseRunner.Phase phase,
173: int delay) {
174: PhaseRunner pr = csm2task.get(file);
175:
176: if (pr != null) {
177: if (!pr.isValid()) {
178: //System.err.println("CsmFileTaskFactory: invalid task detected: " + pr.getClass().toString());
179: FileObject fo = CsmUtilities.getFileObject(file);
180: pr = createTask(fo);
181: assert pr.isValid();
182: //System.err.println("CsmFileTaskFactory: new task created: " + pr.getClass().toString());
183: csm2task.put(file, pr);
184: }
185: post(pr, file, phase, delay);
186: }
187: }
188:
189: private final void post(PhaseRunner pr, CsmFile file,
190: PhaseRunner.Phase phase, int delay) {
191: WORKER.post(new CsmSafeRunnable(getRunnable(pr, phase), file),
192: delay);
193: }
194:
195: private static RequestProcessor WORKER = new RequestProcessor(
196: "CsmFileTaskFactory", 1); //NOI18N
197:
198: static {
199: CsmFileTaskFactoryManager.ACCESSOR = new CsmFileTaskFactoryManager.Accessor() {
200:
201: public void fireChangeEvent(CsmFileTaskFactory f) {
202: f.fileObjectsChanged();
203: }
204: };
205: }
206:
207: private class ProgressListener extends CsmProgressAdapter {
208:
209: @Override
210: public void fileParsingFinished(CsmFile file) {
211: runTask(file, PhaseRunner.Phase.PARSED, IMMEDIATELY);
212: }
213:
214: @Override
215: public void fileParsingStarted(CsmFile file) {
216: runTask(file, PhaseRunner.Phase.PARSING_STARTED,
217: IMMEDIATELY);
218: }
219: }
220:
221: private class ModelListener implements CsmModelListener {
222:
223: public void projectOpened(CsmProject project) {
224: // do nothing
225: }
226:
227: public void projectClosed(CsmProject project) {
228: // TODO: do something? Cleanup, maybe?
229: }
230:
231: public void modelChanged(CsmChangeEvent e) {
232: for (CsmFile f : e.getRemovedFiles()) {
233: if (csm2task.get(f) != null) {
234: synchronized (this ) {
235: runTask(f, PhaseRunner.Phase.CLEANUP,
236: IMMEDIATELY);
237: csm2task.put(f, lazyRunner());
238: }
239: }
240: }
241: if (!e.getNewFiles().isEmpty()) {
242: fileObjectsChanged();
243: }
244: }
245:
246: }
247:
248: public static interface PhaseRunner {
249: public enum Phase {
250: INIT, PARSING_STARTED, PARSED, CLEANUP
251:
252: };
253:
254: public abstract void run(Phase phase);
255:
256: public abstract boolean isValid();
257:
258: }
259:
260: private static final Runnable getRunnable(final PhaseRunner pr,
261: final PhaseRunner.Phase phase) {
262: return new Runnable() {
263: public void run() {
264: pr.run(phase);
265: }
266: };
267: }
268:
269: protected static PhaseRunner lazyRunner() {
270: return new PhaseRunner() {
271: public void run(Phase phase) {
272: // do nothing for all phases
273: }
274:
275: public boolean isValid() {
276: return true;
277: }
278: };
279: }
280:
281: private static final class CsmSafeRunnable implements Runnable {
282: private CsmFile file;
283: private Runnable run;
284:
285: public CsmSafeRunnable(Runnable run, CsmFile file) {
286: this .run = run;
287: this .file = file;
288: }
289:
290: public void run() {
291: if (file.isValid() /*&& (file.isHeaderFile() || file.isSourceFile())*/) {
292: run.run();
293: }
294: }
295: }
296:
297: }
|