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: package org.netbeans.modules.java.hints.infrastructure;
043:
044: import com.sun.source.tree.Tree;
045: import java.io.IOException;
046: import java.util.Enumeration;
047: import java.util.HashMap;
048: import java.util.LinkedList;
049: import java.util.List;
050: import java.util.List;
051: import java.util.Map;
052: import java.util.Map.Entry;
053: import java.util.Set;
054: import java.util.logging.Level;
055: import java.util.logging.Logger;
056: import java.util.prefs.Preferences;
057: import javax.swing.tree.DefaultMutableTreeNode;
058: import javax.swing.tree.DefaultMutableTreeNode;
059: import javax.swing.tree.DefaultTreeModel;
060: import javax.swing.tree.DefaultTreeModel;
061: import javax.swing.tree.DefaultTreeModel;
062: import javax.swing.tree.TreeModel;
063: import org.netbeans.modules.java.hints.infrastructure.Pair;
064: import org.netbeans.modules.java.hints.options.HintsSettings;
065: import org.netbeans.modules.java.hints.spi.AbstractHint;
066: import org.netbeans.modules.java.hints.spi.ErrorRule;
067: import org.netbeans.modules.java.hints.spi.Rule;
068: import org.netbeans.modules.java.hints.spi.TreeRule;
069: import org.openide.cookies.InstanceCookie;
070: import org.openide.filesystems.FileObject;
071: import org.openide.filesystems.FileSystem;
072: import org.openide.filesystems.Repository;
073: import org.openide.loaders.DataObject;
074: import org.openide.loaders.DataObjectNotFoundException;
075:
076: /** Manages rules read from the system filesystem.
077: *
078: * @author Petr Hrebejk
079: */
080: public class RulesManager {
081:
082: // The logger
083: public static Logger LOG = Logger
084: .getLogger("org.netbeans.modules.java.hints"); // NOI18N
085:
086: // Extensions of files
087: private static final String INSTANCE_EXT = ".instance";
088:
089: // Non GUI attribute for NON GUI rules
090: private static final String NON_GUI = "nonGUI"; // NOI18N
091:
092: private static final String RULES_FOLDER = "org-netbeans-modules-java-hints/rules/"; // NOI18N
093: private static final String ERRORS = "errors"; // NOI18N
094: private static final String HINTS = "hints"; // NOI18N
095: private static final String SUGGESTIONS = "suggestions"; // NOI18N
096:
097: // Maps of registered rules
098: private static Map<String, List<ErrorRule>> errors = new HashMap<String, List<ErrorRule>>();
099: private static Map<Tree.Kind, List<TreeRule>> hints = new HashMap<Tree.Kind, List<TreeRule>>();
100: private static Map<Tree.Kind, List<TreeRule>> suggestions = new HashMap<Tree.Kind, List<TreeRule>>();
101:
102: // Tree models for the settings GUI
103: private static TreeModel errorsTreeModel;
104: private static TreeModel hintsTreeModel;
105: private static TreeModel suggestionsTreeModel;
106:
107: private static RulesManager INSTANCE;
108:
109: private RulesManager() {
110: // XXX Start listening on the rules forder. To handle module set changes.
111: initErrors();
112: initHints();
113: initSuggestions();
114: }
115:
116: public static synchronized RulesManager getInstance() {
117: if (INSTANCE == null) {
118: INSTANCE = new RulesManager();
119: }
120: return INSTANCE;
121: }
122:
123: public Map<String, List<ErrorRule>> getErrors() {
124: return errors;
125: }
126:
127: public Map<Tree.Kind, List<TreeRule>> getHints() {
128: return hints;
129: }
130:
131: public Map<Tree.Kind, List<TreeRule>> getHints(boolean onLine) {
132: Map<Tree.Kind, List<TreeRule>> result = new HashMap<Tree.Kind, List<TreeRule>>();
133:
134: for (Entry<Tree.Kind, List<TreeRule>> e : getHints().entrySet()) {
135: List<TreeRule> nueRules = new LinkedList<TreeRule>();
136:
137: for (TreeRule r : e.getValue()) {
138: if (!(r instanceof AbstractHint)) {
139: if (!onLine)
140: nueRules.add(r);
141: continue;
142: }
143:
144: AbstractHint ah = (AbstractHint) r;
145:
146: Preferences p = ah.getPreferences(null);
147:
148: if (p == null) {
149: if (!onLine)
150: nueRules.add(r);
151: continue;
152: }
153:
154: if (ah.getSeverity() == AbstractHint.HintSeverity.CURRENT_LINE_WARNING) {
155: if (onLine)
156: nueRules.add(r);
157: } else {
158: if (!onLine)
159: nueRules.add(r);
160: }
161: }
162:
163: if (!nueRules.isEmpty()) {
164: result.put(e.getKey(), nueRules);
165: }
166: }
167:
168: return result;
169: }
170:
171: public Map<Tree.Kind, List<TreeRule>> getSuggestions() {
172: return suggestions;
173: }
174:
175: public TreeModel getErrorsTreeModel() {
176: return errorsTreeModel;
177: }
178:
179: public TreeModel getHintsTreeModel() {
180: return hintsTreeModel;
181: }
182:
183: public TreeModel getSuggestionsTreeModel() {
184: return suggestionsTreeModel;
185: }
186:
187: // Private methods ---------------------------------------------------------
188:
189: private static void initErrors() {
190: DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
191: errorsTreeModel = new DefaultTreeModel(rootNode);
192: FileSystem fs = Repository.getDefault().getDefaultFileSystem();
193: FileObject folder = fs.getRoot().getFileObject(
194: RULES_FOLDER + ERRORS);
195: List<Pair<Rule, FileObject>> rules = readRules(folder);
196: categorizeErrorRules(rules, errors, folder, rootNode);
197: }
198:
199: private static void initHints() {
200: DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
201: hintsTreeModel = new DefaultTreeModel(rootNode);
202: FileSystem fs = Repository.getDefault().getDefaultFileSystem();
203: FileObject folder = fs.getRoot().getFileObject(
204: RULES_FOLDER + HINTS);
205: List<Pair<Rule, FileObject>> rules = readRules(folder);
206: categorizeTreeRules(rules, hints, folder, rootNode);
207: }
208:
209: private static void initSuggestions() {
210: DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
211: suggestionsTreeModel = new DefaultTreeModel(rootNode);
212: FileSystem fs = Repository.getDefault().getDefaultFileSystem();
213: FileObject folder = fs.getRoot().getFileObject(
214: RULES_FOLDER + SUGGESTIONS);
215: List<Pair<Rule, FileObject>> rules = readRules(folder);
216: categorizeTreeRules(rules, suggestions, folder, rootNode);
217: }
218:
219: /** Read rules from system filesystem */
220: private static List<Pair<Rule, FileObject>> readRules(
221: FileObject folder) {
222:
223: List<Pair<Rule, FileObject>> rules = new LinkedList<Pair<Rule, FileObject>>();
224:
225: if (folder == null) {
226: return rules;
227: }
228:
229: HashMap<FileObject, DefaultMutableTreeNode> dir2node = new HashMap<FileObject, DefaultMutableTreeNode>();
230:
231: // XXX Probably not he best order
232: Enumeration e = folder.getData(true);
233: while (e.hasMoreElements()) {
234: FileObject o = (FileObject) e.nextElement();
235: String name = o.getNameExt().toLowerCase();
236:
237: if (o.canRead()) {
238: Rule r = null;
239: if (name.endsWith(INSTANCE_EXT)) {
240: r = instantiateRule(o);
241: }
242: if (r != null) {
243: rules.add(new Pair<Rule, FileObject>(r, o));
244: }
245: }
246: }
247: return rules;
248: }
249:
250: private static void categorizeErrorRules(
251: List<Pair<Rule, FileObject>> rules,
252: Map<String, List<ErrorRule>> dest, FileObject rootFolder,
253: DefaultMutableTreeNode rootNode) {
254:
255: Map<FileObject, DefaultMutableTreeNode> dir2node = new HashMap<FileObject, DefaultMutableTreeNode>();
256: dir2node.put(rootFolder, rootNode);
257:
258: for (Pair<Rule, FileObject> pair : rules) {
259: Rule rule = pair.getA();
260: FileObject fo = pair.getB();
261:
262: if (rule instanceof ErrorRule) {
263: addRule((ErrorRule) rule, dest);
264: FileObject parent = fo.getParent();
265: DefaultMutableTreeNode category = dir2node.get(parent);
266: if (category == null) {
267: category = new DefaultMutableTreeNode(parent);
268: rootNode.add(category);
269: dir2node.put(parent, category);
270: }
271: category.add(new DefaultMutableTreeNode(rule, false));
272: } else {
273: LOG
274: .log(Level.WARNING, "The rule defined in "
275: + fo.getPath()
276: + "is not instance of ErrorRule");
277: }
278: }
279: }
280:
281: private static void categorizeTreeRules(
282: List<Pair<Rule, FileObject>> rules,
283: Map<Tree.Kind, List<TreeRule>> dest, FileObject rootFolder,
284: DefaultMutableTreeNode rootNode) {
285:
286: Map<FileObject, DefaultMutableTreeNode> dir2node = new HashMap<FileObject, DefaultMutableTreeNode>();
287: dir2node.put(rootFolder, rootNode);
288:
289: for (Pair<Rule, FileObject> pair : rules) {
290: Rule rule = pair.getA();
291: FileObject fo = pair.getB();
292:
293: if (rule instanceof TreeRule) {
294:
295: Object nonGuiObject = fo.getAttribute(NON_GUI);
296: boolean toGui = true;
297:
298: if (nonGuiObject != null
299: && nonGuiObject instanceof Boolean
300: && ((Boolean) nonGuiObject).booleanValue()) {
301: toGui = false;
302: }
303:
304: addRule((TreeRule) rule, dest);
305: FileObject parent = fo.getParent();
306: DefaultMutableTreeNode category = dir2node.get(parent);
307: if (category == null) {
308: category = new DefaultMutableTreeNode(parent);
309: rootNode.add(category);
310: dir2node.put(parent, category);
311: }
312: if (toGui) {
313: category
314: .add(new DefaultMutableTreeNode(rule, false));
315: }
316: } else {
317: LOG.log(Level.WARNING, "The rule defined in "
318: + fo.getPath() + "is not instance of TreeRule");
319: }
320:
321: }
322: }
323:
324: private static void addRule(TreeRule rule,
325: Map<Tree.Kind, List<TreeRule>> dest) {
326:
327: for (Tree.Kind kind : rule.getTreeKinds()) {
328: List<TreeRule> l = dest.get(kind);
329: if (l == null) {
330: l = new LinkedList<TreeRule>();
331: dest.put(kind, l);
332: }
333: l.add(rule);
334: }
335:
336: }
337:
338: @SuppressWarnings("unchecked")
339: private static void addRule(ErrorRule rule,
340: Map<String, List<ErrorRule>> dest) {
341:
342: for (String code : (Set<String>) rule.getCodes()) {
343: List<ErrorRule> l = dest.get(code);
344: if (l == null) {
345: l = new LinkedList<ErrorRule>();
346: dest.put(code, l);
347: }
348: l.add(rule);
349: }
350:
351: }
352:
353: private static Rule instantiateRule(FileObject fileObject) {
354: try {
355: DataObject dobj = DataObject.find(fileObject);
356: InstanceCookie ic = dobj.getCookie(InstanceCookie.class);
357: Object instance = ic.instanceCreate();
358:
359: if (instance instanceof Rule) {
360: return (Rule) instance;
361: } else {
362: return null;
363: }
364: } catch (IOException e) {
365: LOG.log(Level.INFO, null, e);
366: } catch (ClassNotFoundException e) {
367: LOG.log(Level.INFO, null, e);
368: }
369:
370: return null;
371: }
372:
373: // Unused code -------------------------------------------------------------
374:
375: // /** Rules to be run on elements */
376: // private static Map<ElementKind,List<ElementRule>> elementRules = new HashMap<ElementKind,List<ElementRule>>();
377:
378: // private static void addRule( ElementRule rule ) {
379: //
380: // for( ElementKind kind : rule.getElementKinds()) {
381: // List<ElementRule> l = elementRules.get( kind );
382: // if ( l == null ) {
383: // l = new LinkedList<ElementRule>();
384: // elementRules.put( kind, l );
385: // }
386: // l.add( rule );
387: // }
388: //
389: // }
390:
391: // private static class ElementWalker extends ElementScanner6<List<ErrorDescription>,CompilationInfo> {
392: //
393: // private List<ErrorDescription> warnings = new LinkedList<ErrorDescription>();
394: //
395: // @Override
396: // public List<ErrorDescription> scan( Element element, CompilationInfo compilationInfo ) {
397: //
398: // if ( element == null ) {
399: // return warnings;
400: // }
401: //
402: // List<ElementRule> rules = elementRules.get( element.getKind() ); // Find list of rules associated with given kind
403: // if ( rules != null ) {
404: // for (ElementRule rule : rules) { // Run the rules for given node
405: // List<ErrorDescription> w = rule.run( compilationInfo, element, runNumber );
406: // if ( w != null ) {
407: // warnings.addAll( w );
408: // }
409: // }
410: // }
411: //
412: // super.scan( element, compilationInfo );
413: //
414: // return warnings;
415: // }
416: //
417: // }
418: //
419:
420: // /** Runs all rules registered to ElementKinds */
421: // private static List<ErrorDescription> runElementRules() {
422: // ElementScanner6<List<ErrorDescription>,CompilationInfo> v = new ElementWalker();
423: // // XXX How to implement?
424: // return Collections.<ErrorDescription>emptyList();
425: // }
426:
427: }
|