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:
042: package org.netbeans.modules.cnd.modelimpl.csm.core;
043:
044: import java.io.DataInput;
045: import java.io.DataOutput;
046: import java.io.File;
047: import java.io.IOException;
048: import java.util.*;
049: import javax.swing.event.ChangeEvent;
050: import javax.swing.event.ChangeListener;
051: import org.netbeans.modules.cnd.api.model.CsmFile;
052: import org.netbeans.modules.cnd.api.model.CsmUID;
053: import org.netbeans.modules.cnd.api.project.NativeFileItem;
054: import org.netbeans.modules.cnd.api.project.NativeProject;
055: import org.netbeans.modules.cnd.apt.support.APTDriver;
056: import org.netbeans.modules.cnd.apt.support.APTPreprocHandler;
057: import org.netbeans.modules.cnd.apt.utils.APTIncludeUtils;
058: import org.netbeans.modules.cnd.modelimpl.cache.CacheManager;
059: import org.netbeans.modules.cnd.modelimpl.debug.Diagnostic;
060: import org.netbeans.modules.cnd.modelimpl.debug.DiagnosticExceptoins;
061: import org.netbeans.modules.cnd.modelimpl.debug.TraceFlags;
062: import org.openide.util.RequestProcessor;
063:
064: /**
065: * Project implementation
066: * @author Vladimir Kvashin
067: */
068: public final class ProjectImpl extends ProjectBase {
069:
070: private ProjectImpl(ModelImpl model, Object platformProject,
071: String name) {
072: super (model, platformProject, name);
073: // RepositoryUtils.put(this);
074: }
075:
076: public static ProjectImpl createInstance(ModelImpl model,
077: String platformProject, String name) {
078: return createInstance(model, (Object) platformProject, name);
079: }
080:
081: public static ProjectImpl createInstance(ModelImpl model,
082: NativeProject platformProject, String name) {
083: return createInstance(model, (Object) platformProject, name);
084: }
085:
086: private static ProjectImpl createInstance(ModelImpl model,
087: Object platformProject, String name) {
088: ProjectBase instance = null;
089: if (TraceFlags.PERSISTENT_REPOSITORY) {
090: try {
091: instance = readInstance(model, platformProject, name);
092: } catch (Exception e) {
093: // just report to console;
094: // the code below will create project "from scratch"
095: cleanRepository(platformProject, false);
096: DiagnosticExceptoins.register(e);
097: }
098: }
099: if (instance == null) {
100: instance = new ProjectImpl(model, platformProject, name);
101: }
102: return (ProjectImpl) instance;
103: }
104:
105: protected void scheduleIncludedFileParsing(FileImpl csmFile,
106: APTPreprocHandler.State state) {
107: // add project's file to the head
108: ParserQueue.instance().addFirst(csmFile, state, true);
109: }
110:
111: public @Override
112: void onFileEditStart(final FileBuffer buf, NativeFileItem nativeFile) {
113: if (!acceptNativeItem(nativeFile)) {
114: return;
115: }
116: if (TraceFlags.DEBUG)
117: Diagnostic
118: .trace("------------------------- onFileEditSTART "
119: + buf.getFile().getName()); // NOI18N
120: final FileImpl impl = createOrFindFileImpl(buf, nativeFile);
121: if (impl != null) {
122: impl.setBuffer(buf);
123: synchronized (editedFiles) {
124: editedFiles.add(impl);
125: }
126: if (TraceFlags.USE_AST_CACHE) {
127: CacheManager.getInstance().invalidate(impl);
128: } else {
129: APTDriver.getInstance().invalidateAPT(buf);
130: }
131: schedule(buf, impl);
132: buf.addChangeListener(new ChangeListener() {
133: public void stateChanged(ChangeEvent e) {
134: schedule(buf, impl);
135: }
136: });
137: }
138: }
139:
140: public @Override
141: void onFileEditEnd(FileBuffer buf, NativeFileItem nativeFile) {
142: if (!acceptNativeItem(nativeFile)) {
143: return;
144: }
145: if (TraceFlags.DEBUG)
146: Diagnostic.trace("------------------------- onFileEditEND "
147: + buf.getFile().getName()); // NOI18N
148: FileImpl file = getFile(buf.getFile());
149: if (file != null) {
150: synchronized (editedFiles) {
151: if (!editedFiles.remove(file)) {
152: // FixUp double file edit end on mounted files
153: return;
154: }
155: }
156: file.setBuffer(buf);
157: if (TraceFlags.USE_DEEP_REPARSING) {
158: DeepReparsingUtils.reparseOnEdit(file, this );
159: } else {
160: ParserQueue.instance().addFirst(file,
161: getPreprocHandler(buf.getFile()).getState(),
162: false);
163: }
164: }
165: }
166:
167: private void addToQueue(FileBuffer buf, FileImpl file) {
168: if (isDisposing()) {
169: return;
170: }
171: try {
172: file.scheduleParsing(true);
173: } catch (InterruptedException ex) {
174: DiagnosticExceptoins.register(ex);
175: }
176: }
177:
178: public void onFilePropertyChanged(NativeFileItem nativeFile) {
179: if (!acceptNativeItem(nativeFile)) {
180: return;
181: }
182: if (TraceFlags.DEBUG)
183: Diagnostic
184: .trace("------------------------- onFilePropertyChanged "
185: + nativeFile.getFile().getName()); // NOI18N
186: DeepReparsingUtils.reparseOnPropertyChanged(nativeFile, this );
187: }
188:
189: public void onFilePropertyChanged(List<NativeFileItem> items) {
190: if (items.size() > 0) {
191: DeepReparsingUtils.reparseOnPropertyChanged(items, this );
192: }
193: }
194:
195: public void onFileRemoved(FileImpl impl) {
196: try {
197: //Notificator.instance().startTransaction();
198: onFileRemovedImpl(impl);
199: if (impl != null) {
200: DeepReparsingUtils.reparseOnRemoved(impl, this );
201: }
202: } finally {
203: //Notificator.instance().endTransaction();
204: Notificator.instance().flush();
205: }
206: }
207:
208: private FileImpl onFileRemovedImpl(FileImpl impl) {
209: APTIncludeUtils.clearFileExistenceCache();
210: if (impl != null) {
211: synchronized (editedFiles) {
212: editedFiles.remove(impl);
213: }
214: removeNativeFileItem(impl.getUID());
215: impl.dispose();
216: removeFile(new File(impl.getAbsolutePath()));
217: if (TraceFlags.USE_AST_CACHE) {
218: CacheManager.getInstance().invalidate(impl);
219: } else {
220: APTDriver.getInstance().invalidateAPT(impl.getBuffer());
221: }
222: ParserQueue.instance().remove(impl);
223: }
224: return impl;
225: }
226:
227: public void onFileRemoved(List<NativeFileItem> items) {
228: try {
229: ParserQueue.instance().onStartAddingProjectFiles(this );
230: List<FileImpl> toReparse = new ArrayList<FileImpl>();
231: for (NativeFileItem item : items) {
232: File file = item.getFile();
233: try {
234: //Notificator.instance().startTransaction();
235: FileImpl impl = getFile(file);
236: if (impl != null) {
237: onFileRemovedImpl(impl);
238: toReparse.add(impl);
239: }
240: } finally {
241: //Notificator.instance().endTransaction();
242: Notificator.instance().flush();
243: }
244: }
245: DeepReparsingUtils.reparseOnRemoved(toReparse, this );
246: } finally {
247: ParserQueue.instance().onEndAddingProjectFiles(this );
248: }
249: }
250:
251: public void onFileAdded(NativeFileItem nativeFile) {
252: onFileAddedImpl(nativeFile, true);
253: }
254:
255: private NativeFileItem onFileAddedImpl(NativeFileItem nativeFile,
256: boolean deepReparse) {
257: if (acceptNativeItem(nativeFile)) {
258: APTIncludeUtils.clearFileExistenceCache();
259: try {
260: //Notificator.instance().startTransaction();
261: createIfNeed(nativeFile, isSourceFile(nativeFile), null);
262: return nativeFile;
263: } finally {
264: //Notificator.instance().endTransaction();
265: Notificator.instance().flush();
266: if (deepReparse) {
267: DeepReparsingUtils.reparseOnAdded(nativeFile, this );
268: }
269: }
270: }
271: return null;
272: }
273:
274: public void onFileAdded(List<NativeFileItem> items) {
275: try {
276: ParserQueue.instance().onStartAddingProjectFiles(this );
277: List<NativeFileItem> toReparse = new ArrayList<NativeFileItem>();
278: for (NativeFileItem item : items) {
279: NativeFileItem done = onFileAddedImpl(item, false);
280: if (done != null) {
281: toReparse.add(done);
282: }
283: }
284: DeepReparsingUtils.reparseOnAdded(toReparse, this );
285: } finally {
286: ParserQueue.instance().onEndAddingProjectFiles(this );
287: }
288: }
289:
290: protected @Override
291: void ensureChangedFilesEnqueued() {
292: synchronized (editedFiles) {
293: super .ensureChangedFilesEnqueued();
294: for (Iterator iter = editedFiles.iterator(); iter.hasNext();) {
295: FileImpl file = (FileImpl) iter.next();
296: if (!file.isParsingOrParsed()) {
297: ParserQueue.instance().addLast(
298: file,
299: getPreprocHandler(
300: file.getBuffer().getFile())
301: .getState());
302: }
303: }
304: }
305: //N.B. don't clear list of editedFiles here.
306: }
307:
308: protected @Override
309: boolean hasChangedFiles(CsmFile skipFile) {
310: if (skipFile == null) {
311: return false;
312: }
313: synchronized (editedFiles) {
314: for (Iterator iter = editedFiles.iterator(); iter.hasNext();) {
315: FileImpl file = (FileImpl) iter.next();
316: if ((skipFile != file) && !file.isParsingOrParsed()) {
317: return true;
318: }
319: }
320: }
321: return false;
322: }
323:
324: private Set<CsmFile> editedFiles = new HashSet<CsmFile>();
325:
326: public @Override
327: ProjectBase findFileProject(CharSequence absPath) {
328: ProjectBase retValue = super .findFileProject(absPath);
329: // trick for tracemodel. We should accept all not registered files as well, till it is not system one.
330: if (ParserThreadManager.instance().isStandalone()) {
331: retValue = absPath.toString().startsWith("/usr") ? retValue
332: : this ; // NOI18N
333: }
334: return retValue;
335: }
336:
337: public boolean isArtificial() {
338: return false;
339: }
340:
341: @Override
342: public NativeFileItem getNativeFileItem(CsmUID<CsmFile> file) {
343: return nativeFiles.getNativeFileItem(file);
344: }
345:
346: @Override
347: protected void putNativeFileItem(CsmUID<CsmFile> file,
348: NativeFileItem nativeFileItem) {
349: nativeFiles.putNativeFileItem(file, nativeFileItem);
350: }
351:
352: @Override
353: protected void removeNativeFileItem(CsmUID<CsmFile> file) {
354: nativeFiles.removeNativeFileItem(file);
355: }
356:
357: @Override
358: protected void clearNativeFileContainer() {
359: nativeFiles.clear();
360: }
361:
362: private NativeFileContainer nativeFiles = new NativeFileContainer();
363:
364: ////////////////////////////////////////////////////////////////////////////
365: // impl of persistent
366:
367: public @Override
368: void write(DataOutput aStream) throws IOException {
369: super .write(aStream);
370: // we don't need this since ProjectBase persists fqn
371: //UIDObjectFactory aFactory = UIDObjectFactory.getDefaultFactory();
372: //aFactory.writeUID(getUID(), aStream);
373: LibraryManager.getInstance().writeProjectLibraries(getUID(),
374: aStream);
375: }
376:
377: public ProjectImpl(DataInput input) throws IOException {
378: super (input);
379: // we don't need this since ProjectBase persists fqn
380: //UIDObjectFactory aFactory = UIDObjectFactory.getDefaultFactory();
381: //CsmUID uid = aFactory.readUID(input);
382: //LibraryManager.getInsatnce().read(uid, input);
383: LibraryManager.getInstance().readProjectLibraries(getUID(),
384: input);
385: //nativeFiles = new NativeFileContainer();
386: }
387:
388: ////////////////////////////////////////////////////////////////////////////
389: private RequestProcessor.Task task = null;
390: private final static int DELAY = 1001;
391:
392: public void schedule(final FileBuffer buf, final FileImpl file) {
393: if (task == null) {
394: task = RequestProcessor.getDefault().create(new Runnable() {
395: public void run() {
396: try {
397: addToQueue(buf, file);
398: } catch (AssertionError ex) {
399: DiagnosticExceptoins.register(ex);
400: } catch (Exception ex) {
401: DiagnosticExceptoins.register(ex);
402: }
403: }
404: }, true);
405: task.setPriority(Thread.MIN_PRIORITY);
406: }
407: task.cancel();
408: task.schedule(DELAY);
409: }
410:
411: @Override
412: public void setDisposed() {
413: super.setDisposed();
414: if (task != null) {
415: task.cancel();
416: }
417: }
418:
419: }
|