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"),
    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 }