View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang;
5   
6   import java.util.ArrayList;
7   import java.util.Arrays;
8   import java.util.Collections;
9   import java.util.List;
10  
11  import net.sourceforge.pmd.lang.ecmascript.rule.EcmascriptRuleChainVisitor;
12  import net.sourceforge.pmd.lang.java.rule.JavaRuleChainVisitor;
13  import net.sourceforge.pmd.lang.jsp.rule.JspRuleChainVisitor;
14  import net.sourceforge.pmd.lang.plsql.rule.PLSQLRuleChainVisitor;
15  import net.sourceforge.pmd.lang.rule.RuleChainVisitor;
16  import net.sourceforge.pmd.lang.vm.rule.VmRuleChainVisitor;
17  import net.sourceforge.pmd.lang.xml.rule.XmlRuleChainVisitor;
18  
19  /**
20   * This is an enumeration of the Languages of which PMD is aware.  The primary
21   * use of a Language is for Rules, but they are also used by utilities such as
22   * CPD.
23   * <p>
24   * The following are key components of a Language in PMD:
25   * <ul>
26   * 	<li>Name - Full name of the Language</li>
27   * 	<li>Short name - The common short form of the Language</li>
28   * 	<li>Terse name - The shortest and simplest possible form of the Language
29   * 		name, generally used for Rule configuration</li>
30   * 	<li>Extensions - File extensions associated with the Language</li>
31   * 	<li>Rule Chain Visitor - The RuleChainVisitor implementation used for this
32   * 		Language</li>
33   * 	<li>Versions - The LanguageVersions associated with the Language</li>
34   * </ul>
35   *
36   * @see LanguageVersion
37   * @see LanguageVersionDiscoverer
38   */
39  public enum Language {
40  
41      //ANY("Any", null, null, null, (String)null),
42      //UNKNOWN("Unknown", null, "unknown", null, (String)null),
43      CPP("C++", null, "cpp", null, "h", "c", "cpp", "cxx", "cc", "C"),
44      FORTRAN("Fortran", null, "fortran", null, "for", "f", "f66", "f77", "f90"),
45      ECMASCRIPT("Ecmascript", null, "ecmascript", EcmascriptRuleChainVisitor.class, "js"),
46      JAVA("Java", null, "java", JavaRuleChainVisitor.class, "java"),
47      JSP("Java Server Pages", "JSP", "jsp", JspRuleChainVisitor.class, "jsp"),
48      PHP("PHP: Hypertext Preprocessor", "PHP", "php", null, "php", "class"),
49      PLSQL("PLSQL", null, "plsql", PLSQLRuleChainVisitor.class
50           ,"sql", "trg", "prc","fnc"
51  	 ,"pld" // Oracle*Forms 
52  	 ,"pls" ,"plh" ,"plb" // Packages
53  	 ,"pck" ,"pks" ,"pkh" ,"pkb" // Packages
54  	 ,"typ" ,"tyb" // Object Types
55  	 ,"tps" ,"tpb" // Object Types
56           ),
57      RUBY("Ruby", null, "ruby", null, "rb", "cgi", "class"),
58      XSL("XSL", null, "xsl", XmlRuleChainVisitor.class, "xsl", "xslt"),
59      XML("XML", null, "xml", XmlRuleChainVisitor.class, "xml"),
60      VM("VM", null, "vm", VmRuleChainVisitor.class, "vm");
61  
62      private final String name;
63      private final String shortName;
64      private final String terseName;
65      private final List<String> extensions;
66      private final Class<?> ruleChainVisitorClass;
67      private final List<LanguageVersion> versions;
68  
69      /**
70       * Language constructor.
71       *
72       * @param name The name of this Language.  Must not be <code>null</code>.
73       * @param shortName The short name of this Language, if <code>null</code> the
74       * name will be used at the short name.
75       * @param terseName The terse name of this Language.
76       * Must not be <code>null</code>.
77       * @param ruleChainVisitorClass The RuleChainVisitor implementation class.
78       * May be <code>null</code>.
79       * @param extensions An array of extensions for this Language.
80       * May be <code>null</code>.
81       */
82      private Language(String name, String shortName, String terseName, Class<?> ruleChainVisitorClass,
83  	    String... extensions) {
84  	if (name == null) {
85  	    throw new IllegalArgumentException("Name must not be null.");
86  	}
87  	if (terseName == null) {
88  	    throw new IllegalArgumentException("Terse name must not be null.");
89  	}
90  	this.name = name;
91  	this.shortName = shortName != null ? shortName : name;
92  	this.terseName = terseName;
93  	this.ruleChainVisitorClass = ruleChainVisitorClass;
94  	this.extensions = Collections.unmodifiableList(Arrays.asList(extensions));
95  	this.versions = new ArrayList<LanguageVersion>();
96  
97  	// Sanity check: RuleChainVisitor is actually so, and has no arg-constructor?
98  	if (ruleChainVisitorClass != null) {
99  	    try {
100 		Object obj = ruleChainVisitorClass.newInstance();
101 		if (!(obj instanceof RuleChainVisitor)) {
102 		    throw new IllegalStateException("RuleChainVisitor class <" + ruleChainVisitorClass.getName()
103 			    + "> does not implement the RuleChainVisitor interface!");
104 		}
105 	    } catch (InstantiationException e) {
106 		throw new IllegalStateException("Unable to invoke no-arg constructor for RuleChainVisitor class <"
107 			+ ruleChainVisitorClass.getName() + ">!");
108 	    } catch (IllegalAccessException e) {
109 		throw new IllegalStateException("Unable to invoke no-arg constructor for RuleChainVisitor class <"
110 			+ ruleChainVisitorClass.getName() + ">!");
111 	    }
112 	}
113     }
114 
115     /**
116      * Get the full name of this Language.  This is generally the name of this
117      * Language without the use of acronyms.
118      * @return The full name of this Language.
119      */
120     public String getName() {
121 	return name;
122     }
123 
124     /**
125      * Get the short name of this Language.  This is the commonly used short
126      * form of this Language's name, perhaps an acronym.
127      * @return The short name of this Language.
128      */
129     public String getShortName() {
130 	return shortName;
131     }
132 
133     /**
134      * Get the terse name of this Language.  This is used for Rule configuration.
135      * @return The terse name of this Language.
136      */
137     public String getTerseName() {
138 	return terseName;
139     }
140 
141     /**
142      * Get the list of file extensions associated with this Language.
143      * @return List of file extensions.
144      */
145     public List<String> getExtensions() {
146 	return extensions;
147     }
148 
149     /**
150      * Returns whether the given Language handles the given file extension.
151      * The comparison is done ignoring case.
152      * @param extension A file extension.
153      * @return <code>true</code> if this Language handles this extension, <code>false</code> otherwise.
154      */
155     public boolean hasExtension(String extension) {
156 	if (extension != null) {
157 	    for (String ext : extensions) {
158 		if (ext.equalsIgnoreCase(extension)) {
159 		    return true;
160 		}
161 	    }
162 	}
163 	return false;
164     }
165 
166     /**
167      * Get the RuleChainVisitor implementation class used when visiting the AST
168      * structure for this Rules for this Language.
169      * @return The RuleChainVisitor class.
170      * @see RuleChainVisitor
171      */
172     public Class<?> getRuleChainVisitorClass() {
173 	return ruleChainVisitorClass;
174     }
175 
176     /**
177      * Gets the list of supported LanguageVersion for this Language.
178      * @return The LanguageVersion for this Language.
179      */
180     public List<LanguageVersion> getVersions() {
181 	return versions;
182     }
183 
184     /**
185      * Get the current PMD defined default LanguageVersion for this Language.
186      * This is an arbitrary choice made by the PMD product, and can change
187      * between PMD releases.  Every Language has a default version.
188      * @return The current default LanguageVersion for this Language.
189      */
190     public LanguageVersion getDefaultVersion() {
191 	init();
192 	for (LanguageVersion version : getVersions()) {
193 	    if (version.isDefaultVersion()) {
194 		return version;
195 	    }
196 	}
197 	throw new IllegalStateException("No default LanguageVersion configured for " + this);
198     }
199 
200     /**
201      * Get the LanguageVersion for the version string from this Language.
202      * @param version The language version string.
203      * @return The corresponding LanguageVersion, <code>null</code> if the
204      * version string is not recognized.
205      */
206     public LanguageVersion getVersion(String version) {
207 	init();
208 	for (LanguageVersion languageVersion : getVersions()) {
209 	    if (languageVersion.getVersion().equals(version)) {
210 		return languageVersion;
211 	    }
212 	}
213 	return null;
214     }
215 
216     /**
217      * A friendly String form of the Language.
218      */
219     @Override
220     public String toString() {
221 	return "Language [" + name + "]";
222     }
223 
224     /**
225      * A utility method to find the Languages which have Rule support.
226      * @return A List of Languages with Rule support.
227      */
228     public static List<Language> findWithRuleSupport() {
229 	List<Language> languages = new ArrayList<Language>();
230 	for (Language language : Language.values()) {
231 	    if (language.getRuleChainVisitorClass() != null) {
232 		languages.add(language);
233 	    }
234 	}
235 	return languages;
236     }
237 
238     /**
239      * A utility method to find the Languages which are associated with
240      * the given file extension.
241      * @param extension The file extension.
242      * @return A List of Languages which handle the extension.
243      */
244     public static List<Language> findByExtension(String extension) {
245 	List<Language> languages = new ArrayList<Language>();
246 	for (Language language : Language.values()) {
247 	    if (language.hasExtension(extension)) {
248 		languages.add(language);
249 	    }
250 	}
251 	return languages;
252     }
253 
254     /**
255      * A utility method to find the Language associated with the given
256      * terse name, whatever the case is.
257      * @param terseName The Language terse name.
258      * @return The Language with this terse name, <code>null</code> if there is
259      * no Language with this terse name.
260      */
261     public static Language findByTerseName(String terseName) {
262 	for (Language language : Language.values()) {
263 	    if (language.getTerseName().equalsIgnoreCase(terseName)) {
264 		return language;
265 	    }
266 	}
267 	return null;
268     }
269 
270     /**
271      * Return a comma separated list of Language terse names.
272      * @param languages The languages.
273      * @return Comma separated terse names.
274      */
275     public static String commaSeparatedTerseNames(List<Language> languages) {
276 	StringBuilder builder = new StringBuilder();
277 	for (Language language : languages) {
278 	    if (builder.length() > 0) {
279 		builder.append(", ");
280 	    }
281 	    builder.append(language.getTerseName());
282 	}
283 	return builder.toString();
284     }
285 
286     private static void init() {
287 	// Force initialization of the LanguageVersion enum.
288 	// This must be done before the versions can be accessed on this enum.
289 	LanguageVersion.values();
290     }
291 
292     /**
293      * Return the default language for PMD.
294      * @return the proper default language
295      */
296     public static Language getDefaultLanguage() {
297 	return Language.JAVA;
298     }
299 }