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-2006 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.vmd.midp.java;
042:
043: import java.beans.PropertyChangeEvent;
044: import java.beans.PropertyChangeListener;
045: import java.io.IOException;
046: import java.lang.ref.WeakReference;
047: import java.util.ArrayList;
048: import javax.lang.model.element.TypeElement;
049: import javax.lang.model.util.Elements;
050: import java.util.Collection;
051: import java.util.Collections;
052: import java.util.HashMap;
053: import java.util.List;
054: import java.util.concurrent.ConcurrentHashMap;
055: import java.util.concurrent.ConcurrentLinkedQueue;
056: import java.util.concurrent.atomic.AtomicBoolean;
057: import org.netbeans.api.java.classpath.ClassPath;
058: import org.netbeans.api.java.project.JavaProjectConstants;
059: import org.netbeans.modules.vmd.midp.components.*;
060: import org.netbeans.api.java.source.ClasspathInfo;
061: import org.netbeans.api.java.source.CompilationController;
062: import org.netbeans.api.java.source.JavaSource;
063: import org.netbeans.api.java.source.Task;
064: import org.netbeans.api.project.Project;
065: import org.netbeans.api.project.SourceGroup;
066: import org.netbeans.modules.vmd.api.io.DataObjectContext;
067: import org.netbeans.modules.vmd.api.io.ProjectUtils;
068: import org.netbeans.modules.vmd.api.model.ComponentProducer;
069: import org.netbeans.modules.vmd.api.model.Debug;
070: import org.netbeans.modules.vmd.api.model.DescriptorRegistry;
071: import org.netbeans.modules.vmd.api.model.DesignDocument;
072: import org.netbeans.modules.vmd.api.model.TypeID;
073: import org.openide.filesystems.FileObject;
074: import org.openide.util.RequestProcessor;
075: import org.openide.util.WeakListeners;
076:
077: /**
078: * Utility class for checking whether given typeID is in classpath of current project
079: *
080: * @author Anton Chechel
081: */
082: public final class MidpJavaSupport implements Runnable,
083: PropertyChangeListener {
084:
085: private static HashMap<String, MidpJavaSupport> instanceMap;
086: private static RequestProcessor validationRP;
087:
088: private final ConcurrentLinkedQueue<String> validationQueue;
089: private final ConcurrentHashMap<String, Boolean> validationCache;
090: private final AtomicBoolean isValidationRunning;
091: private WeakReference<DesignDocument> document;
092:
093: private MidpJavaSupport(DesignDocument document) {
094: this .document = new WeakReference<DesignDocument>(document);
095:
096: validationQueue = new ConcurrentLinkedQueue<String>();
097: validationCache = new ConcurrentHashMap<String, Boolean>();
098: isValidationRunning = new AtomicBoolean();
099:
100: registerClassPathListener();
101: }
102:
103: public Boolean checkValidityCached(TypeID typeID) {
104: return checkValidityCached(MidpTypes.getFQNClassName(typeID));
105: }
106:
107: public Boolean checkValidityCached(String fqnName) {
108: if (validationCache.containsKey(fqnName)) {
109: return validationCache.get(fqnName);
110: } else {
111: scheduleValidation(fqnName);
112: return null;
113: }
114: }
115:
116: private void scheduleValidation(String fqnName) {
117: validationQueue.add(fqnName);
118: if (!isValidationRunning.getAndSet(true)) {
119: validationRP.post(this );
120: }
121: }
122:
123: public void run() {
124: final DesignDocument doc = document.get();
125: if (doc == null) {
126: return;
127: }
128:
129: while (true) {
130: if (validationQueue.isEmpty()) {
131: isValidationRunning.set(false);
132: break;
133: }
134: String fqnName = validationQueue.remove();
135: boolean isValid = checkValidity(doc, fqnName);
136: validationCache.put(fqnName, isValid);
137: }
138: }
139:
140: private void registerClassPathListener() {
141: final DesignDocument doc = document.get();
142: if (doc == null) {
143: return;
144: }
145:
146: final ClasspathInfo info = getClasspathInfo(ProjectUtils
147: .getProject(doc));
148: if (info == null) {
149: Debug.warning("Can't get ClasspathInfo for project"); // NOI18N
150: return;
151: }
152:
153: try {
154: Task<CompilationController> ct = new Task<CompilationController>() {
155:
156: public void run(CompilationController cc)
157: throws Exception {
158: ClassPath cp = info
159: .getClassPath(ClasspathInfo.PathKind.BOOT);
160: PropertyChangeListener wcl = WeakListeners
161: .propertyChange(MidpJavaSupport.this , cp);
162: cp.addPropertyChangeListener(wcl);
163: }
164: };
165:
166: JavaSource.create(info).runUserActionTask(ct, true);
167: } catch (IOException ex) {
168: Debug.warning(ex);
169: }
170: }
171:
172: private ClasspathInfo getClasspathInfo(Project project) {
173: if (project == null) {
174: return null;
175: }
176:
177: SourceGroup group = getSourceGroup(project);
178: return group != null ? ClasspathInfo.create(group
179: .getRootFolder()) : null;
180: }
181:
182: private SourceGroup getSourceGroup(Project project) {
183: SourceGroup[] sourceGroups = org.netbeans.api.project.ProjectUtils
184: .getSources(project).getSourceGroups(
185: JavaProjectConstants.SOURCES_TYPE_JAVA);
186: return sourceGroups != null && sourceGroups.length > 0 ? sourceGroups[0]
187: : null;
188: }
189:
190: // for classpath listener
191: public void propertyChange(PropertyChangeEvent evt) {
192: updateCacheInternally();
193: }
194:
195: void updateCacheInternally() {
196: final DesignDocument doc = document.get();
197: if (doc == null) {
198: return;
199: }
200:
201: validationCache.clear();
202:
203: final String projectID = doc.getDocumentInterface()
204: .getProjectID();
205: final String projectType = doc.getDocumentInterface()
206: .getProjectType();
207: final List<ComponentProducer> producers = new ArrayList<ComponentProducer>();
208:
209: final DescriptorRegistry registry = DescriptorRegistry
210: .getDescriptorRegistry(projectType, projectID);
211: registry.readAccess(new Runnable() {
212:
213: public void run() {
214: producers.addAll(registry.getComponentProducers());
215: }
216: });
217:
218: final ClasspathInfo info = getClasspathInfo(ProjectUtils
219: .getProject(doc));
220: if (info != null) {
221: try {
222: JavaSource.create(info).runWhenScanFinished(
223: new Task<CompilationController>() {
224:
225: public void run(CompilationController cc)
226: throws Exception {
227: for (ComponentProducer componentProducer : producers) {
228: componentProducer.checkValidity(
229: doc, true);
230: }
231: }
232: }, true);
233: } catch (IOException ex) {
234: Debug.warning(ex);
235: }
236: }
237:
238: }
239:
240: /**
241: * Checks whether given typeID is in classpath of current project
242: *
243: * @param document container of gived typeID
244: * @param typeID to be checked
245: * @return isValid
246: */
247: public static boolean checkValidity(DesignDocument document,
248: TypeID typeID) {
249: return checkValidity(document, MidpTypes
250: .getFQNClassName(typeID));
251: }
252:
253: /**
254: * Checks whether given fqName is in classpath of current project
255: *
256: * @param document container with given classpath context
257: * @param fqName to be checked
258: * @return isValid
259: */
260: public static boolean checkValidity(DesignDocument document,
261: String fqName) {
262: DataObjectContext context = ProjectUtils
263: .getDataObjectContextForDocument(document);
264: if (context == null) { // document is loading
265: return true;
266: }
267:
268: List<SourceGroup> sg = ProjectUtils.getSourceGroups(context);
269: boolean isValid = false;
270: CheckingTask ct = new CheckingTask();
271: Collection<FileObject> collection = Collections.emptySet();
272: for (SourceGroup sourceGroup : sg) {
273: ct.setFQName(fqName);
274:
275: ClasspathInfo cpi = ClasspathInfo.create(sourceGroup
276: .getRootFolder());
277: try {
278: JavaSource.create(cpi, collection).runUserActionTask(
279: ct, true);
280: } catch (Exception ex) {
281: Debug.warning("Can't create javasource for", fqName); // NOI18N
282: }
283: isValid = ct.getResult();
284: if (!isValid) {
285: break;
286: }
287: }
288:
289: return isValid;
290: }
291:
292: /**
293: * @return instance of JavaClassNameResolver for given DesignDocument, creates it
294: * if doesn't exist
295: */
296: public synchronized static MidpJavaSupport getCache(
297: DesignDocument document) {
298: if (instanceMap == null) {
299: instanceMap = new HashMap<String, MidpJavaSupport>(1);
300: validationRP = new RequestProcessor(
301: "VMD MIDP ClassPath validation"); // NOI18N
302: }
303:
304: String projectID = document.getDocumentInterface()
305: .getProjectID();
306: MidpJavaSupport instance = instanceMap.get(projectID);
307: if (instance == null) {
308: instance = new MidpJavaSupport(document);
309: instanceMap.put(projectID, instance);
310: }
311:
312: return instance;
313: }
314:
315: private static final class CheckingTask implements
316: Task<CompilationController> {
317:
318: private boolean result;
319: private String fqName;
320:
321: public void run(CompilationController controller)
322: throws Exception {
323: Elements elements = controller.getElements();
324: TypeElement te = elements.getTypeElement(fqName);
325: result = (te != null);
326: }
327:
328: public boolean getResult() {
329: return result;
330: }
331:
332: public void setFQName(String fqName) {
333: this.fqName = fqName;
334: }
335: }
336: }
|