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