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:
042: /*
043: * MIDletScanner.java
044: *
045: * Created on 27. May 2004, 15:44
046: */
047: package org.netbeans.modules.mobility.project.ui.customizer;
048:
049: import java.io.BufferedReader;
050: import java.io.File;
051: import java.io.IOException;
052: import java.io.InputStreamReader;
053: import java.lang.ref.Reference;
054: import java.lang.ref.WeakReference;
055: import java.net.URL;
056: import java.util.ArrayList;
057: import java.util.Collection;
058: import java.util.Enumeration;
059: import java.util.HashMap;
060: import java.util.HashSet;
061: import java.util.Iterator;
062: import java.util.List;
063: import java.util.Set;
064: import java.util.regex.Pattern;
065: import javax.lang.model.element.Modifier;
066: import javax.lang.model.element.TypeElement;
067: import javax.lang.model.util.Types;
068: import javax.swing.DefaultComboBoxModel;
069: import javax.swing.event.ChangeEvent;
070: import javax.swing.event.ChangeListener;
071: import org.netbeans.api.java.classpath.ClassPath;
072: import org.netbeans.api.java.platform.JavaPlatform;
073: import org.netbeans.api.java.platform.JavaPlatformManager;
074: import org.netbeans.api.java.platform.Specification;
075: import org.netbeans.api.java.source.CancellableTask;
076: import org.netbeans.api.java.source.ClasspathInfo;
077: import org.netbeans.api.java.source.CompilationController;
078: import org.netbeans.api.java.source.JavaSource;
079: import org.netbeans.api.mobility.project.ui.customizer.ProjectProperties;
080: import org.netbeans.api.project.ant.AntArtifact;
081: import org.netbeans.api.project.libraries.Library;
082: import org.netbeans.modules.mobility.cldcplatform.J2MEPlatform;
083: import org.netbeans.modules.mobility.project.DefaultPropertiesDescriptor;
084: import org.netbeans.spi.mobility.project.ui.customizer.support.VisualPropertySupport;
085: import org.openide.filesystems.FileObject;
086: import org.openide.filesystems.FileUtil;
087: import org.openide.filesystems.URLMapper;
088: import org.openide.util.RequestProcessor;
089:
090: /**
091: *
092: * @author Adam Sotona
093: */
094: public class MIDletScanner implements Runnable {
095:
096: private final ProjectProperties props;
097: private J2MEPlatform activePlatform;
098: private final HashMap<FileObject, HashSet<String>> roots2icons = new HashMap<FileObject, HashSet<String>>();
099: private final HashMap<FileObject, HashSet<String>> roots2midlets = new HashMap<FileObject, HashSet<String>>();
100: private final HashMap<FileObject, HashSet<DefaultComboBoxModel>> roots2iconModels = new HashMap<FileObject, HashSet<DefaultComboBoxModel>>();
101: private final HashMap<FileObject, HashSet<DefaultComboBoxModel>> roots2midletModelts = new HashMap<FileObject, HashSet<DefaultComboBoxModel>>();
102: private final HashSet<ChangeListener> listeners = new HashSet<ChangeListener>();
103: private boolean parsing = false;
104: private static Reference<MIDletScanner> cache = new WeakReference(
105: null);
106:
107: public static MIDletScanner getDefault(ProjectProperties props) {
108: MIDletScanner sc = cache.get();
109: if (sc == null || sc.props != props) {
110: sc = new MIDletScanner(props);
111: cache = new WeakReference(sc);
112: }
113: return sc;
114: }
115:
116: private MIDletScanner(ProjectProperties props) {
117: this .props = props;
118: }
119:
120: public boolean isScanning() {
121: return parsing;
122: }
123:
124: public void scan(final DefaultComboBoxModel midlets,
125: final DefaultComboBoxModel icons,
126: final String configuration, final ChangeListener l) {
127: synchronized (this ) {
128: for (final FileObject root : getRootsFor(configuration)) {
129: if (icons != null) {
130: icons.removeAllElements();
131: HashSet<DefaultComboBoxModel> models = roots2iconModels
132: .get(root);
133: if (models == null) {
134: models = new HashSet<DefaultComboBoxModel>();
135: roots2iconModels.put(root, models);
136: }
137: models.add(icons);
138: }
139: if (midlets != null) {
140: midlets.removeAllElements();
141: HashSet<DefaultComboBoxModel> models = roots2midletModelts
142: .get(root);
143: if (models == null) {
144: models = new HashSet<DefaultComboBoxModel>();
145: roots2midletModelts.put(root, models);
146: }
147: models.add(midlets);
148: }
149: }
150: String platform = (String) props
151: .get(VisualPropertySupport
152: .translatePropertyName(
153: configuration,
154: DefaultPropertiesDescriptor.PLATFORM_ACTIVE,
155: true));
156: final JavaPlatform[] platforms = JavaPlatformManager
157: .getDefault().getPlatforms(
158: null,
159: new Specification(
160: J2MEPlatform.SPECIFICATION_NAME,
161: null));
162: J2MEPlatform pl;
163: for (int i = 0; platform != null && i < platforms.length; i++) {
164: pl = (J2MEPlatform) platforms[i];
165: if (platform.equals(pl.getDisplayName())) {
166: activePlatform = pl;
167: break;
168: }
169: }
170:
171: listeners.add(l);
172: if (!parsing) {
173: parsing = true;
174: RequestProcessor.getDefault().post(this );
175: }
176: }
177: }
178:
179: //------------------------------------------------------------------------------
180:
181: private void fireStateChanged() {
182: final ChangeEvent e = new ChangeEvent(this );
183: for (ChangeListener chl : listeners) {
184: chl.stateChanged(e);
185: }
186: listeners.clear();
187: }
188:
189: private Collection<FileObject> getArchiveRoots(
190: final FileObject fo[]) {
191: final ArrayList<FileObject> files = new ArrayList<FileObject>(
192: fo.length);
193: for (FileObject foi : fo) {
194: files.add(FileUtil.isArchiveFile(foi) ? FileUtil
195: .getArchiveRoot(foi) : foi);
196: }
197: return files;
198: }
199:
200: private HashSet<FileObject> getRootsFor(final String configuration) {
201: final HashSet<FileObject> roots = new HashSet<FileObject>();
202: roots.add(props.getSourceRoot());
203: List<VisualClassPathItem> cpItems = (List<VisualClassPathItem>) props
204: .get(VisualPropertySupport.translatePropertyName(
205: configuration,
206: DefaultPropertiesDescriptor.LIBS_CLASSPATH,
207: true));
208: if (cpItems == null)
209: cpItems = (List<VisualClassPathItem>) props
210: .get(DefaultPropertiesDescriptor.LIBS_CLASSPATH);
211: if (cpItems == null)
212: return roots;
213: for (final VisualClassPathItem item : cpItems) {
214: if (VisualClassPathItem.TYPE_ARTIFACT == item.getType()) {
215: final AntArtifact aa = (AntArtifact) item.getElement();
216: if (aa != null)
217: roots
218: .addAll(getArchiveRoots(aa
219: .getArtifactFiles()));
220: } else if (VisualClassPathItem.TYPE_JAR == item.getType()
221: || VisualClassPathItem.TYPE_FOLDER == item
222: .getType()) {
223: final File f = (File) item.getElement();
224: if (f != null)
225: try {
226: final FileObject fo = FileUtil.toFileObject(f);
227: if (fo != null)
228: roots
229: .add(FileUtil.isArchiveFile(fo) ? FileUtil
230: .getArchiveRoot(fo)
231: : fo);
232: } catch (IllegalArgumentException iae) {
233: }
234: } else if (VisualClassPathItem.TYPE_LIBRARY == item
235: .getType()) {
236: final Library l = (Library) item.getElement();
237: if (l != null) {
238: final Iterator iter = l.getContent("classpath")
239: .iterator(); //NOI18N
240: while (iter.hasNext()) {
241: final FileObject fo = URLMapper
242: .findFileObject((URL) iter.next());
243: if (fo != null)
244: roots
245: .add(FileUtil.isArchiveFile(fo) ? FileUtil
246: .getArchiveRoot(fo)
247: : fo);
248: }
249: }
250: }
251: }
252: roots.remove(null);
253: return roots;
254: }
255:
256: public void run() {
257: FileObject root = null;
258: HashSet<String> midlets, icons;
259: while (true) {
260: synchronized (this ) {
261: HashSet<DefaultComboBoxModel> models = roots2iconModels
262: .remove(root);
263: if (models != null) {
264: fillModels(models, roots2icons.get(root));
265: }
266: models = roots2midletModelts.remove(root);
267: if (models != null) {
268: fillModels(models, roots2midlets.get(root));
269: }
270: root = getNextRoot();
271: if (root == null) {
272: parsing = false;
273: fireStateChanged();
274: return;
275: }
276: if (roots2icons.containsKey(root))
277: continue;
278: icons = new HashSet<String>();
279: midlets = new HashSet<String>();
280: roots2icons.put(root, icons);
281: roots2midlets.put(root, midlets);
282: }
283: try {
284: scanForMIDletsAndIcons(root, icons, midlets);
285: } catch (Exception e) {
286: //don't allow to leave run() this way
287: }
288: }
289: }
290:
291: private FileObject getNextRoot() {
292: if (!roots2iconModels.isEmpty())
293: return roots2iconModels.keySet().iterator().next();
294: if (!roots2midletModelts.isEmpty())
295: return roots2midletModelts.keySet().iterator().next();
296: return null;
297: }
298:
299: private void fillModels(final HashSet<DefaultComboBoxModel> models,
300: final HashSet<String> elements) {
301: for (final DefaultComboBoxModel m : models) {
302: for (final String s : elements) {
303: if (m.getIndexOf(s) < 0)
304: m.addElement(s);
305: }
306: }
307: }
308:
309: private void scanForMIDletsAndIcons(final FileObject root,
310: final HashSet<String> icons, final HashSet<String> midlets) {
311: final String rootPath = root.getPath();
312: final int rootLength = rootPath.length();
313: final Enumeration en = root.getChildren(true);
314: while (en.hasMoreElements()) {
315: final FileObject fo = (FileObject) en.nextElement();
316: if (fo.isData()) {
317: final String ext = fo.getExt().toLowerCase();
318: if ("png".equals(ext)) { // NOI18N
319: String name = fo.getPath().substring(rootLength);
320: if (!name.startsWith("/"))
321: name = "/" + name; //NOI18N
322: synchronized (this ) {
323: icons.add(name);
324: }
325: } else if (("java".equals(ext) || "class".equals(ext))) { // NOI18N
326: //apply brute force (classpath independent if platform is not available)
327: if (activePlatform != null ? isMIDlet2(root, fo)
328: : isMIDlet(fo))
329: synchronized (this ) {
330: String name = FileUtil.getRelativePath(
331: root, fo);
332: midlets.add(name.substring(0,
333: name.length() - ext.length() - 1)
334: .replace('/', '.').replace('\\',
335: '.'));
336: }
337: }
338: }
339: }
340: }
341:
342: private static Pattern p = Pattern
343: .compile("\\s+extends\\s+(javax\\.microedition\\.midlet\\.)?MIDlet");//NOI18N
344:
345: private boolean isMIDlet(FileObject fo) {
346: BufferedReader br = null;
347: try {
348: br = new BufferedReader(new InputStreamReader(fo
349: .getInputStream()));
350: String s;
351: while ((s = br.readLine()) != null) {
352: if (s.indexOf("javax/microedition/midlet/MIDlet") >= 0
353: || p.matcher(s).find())
354: return true; //NOI18N
355: }
356: } catch (IOException ioe) {
357: } finally {
358: if (br != null)
359: try {
360: br.close();
361: } catch (IOException ioe) {
362: }
363: }
364: return false;
365: }
366:
367: private boolean isMIDlet2(FileObject root, final FileObject file) {
368: final Boolean[] result = new Boolean[] { false };
369: ClassPath boot = activePlatform.getBootstrapLibraries();
370: ClassPath rtm2 = ClassPath
371: .getClassPath(root, ClassPath.EXECUTE);
372: ClassPath rtm1 = ClassPath
373: .getClassPath(root, ClassPath.COMPILE);
374: ClassPath rtm = org.netbeans.spi.java.classpath.support.ClassPathSupport
375: .createProxyClassPath(new ClassPath[] { rtm1, rtm2 });
376: final ClassPath clp = org.netbeans.spi.java.classpath.support.ClassPathSupport
377: .createProxyClassPath(new ClassPath[] {
378: ClassPath.getClassPath(root, ClassPath.SOURCE),
379: rtm1 });
380:
381: ClasspathInfo cpInfo = ClasspathInfo.create(boot, rtm, clp);
382: JavaSource js = JavaSource.create(cpInfo);
383: try {
384:
385: js.runUserActionTask(
386: new CancellableTask<CompilationController>() {
387:
388: public void run(CompilationController control)
389: throws Exception {
390: TypeElement type = control
391: .getElements()
392: .getTypeElement(
393: "javax.microedition.midlet.MIDlet");
394: if (type == null) {
395: return;
396: }
397: String name = clp.getResourceName(file);
398: if (name == null)
399: return;
400: name = name.substring(0,
401: name.lastIndexOf('.')).replace('/',
402: '.');
403: TypeElement xtype = control.getElements()
404: .getTypeElement(name);
405: if (xtype == null) {
406: return;
407: }
408: Set<Modifier> modifiers = xtype
409: .getModifiers();
410: if (modifiers.contains(Modifier.ABSTRACT)
411: || !modifiers
412: .contains(Modifier.PUBLIC)) {
413: return;
414: }
415: Types types = control.getTypes();
416: result[0] = types.isSubtype(types
417: .erasure(xtype.asType()), types
418: .erasure(type.asType()));
419: }
420:
421: public void cancel() {
422: }
423: }, true);
424: } catch (IOException ioe) {
425: }
426:
427: return result[0];
428: }
429: }
|