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", "plsql" };
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  }