View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.cpd;
    5
    6   import java.util.Properties;
    7
    8   public class LanguageFactory {
    9
    10      /*
    11       * TODO derive and provide this at runtime instead, used by outside IDEs
    12       * FIXME Can't we do something cleaner and
    13       * more dynamic ? Maybe externalise to a properties files that will
    14       * be generated when building pmd ? This will not have to add manually
    15       * new language here ?
    16      */
    17     public static String[] supportedLanguages =
    18             new String[]{"java", "jsp", "cpp", "c", "php", "ruby", "fortran", "ecmascript", "cs"};
    19
    20     private static final String SUFFIX = "Language";
    21     public static final String EXTENSION = "extension";
    22     public static final String BY_EXTENSION = "by_extension";
    23     private static final String PACKAGE = "net.sourceforge.pmd.cpd.";
    24
    25      public Language createLanguage(String language) {
    26          return createLanguage(language, new Properties());
    27      }
    28
    29     public Language createLanguage(String language, Properties properties)
    30     {
    31       language = this.languageAliases(language);
    32       // First, we look for a parser following this syntax 'RubyLanguage'
    33       Language implementation;
    34       try {
    35         implementation = this.dynamicLanguageImplementationLoad(this.languageConventionSyntax(language));
    36         if ( implementation == null )
    37         {
    38           // if it failed, we look for a parser following this syntax 'CPPLanguage'
    39           implementation = this.dynamicLanguageImplementationLoad(language.toUpperCase());
    40           //TODO: Should we try to break the coupling with PACKAGE by try to load the class
    41           // based on her sole name ?
    42           if ( implementation == null )
    43           {
    44             // No proper implementation
    45             // FIXME: We should log a warning, shouldn't we ?
    46             return new AnyLanguage(language);
    47           }
    48         }
    49         return implementation;
    50       } catch (InstantiationException e) {
    51         e.printStackTrace();
    52       } catch (IllegalAccessException e) {
    53         e.printStackTrace();
    54       }
    55       return null;
    56     }
    57
    58       private String languageAliases(String language)
    59       {
    60         // CPD and C language share the same parser
    61         if ( "c".equals(language) ) {
    62           return "cpp";
    63         }
    64         return language;
    65       }
    66
    67      private Language dynamicLanguageImplementationLoad(String language) throws InstantiationException, IllegalAccessException
    68      {
    69          try {
    70              return (Language) this.getClass().getClassLoader().loadClass(
    71                  PACKAGE + language + SUFFIX).newInstance();
    72          } catch (ClassNotFoundException e) {
    73              // No class found, returning default implementation
    74              // FIXME: There should be somekind of log of this
    75              return null;
    76          } catch (NoClassDefFoundError e) {
    77              // Windows is case insensitive, so it may find the file, even though
    78              // the name has a different case. Since Java is case sensitive, it
    79              // will not accept the classname inside the file that was found and
    80              // will throw a NoClassDefFoundError
    81              return null;
    82          }
    83      }
    84
    85     /*
    86      * This method does simply this:
    87      * ruby -> Ruby
    88      * fortran -> Fortran
    89      * ...
    90      */
    91     private String languageConventionSyntax(String language) {
    92         return Character.toUpperCase(language.charAt(0)) + language.substring(1, language.length()).toLowerCase();
    93      }
    94  }