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.ArrayList;
049: import java.util.Collection;
050: import java.util.Collections;
051: import java.util.HashSet;
052: import java.util.List;
053: import java.util.Map;
054: import java.util.Set;
055: import java.util.concurrent.ConcurrentHashMap;
056: import org.netbeans.modules.cnd.api.model.CsmModelAccessor;
057: import org.netbeans.modules.cnd.api.model.CsmProject;
058: import org.netbeans.modules.cnd.api.model.CsmUID;
059: import org.netbeans.modules.cnd.apt.support.ResolvedPath;
060: import org.netbeans.modules.cnd.modelimpl.debug.TraceFlags;
061: import org.netbeans.modules.cnd.modelimpl.repository.PersistentUtils;
062: import org.openide.filesystems.FileUtil;
063:
064: /**
065: * Artificial libraries manager.
066: * Manage auto ctrated libraries (artificial libraries) for included files.
067: *
068: *
069: * @author Alexander Simon
070: */
071: public final class LibraryManager {
072: private static LibraryManager instance = new LibraryManager();
073:
074: public static LibraryManager getInstance() {
075: return instance;
076: }
077:
078: private LibraryManager() {
079: }
080:
081: private final Map<String, LibraryEntry> librariesEntries = new ConcurrentHashMap<String, LibraryEntry>();
082: private Object lock = new Object();
083:
084: /**
085: * Returns collection of artificial libraries used in project
086: */
087: public Collection<LibProjectImpl> getLiraries(ProjectImpl project) {
088: List<LibProjectImpl> res = new ArrayList<LibProjectImpl>();
089: CsmUID<CsmProject> projectUid = project.getUID();
090: for (LibraryEntry entry : librariesEntries.values()) {
091: if (entry.containsProject(projectUid)) {
092: LibProjectImpl lib = (LibProjectImpl) entry
093: .getLibrary().getObject();
094: if (lib != null) {
095: res.add(lib);
096: }
097: }
098: }
099: return res;
100: }
101:
102: /**
103: * Returns collection uids of artificial libraries used in project
104: */
105: public Collection<CsmUID<CsmProject>> getLirariesKeys(
106: CsmUID<CsmProject> projectUid) {
107: List<CsmUID<CsmProject>> res = new ArrayList<CsmUID<CsmProject>>();
108: for (LibraryEntry entry : librariesEntries.values()) {
109: if (entry.containsProject(projectUid)) {
110: res.add(entry.getLibrary());
111: }
112: }
113: return res;
114: }
115:
116: private void trace(String where, FileImpl curFile,
117: ResolvedPath resolvedPath, ProjectBase res,
118: ProjectBase start) {
119: System.out.println("Resolved Path " + resolvedPath.getPath()); //NOI18N
120: System.out.println(" start project " + start); //NOI18N
121: System.out.println(" found in " + where + " " + res); //NOI18N
122: System.out.println(" included from " + curFile); //NOI18N
123: System.out.println(" file from project "
124: + curFile.getProject()); //NOI18N
125: for (CsmProject prj : start.getLibraries()) {
126: System.out.println(" search lib " + prj); //NOI18N
127: }
128: }
129:
130: /**
131: * Find project for resolved file.
132: * Search for project in proroject, dependancies, artificial libraries.
133: * If search is false then method creates artificial library or returns base project.
134: */
135: public ProjectBase resolveFileProjectOnInclude(
136: ProjectBase baseProject, FileImpl curFile,
137: ResolvedPath resolvedPath) {
138: String absPath = resolvedPath.getPath();
139: File searchFor = new File(absPath);
140: ProjectBase res = searchInProjectFiles(baseProject, searchFor);
141: if (res != null) {
142: //if (TraceFlags.TRACE_RESOLVED_LIBRARY) trace("Projects", curFile, resolvedPath, res, baseProject); //NOI18N
143: return res;
144: }
145: String folder = FileUtil.normalizeFile(
146: new File(resolvedPath.getFolder())).getAbsolutePath();
147: res = searchInProjectRoots(baseProject, getPathToFolder(folder,
148: resolvedPath.getPath()));
149: if (res != null) {
150: //if (TraceFlags.TRACE_RESOLVED_LIBRARY) trace("Projects roots", curFile, resolvedPath, res, baseProject); //NOI18N
151: return res;
152: }
153: res = searchInProjectFilesArtificial(baseProject, searchFor);
154: if (res != null) {
155: //if (TraceFlags.TRACE_RESOLVED_LIBRARY) trace("Libraries", curFile, resolvedPath, res, baseProject);
156: return res;
157: }
158: res = searchInProjectRootsArtificial(baseProject,
159: getPathToFolder(folder, resolvedPath.getPath()));
160: if (res == null) {
161: if (resolvedPath.isDefaultSearchPath()) {
162: res = baseProject;
163: //if (TraceFlags.TRACE_RESOLVED_LIBRARY) trace("Base Project as Default Search Path", curFile, resolvedPath, res, baseProject); //NOI18N
164: } else if (!baseProject.isArtificial()) {
165: res = getLibrary((ProjectImpl) baseProject, folder);
166: if (TraceFlags.TRACE_RESOLVED_LIBRARY)
167: trace("Library for folder " + folder, curFile,
168: resolvedPath, res, baseProject); //NOI18N
169: } else {
170: res = baseProject;
171: //if (TraceFlags.TRACE_RESOLVED_LIBRARY) trace("Base Project", curFile, resolvedPath, res, baseProject); //NOI18N
172: }
173: } else {
174: //if (TraceFlags.TRACE_RESOLVED_LIBRARY) trace("Libraries roots", curFile, resolvedPath, res, baseProject); //NOI18N
175: }
176: return res;
177: }
178:
179: private List<String> getPathToFolder(String folder, String path) {
180: List<String> res = new ArrayList<String>(3);
181: res.add(folder);
182: if (path.startsWith(folder)) {
183: File file = FileUtil.normalizeFile(new File(path));
184: while (file != null) {
185: String dir = file.getParent();
186: if (folder.equals(dir) || !dir.startsWith(folder)) {
187: break;
188: }
189: res.add(dir);
190: if (res.size() == 3) {
191: break;
192: }
193: file = file.getParentFile();
194: }
195: }
196: return res;
197: }
198:
199: private ProjectBase searchInProjectFiles(ProjectBase baseProject,
200: File searchFor) {
201: return searchInProjectFiles(baseProject, searchFor,
202: new HashSet<ProjectBase>());
203: }
204:
205: private ProjectBase searchInProjectFiles(ProjectBase baseProject,
206: File searchFor, Set<ProjectBase> set) {
207: if (set.contains(baseProject)) {
208: return null;
209: }
210: set.add(baseProject);
211: baseProject.ensureFilesCreated();
212: FileImpl file = baseProject.getFile(searchFor);
213: if (file != null) {
214: return baseProject;
215: }
216: for (CsmProject prj : baseProject.getLibraries()) {
217: if (prj.isArtificial()) {
218: break;
219: }
220: ((ProjectBase) prj).ensureFilesCreated();
221: ProjectBase res = searchInProjectFiles((ProjectBase) prj,
222: searchFor, set);
223: if (res != null) {
224: return res;
225: }
226: }
227: return null;
228: }
229:
230: private ProjectBase searchInProjectFilesArtificial(
231: ProjectBase baseProject, File searchFor) {
232: for (CsmProject prj : baseProject.getLibraries()) {
233: if (prj.isArtificial()) {
234: ((ProjectBase) prj).ensureFilesCreated();
235: ProjectBase res = searchInProjectFiles(
236: (ProjectBase) prj, searchFor);
237: if (res != null) {
238: return res;
239: }
240: }
241: }
242: return null;
243: }
244:
245: private ProjectBase searchInProjectRoots(ProjectBase baseProject,
246: List<String> folders) {
247: return searchInProjectRoots(baseProject, folders,
248: new HashSet<ProjectBase>());
249: }
250:
251: private ProjectBase searchInProjectRoots(ProjectBase baseProject,
252: List<String> folders, HashSet<ProjectBase> set) {
253: if (set.contains(baseProject)) {
254: return null;
255: }
256: set.add(baseProject);
257: for (String folder : folders) {
258: if (baseProject.isMySource(folder)) {
259: return baseProject;
260: }
261: }
262: for (CsmProject prj : baseProject.getLibraries()) {
263: if (prj.isArtificial()) {
264: break;
265: }
266: ProjectBase res = searchInProjectRoots((ProjectBase) prj,
267: folders, set);
268: if (res != null) {
269: return res;
270: }
271: }
272: return null;
273: }
274:
275: private ProjectBase searchInProjectRootsArtificial(
276: ProjectBase baseProject, List<String> folders) {
277: for (CsmProject prj : baseProject.getLibraries()) {
278: if (prj.isArtificial()) {
279: ProjectBase res = searchInProjectRoots(
280: (ProjectBase) prj, folders);
281: if (res != null) {
282: return res;
283: }
284: }
285: }
286: return null;
287: }
288:
289: private LibProjectImpl getLibrary(ProjectImpl project, String folder) {
290: CsmUID<CsmProject> projectUid = project.getUID();
291: LibraryEntry entry = librariesEntries.get(folder);
292: if (entry == null) {
293: entry = getOrCreateLibrary(project.getModel(), folder);
294: }
295: if (!entry.containsProject(projectUid)) {
296: entry.addProject(projectUid);
297: }
298: return (LibProjectImpl) entry.getLibrary().getObject();
299: }
300:
301: private LibraryEntry getOrCreateLibrary(final ModelImpl model,
302: final String includeFolder) {
303: LibraryEntry entry = librariesEntries.get(includeFolder);
304: if (entry == null) {
305: boolean needFire = false;
306: synchronized (lock) {
307: entry = librariesEntries.get(includeFolder);
308: if (entry == null) {
309: entry = new LibraryEntry(includeFolder);
310: librariesEntries.put(includeFolder, entry);
311: needFire = true;
312: }
313: }
314: if (needFire) {
315: final LibraryEntry passEntry = entry;
316: CsmModelAccessor.getModel().enqueue(new Runnable() {
317: public void run() {
318: ListenersImpl.getImpl().fireProjectOpened(
319: (ProjectBase) passEntry.getLibrary()
320: .getObject());
321: }
322: }, "postponed library opened " + includeFolder); // NOI18N
323: }
324: }
325: return entry;
326: }
327:
328: public void onProjectPropertyChanged(CsmUID<CsmProject> project) {
329: for (LibraryEntry entry : librariesEntries.values()) {
330: entry.removeProject(project);
331: }
332: }
333:
334: /**
335: * Close unused artificial libraries.
336: */
337: public void onProjectClose(CsmUID<CsmProject> project) {
338: List<LibraryEntry> toClose = new ArrayList<LibraryEntry>();
339: for (LibraryEntry entry : librariesEntries.values()) {
340: entry.removeProject(project);
341: if (entry.isEmpty()) {
342: toClose.add(entry);
343: }
344: }
345: if (toClose.size() > 0) {
346: for (LibraryEntry entry : toClose) {
347: librariesEntries.remove(entry.getFolder());
348: }
349: }
350: closeLibraries(toClose);
351: }
352:
353: private void closeLibraries(Collection<LibraryEntry> entries) {
354: ModelImpl model = (ModelImpl) CsmModelAccessor.getModel();
355: for (LibraryEntry entry : entries) {
356: CsmUID<CsmProject> uid = entry.getLibrary();
357: ProjectBase lib = (ProjectBase) uid.getObject();
358: assert lib != null : "Null project for UID " + uid;
359: model.disposeProject(lib);
360: }
361: }
362:
363: /**
364: * Write artificial libraries for project
365: */
366: /*package-local*/void writeProjectLibraries(
367: CsmUID<CsmProject> project, DataOutput aStream)
368: throws IOException {
369: assert aStream != null;
370: Set<String> res = new HashSet<String>();
371: for (LibraryEntry entry : librariesEntries.values()) {
372: if (entry.containsProject(project)) {
373: res.add(entry.getFolder());
374: }
375: }
376: PersistentUtils.writeCollectionStrings(res, aStream);
377: }
378:
379: /**
380: * Read artificial libraries for project
381: */
382: /*package-local*/void readProjectLibraries(
383: CsmUID<CsmProject> project, DataInput aStream)
384: throws IOException {
385: ModelImpl model = (ModelImpl) CsmModelAccessor.getModel();
386: assert aStream != null;
387: Collection<CharSequence> res = PersistentUtils
388: .readCollectionStrings(aStream, null);
389: for (CharSequence folder : res) {
390: LibraryEntry entry = getOrCreateLibrary(model, folder
391: .toString());
392: entry.addProject(project);
393: }
394: }
395:
396: private static class LibraryEntry {
397: private String folder;
398: private CsmUID<CsmProject> libraryUID;
399: private Set<CsmUID<CsmProject>> dependentProjects;
400:
401: private LibraryEntry(String folder) {
402: this .folder = folder;
403: dependentProjects = Collections
404: .synchronizedSet(new HashSet<CsmUID<CsmProject>>());
405: }
406:
407: private String getFolder() {
408: return folder;
409: }
410:
411: private CsmUID<CsmProject> getLibrary() {
412: if (libraryUID == null) {
413: createUID();
414: }
415: return libraryUID;
416: }
417:
418: private synchronized void createUID() {
419: if (libraryUID == null) {
420: ModelImpl model = (ModelImpl) CsmModelAccessor
421: .getModel();
422: LibProjectImpl library = LibProjectImpl.createInstance(
423: model, folder);
424: libraryUID = library.getUID();
425: }
426: }
427:
428: private boolean isEmpty() {
429: return dependentProjects.size() == 0;
430: }
431:
432: private boolean containsProject(CsmUID<CsmProject> project) {
433: return dependentProjects.contains(project);
434: }
435:
436: private void addProject(CsmUID<CsmProject> project) {
437: dependentProjects.add(project);
438: }
439:
440: private void removeProject(CsmUID<CsmProject> project) {
441: dependentProjects.remove(project);
442: }
443: }
444: }
|