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.platform;
043:
044: import java.io.IOException;
045: import org.netbeans.modules.cnd.api.project.NativeFileItem;
046: import org.netbeans.modules.cnd.api.project.NativeFileItemSet;
047: import org.netbeans.modules.cnd.api.project.NativeProject;
048: import org.netbeans.modules.cnd.modelimpl.debug.Diagnostic;
049:
050: import java.beans.PropertyChangeEvent;
051: import java.beans.PropertyChangeListener;
052:
053: import java.io.File;
054: import java.util.*;
055: import javax.swing.SwingUtilities;
056: import javax.swing.event.ChangeEvent;
057: import javax.swing.event.ChangeListener;
058: import javax.swing.text.Document;
059:
060: import org.netbeans.api.project.*;
061: import org.netbeans.api.project.ui.OpenProjects;
062:
063: import org.netbeans.modules.cnd.MIMENames;
064: import org.netbeans.modules.cnd.api.model.CsmModelAccessor;
065: import org.netbeans.modules.cnd.modelimpl.csm.core.*;
066: import org.netbeans.modules.cnd.modelimpl.debug.TraceFlags;
067: import org.netbeans.modules.cnd.modelimpl.memory.LowMemoryEvent;
068: import org.netbeans.modules.cnd.modelimpl.options.CodeAssistanceOptions;
069: import org.netbeans.modules.cnd.modelimpl.spi.LowMemoryAlerter;
070: import org.openide.cookies.EditorCookie;
071: import org.openide.filesystems.FileObject;
072:
073: import org.openide.filesystems.FileUtil;
074: import org.openide.loaders.DataObject;
075: import org.openide.loaders.DataObjectNotFoundException;
076: import org.openide.modules.InstalledFileLocator;
077: import org.openide.util.Exceptions;
078: import org.openide.util.Lookup;
079: import org.openide.util.RequestProcessor;
080: import org.openide.windows.TopComponent;
081: import org.openide.windows.WindowManager;
082:
083: /**
084: *
085: * @author Vladimir Kvashin
086: */
087: public class ModelSupport implements PropertyChangeListener {
088:
089: private static ModelSupport instance = new ModelSupport();
090:
091: private ModelImpl theModel;
092:
093: private Set<Project> openedProjects = new HashSet<Project>();
094:
095: private final ModifiedObjectsChangeListener modifiedListener = new ModifiedObjectsChangeListener();
096:
097: private static final boolean TRACE_STARTUP = false;
098: private volatile boolean postponeParse = false;
099:
100: private ModelSupport() {
101: }
102:
103: public static ModelSupport instance() {
104: return instance;
105: }
106:
107: public static int getTabSize() {
108: return 8;
109: }
110:
111: public File locateFile(String fileName) {
112: InstalledFileLocator locator = InstalledFileLocator
113: .getDefault();
114: if (locator != null) {
115: File file = locator.locate(fileName,
116: "com.sun.tools.swdev.parser.impl/1", false); // NOI18N
117: if (file != null) {
118: return file;
119: }
120: }
121: // the above code is mostly for debugging purposes;
122: // but seems it didn't spoil anything :)
123: File file = new File(fileName);
124: return file.exists() ? file : null;
125: }
126:
127: public void setModel(ModelImpl model) {
128: this .theModel = model;
129: }
130:
131: public void startup() {
132:
133: DataObject.getRegistry().addChangeListener(modifiedListener);
134:
135: if (!ModelImpl.isStandalone()) {
136: openedProjects = new HashSet<Project>();
137: if (TRACE_STARTUP)
138: System.out.println("Model support: Inited"); // NOI18N
139:
140: if (TopComponent.getRegistry().getOpened().size() > 0) {
141: if (TRACE_STARTUP)
142: System.out
143: .println("Model support: Open projects in Init"); // NOI18N
144: postponeParse = false;
145: OpenProjects.getDefault().addPropertyChangeListener(
146: this );
147: openProjects();
148: } else {
149: if (TRACE_STARTUP)
150: System.out
151: .println("Model support: Postpone open projects"); // NOI18N
152: postponeParse = true;
153: WindowManager.getDefault().invokeWhenUIReady(
154: new Runnable() {
155: public void run() {
156: if (TRACE_STARTUP)
157: System.out
158: .println("Model support: invoked after ready UI"); // NOI18N
159: postponeParse = false;
160: Runnable task = new Runnable() {
161: public void run() {
162: OpenProjects
163: .getDefault()
164: .addPropertyChangeListener(
165: ModelSupport.this );
166: openProjects();
167: }
168: };
169: if (SwingUtilities
170: .isEventDispatchThread()) {
171: RequestProcessor.getDefault().post(
172: task);
173: } else {
174: task.run();
175: }
176: }
177: });
178: }
179: }
180: }
181:
182: public void shutdown() {
183: DataObject.getRegistry().removeChangeListener(modifiedListener);
184: ModelImpl model = theModel;
185: if (model != null) {
186: model.shutdown();
187: }
188: }
189:
190: public void propertyChange(PropertyChangeEvent evt) {
191: try { //FIXUP #109105 OpenProjectList does not get notification about adding a project if the project is stored in the repository
192: if (TRACE_STARTUP)
193: System.out.println("Model support event:"
194: + evt.getPropertyName());
195: if (evt.getPropertyName().equals(
196: OpenProjects.PROPERTY_OPEN_PROJECTS)) {
197: if (!postponeParse) {
198: if (TRACE_STARTUP)
199: System.out
200: .println("Model support: Open projects on OpenProjects.PROPERTY_OPEN_PROJECTS"); // NOI18N
201: RequestProcessor.getDefault().post(new Runnable() {
202: public void run() {
203: openProjects();
204: }
205: });
206: }
207: }
208: } catch (Exception e) {
209: e.printStackTrace(System.err);
210: }
211: }
212:
213: private void openProjects() {
214: Project[] projects = OpenProjects.getDefault()
215: .getOpenProjects();
216:
217: synchronized (openedProjects) {
218: Set<Project> nowOpened = new HashSet<Project>();
219: for (int i = 0; i < projects.length; i++) {
220: nowOpened.add(projects[i]);
221: if (!openedProjects.contains(projects[i])) {
222: addProject(projects[i]);
223: }
224: }
225:
226: Set<Project> toClose = new HashSet<Project>();
227: for (Project project : openedProjects) {
228: if (!nowOpened.contains(project)) {
229: toClose.add(project);
230: }
231: }
232:
233: for (Project project : toClose) {
234: closeProject(project);
235: }
236: }
237: }
238:
239: public static boolean needParseOrphan(final Object platformProject) {
240: if (!ModelImpl.isStandalone()) {
241: Project project = ModelImpl
242: .findProjectByNativeProject(getNativeProject(platformProject));
243: if (project != null) {
244: return new CodeAssistanceOptions(project, true)
245: .getParseOrphanEnabled().booleanValue();
246: }
247: }
248: return true;
249: }
250:
251: public static void trace(NativeFileItem nativeFile) {
252: try {
253: Diagnostic.trace(" native file item"
254: + nativeFile.getFile().getAbsolutePath()); // NOI18N
255: Diagnostic.trace(" user includes: "
256: + nativeFile.getUserIncludePaths()); // NOI18N
257: Diagnostic.trace(" user macros: "
258: + nativeFile.getUserMacroDefinitions()); // NOI18N
259: Diagnostic.trace(" system includes: "
260: + nativeFile.getSystemIncludePaths()); // NOI18N
261: Diagnostic.trace(" system macros: "
262: + nativeFile.getSystemMacroDefinitions()); // NOI18N
263: } catch (Exception ex) {
264: ex.printStackTrace(System.err);
265: }
266: }
267:
268: public static void dumpNativeProject(NativeProject nativeProject) {
269: System.err.println("\n\n\nDumping project "
270: + nativeProject.getProjectDisplayName());
271: System.err.println("\nSystem include paths");
272: for (Iterator it = nativeProject.getSystemIncludePaths()
273: .iterator(); it.hasNext();)
274: System.err.println(" " + it.next());
275: System.err.println("\nUser include paths");
276: for (Iterator it = nativeProject.getUserIncludePaths()
277: .iterator(); it.hasNext();)
278: System.err.println(" " + it.next());
279: System.err.println("\nSystem macros");
280: for (Iterator it = nativeProject.getSystemMacroDefinitions()
281: .iterator(); it.hasNext();)
282: System.err.println(" " + it.next());
283: System.err.println("\nUser macros");
284: for (Iterator it = nativeProject.getUserMacroDefinitions()
285: .iterator(); it.hasNext();)
286: System.err.println(" " + it.next());
287: List<NativeFileItem> sources = new ArrayList<NativeFileItem>();
288: List<NativeFileItem> headers = new ArrayList<NativeFileItem>();
289: for (NativeFileItem item : nativeProject.getAllFiles()) {
290: if (!item.isExcluded()) {
291: switch (item.getLanguage()) {
292: case C:
293: case CPP:
294: sources.add(item);
295: break;
296: case C_HEADER:
297: headers.add(item);
298: break;
299: default:
300: break;
301: }
302: }
303: }
304: System.err
305: .println("\nSources: (" + sources.size() + " files )");
306: for (NativeFileItem elem : sources) {
307: System.err.println(elem.getFile().getAbsolutePath());
308: }
309: System.err
310: .println("\nHeaders: (" + headers.size() + " files )");
311: for (NativeFileItem elem : headers) {
312: System.err.println(elem.getFile().getAbsolutePath());
313: }
314:
315: System.err.println("End of project dump\n\n\n");
316: }
317:
318: public static NativeProject getNativeProject(Object platformProject) {
319: NativeProject nativeProject = platformProject instanceof NativeProject ? (NativeProject) platformProject
320: : null;
321: if (platformProject instanceof Project) {
322: Project project = (Project) platformProject;
323: nativeProject = project.getLookup().lookup(
324: NativeProject.class);
325: }
326: return nativeProject;
327: }
328:
329: private String toString(Project project) {
330: StringBuilder sb = new StringBuilder();
331: ProjectInformation pi = ProjectUtils.getInformation(project);
332: if (pi != null) {
333: sb.append(" Name=" + pi.getName()); // NOI18N
334: sb.append(" DisplayName=" + pi.getDisplayName()); // NOI18N
335: }
336: // SourceGroup[] sg = ProjectUtils.getSources(project).getSourceGroups(Sources.TYPE_GENERIC);
337: // for( int i = 0; i < sg.length; i++ ) {
338: // sb.append(" SG DisplayName=" + sg[i].getDisplayName() + " rootFolder=" + sg[i].getRootFolder());
339: // }
340: return sb.toString();
341: }
342:
343: private void addProject(Project project) {
344: if (TraceFlags.DEBUG)
345: Diagnostic.trace("### ModelSupport.addProject: "
346: + toString(project)); // NOI18N
347: NativeProject nativeProject = project.getLookup().lookup(
348: NativeProject.class);
349: if (nativeProject != null) {
350:
351: CsmModelAccessor.getModel(); // just to ensure it's created
352: ModelImpl model = theModel;
353: if (model == null) {
354: return;
355: }
356:
357: openedProjects.add(project);
358: if (TraceFlags.DEBUG) {
359: dumpProjectFiles(nativeProject);
360: }
361:
362: boolean enableModel = new CodeAssistanceOptions(project)
363: .getCodeAssistanceEnabled().booleanValue();
364:
365: model.addProject(nativeProject, nativeProject
366: .getProjectDisplayName(), enableModel);
367: }
368: }
369:
370: private void dumpProjectFiles(NativeProject nativeProject) {
371: if (TraceFlags.DEBUG) {
372: Diagnostic.trace("+++ Sources:"); // NOI18N
373: List<NativeFileItem> sources = new ArrayList<NativeFileItem>();
374: List<NativeFileItem> headers = new ArrayList<NativeFileItem>();
375: for (NativeFileItem item : nativeProject.getAllFiles()) {
376: if (!item.isExcluded()) {
377: switch (item.getLanguage()) {
378: case C:
379: case CPP:
380: sources.add(item);
381: break;
382: case C_HEADER:
383: headers.add(item);
384: break;
385: default:
386: break;
387: }
388: }
389: }
390: for (NativeFileItem elem : sources) {
391: trace(elem);
392: }
393: Diagnostic.trace("+++ Headers:"); // NOI18N
394: for (NativeFileItem elem : headers) {
395: trace(elem);
396: }
397: }
398: }
399:
400: private void closeProject(Project project) {
401: if (TraceFlags.DEBUG)
402: Diagnostic.trace("### ModelSupport.closeProject: "
403: + toString(project)); // NOI18N
404: ModelImpl model = theModel;
405: if (model == null) {
406: return;
407: }
408: NativeProject nativeProject = project.getLookup().lookup(
409: NativeProject.class);
410: if (nativeProject != null) {
411: model.closeProject(nativeProject);
412: }
413: openedProjects.remove(project);
414: }
415:
416: private void removeProject(Project project) {
417: if (TraceFlags.DEBUG)
418: Diagnostic.trace("### ModelSupport.removeProject: "
419: + toString(project)); // NOI18N
420: ModelImpl model = theModel;
421: if (model == null) {
422: return;
423: }
424: NativeProject nativeProject = project.getLookup().lookup(
425: NativeProject.class);
426: if (nativeProject != null) {
427: model.removeProject(nativeProject);
428: }
429: openedProjects.remove(project);
430: }
431:
432: public static FileBuffer getFileBuffer(File file) {
433: FileObject fo = FileUtil.toFileObject(FileUtil
434: .normalizeFile(file));
435: if (fo != null) {
436: try {
437: DataObject dao = DataObject.find(fo);
438: if (dao.isModified()) {
439: EditorCookie editor = dao
440: .getCookie(EditorCookie.class);
441: if (editor != null) {
442: Document doc = editor.getDocument();
443: if (doc != null) {
444: return new FileBufferDoc(file, doc);
445: }
446: }
447: }
448: } catch (DataObjectNotFoundException e) {
449: // nothing
450: }
451: }
452: return new FileBufferFile(file);
453: }
454:
455: public void onMemoryLow(LowMemoryEvent event, boolean fatal) {
456: LowMemoryAlerter alerter = Lookup.getDefault().lookup(
457: LowMemoryAlerter.class);
458: if (alerter != null) {
459: alerter.alert(event, fatal);
460: }
461: }
462:
463: public static NativeProject[] getOpenNativeProjects() {
464: if (ModelImpl.isStandalone()) {
465: return new NativeProject[0];
466: }
467: List<NativeProject> result = new ArrayList<NativeProject>();
468: Project[] projects = OpenProjects.getDefault()
469: .getOpenProjects();
470: for (int i = 0; i < projects.length; i++) {
471: NativeProject nativeProject = projects[i].getLookup()
472: .lookup(NativeProject.class);
473: if (nativeProject != null) {
474: result.add(nativeProject);
475: }
476: }
477: return result.toArray(new NativeProject[result.size()]);
478: }
479:
480: private static final class BufAndProj {
481: public BufAndProj(FileBuffer buffer, ProjectBase project,
482: NativeFileItem nativeFile) {
483: assert buffer != null : "null buffer";
484: this .buffer = buffer;
485: assert project != null : "null project";
486: this .project = project;
487: assert nativeFile != null : "null nativeFile";
488: this .nativeFile = nativeFile;
489: }
490:
491: public final FileBuffer buffer;
492: public final ProjectBase project;
493: public final NativeFileItem nativeFile;
494: }
495:
496: private class ModifiedObjectsChangeListener implements
497: ChangeListener {
498:
499: private Map<DataObject, Collection<BufAndProj>> buffers = new HashMap<DataObject, Collection<BufAndProj>>();
500:
501: private Collection<BufAndProj> getBufNP(DataObject dao) {
502: Collection<BufAndProj> bufNPcoll = buffers.get(dao);
503: return (bufNPcoll == null) ? Collections
504: .<BufAndProj> emptyList() : bufNPcoll;
505: }
506:
507: private void addBufNP(DataObject dao, BufAndProj bufNP) {
508: Collection<BufAndProj> bufNPcoll = buffers.get(dao);
509: if (bufNPcoll == null) {
510: bufNPcoll = new ArrayList<BufAndProj>();
511: buffers.put(dao, bufNPcoll);
512: }
513: bufNPcoll.add(bufNP);
514: }
515:
516: // TODO: need to change implementation when ataObject will contain correct cookie
517: private void editStart(DataObject curObj) {
518: ModelImpl model = theModel;
519: if (model == null) {
520: return;
521: }
522: if (!curObj.isValid()) {//IZ#114182
523: return;
524: }
525: NativeFileItemSet set = curObj.getLookup().lookup(
526: NativeFileItemSet.class);
527: if (set == null) {
528: set = findCanonicalSet(curObj);
529: }
530:
531: if (set != null && !set.getItems().isEmpty()) {
532:
533: EditorCookie editor = curObj
534: .getCookie(EditorCookie.class);
535: Document doc = editor != null ? editor.getDocument()
536: : null;
537: FileObject primaryFile = curObj.getPrimaryFile();
538: File file = FileUtil.toFile(primaryFile);
539: final FileBufferDoc buffer = new FileBufferDoc(file,
540: doc);
541:
542: for (NativeFileItem nativeFile : set.getItems()) {
543: ProjectBase csmProject = (ProjectBase) model
544: .getProject(nativeFile.getNativeProject());
545: if (csmProject != null) { // this could be null when code assistance is turned off for project
546: addBufNP(curObj, new BufAndProj(buffer,
547: csmProject, nativeFile));
548: csmProject.onFileEditStart(buffer, nativeFile);
549: }
550: }
551: }
552: }
553:
554: private boolean isCndDataObject(FileObject fo) {
555: String type = fo.getMIMEType();
556: return MIMENames.CPLUSPLUS_MIME_TYPE.equals(type)
557: || MIMENames.C_MIME_TYPE.equals(type);
558: }
559:
560: private NativeFileItemSet findCanonicalSet(DataObject curObj) {
561: FileObject fo = curObj.getPrimaryFile();
562: if (fo != null && isCndDataObject(fo)) {
563: File file = FileUtil.toFile(fo);
564: // the file can null, for example, when we edit templates
565: if (file != null) {
566: try {
567: file = FileUtil.normalizeFile(file);
568: fo = FileUtil.toFileObject(file
569: .getCanonicalFile());
570: curObj = DataObject.find(fo);
571: return curObj.getLookup().lookup(
572: NativeFileItemSet.class);
573: } catch (IOException ex) {
574: Exceptions.printStackTrace(ex);
575: }
576: }
577: }
578: return null;
579: }
580:
581: // private void editEnd(DataObject curObj) {
582: // // TODO: some weird logic. New FileBufferFile should be created
583: // // instead.
584: //
585: // BufAndProj bufNP = (BufAndProj) buffers.get(curObj);
586: // if( bufNP != null ) {
587: // bufNP.project.onFileEditEnd(bufNP.buffer);
588: // }
589: // }
590:
591: private void traceStateChanged(ChangeEvent e) {
592: if (TraceFlags.DEBUG) {
593: Diagnostic.trace("state of registry changed:"); // NOI18N
594: Diagnostic.indent();
595: if (e != null) {
596: DataObject[] objs = DataObject.getRegistry()
597: .getModified();
598: if (objs.length == 0) {
599: Diagnostic.trace("all objects are saved"); // NOI18N
600: } else {
601: Diagnostic.trace("set of edited objects:"); // NOI18N
602: for (int i = 0; i < objs.length; i++) {
603: DataObject curObj = objs[i];
604: Diagnostic.trace("object " + i + ":"
605: + curObj.getName()); // NOI18N
606: Diagnostic.indent();
607: Diagnostic.trace("with file: "
608: + curObj.getPrimaryFile()); // NOI18N
609: NativeFileItemSet set = curObj
610: .getNodeDelegate().getLookup()
611: .lookup(NativeFileItemSet.class);
612: if (set == null) {
613: Diagnostic
614: .trace("NativeFileItemSet == null"); // NOI18N
615: } else {
616: Diagnostic.trace("NativeFileItemSet:"); // NOI18N
617: for (NativeFileItem item : set
618: .getItems()) {
619: Diagnostic
620: .trace("\t"
621: + item
622: .getNativeProject()
623: .getProjectDisplayName()); // NOI18N
624: }
625: }
626: EditorCookie editor = curObj
627: .getCookie(EditorCookie.class);
628: Diagnostic.trace("has editor support: "
629: + editor); // NOI18N
630: Document doc = editor != null ? editor
631: .getDocument() : null;
632: Diagnostic.trace("with document: " + doc); // NOI18N
633: Diagnostic.unindent();
634: }
635: }
636: } else {
637: Diagnostic
638: .trace("no additional info from event object"); // NOI18N
639: }
640: Diagnostic.unindent();
641: }
642: }
643:
644: public void stateChanged(ChangeEvent e) {
645: if (TraceFlags.DEBUG) {
646: traceStateChanged(e);
647: }
648: if (e != null) {
649:
650: DataObject[] objs = DataObject.getRegistry()
651: .getModified();
652:
653: Set<DataObject> toDelete = new HashSet<DataObject>();
654:
655: // find all files, which stopped editing
656: for (Iterator iter = buffers.keySet().iterator(); iter
657: .hasNext();) {
658: DataObject dao = (DataObject) iter.next();
659: if (!contains(objs, dao)) {
660: for (BufAndProj bufNP : getBufNP(dao)) {
661: if (bufNP != null) {
662: // removing old doc buffer and creating new one
663: bufNP.project.onFileEditEnd(
664: getFileBuffer(bufNP.buffer
665: .getFile()),
666: bufNP.nativeFile);
667: }
668: }
669: toDelete.add(dao);
670: }
671: }
672:
673: // now remove these files from bufres map
674: for (Iterator iter = toDelete.iterator(); iter
675: .hasNext();) {
676: buffers.remove(iter.next());
677: }
678:
679: // add new buffers
680: for (int i = 0; i < objs.length; i++) {
681: if (!buffers.containsKey(objs[i])) {
682: editStart(objs[i]);
683: }
684: }
685: }
686: }
687:
688: private boolean contains(Object[] objs, Object o) {
689: for (int i = 0; i < objs.length; i++) {
690: if (objs[i].equals(o)) {
691: return true;
692: }
693: }
694: return false;
695: }
696: }
697:
698: }
|