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