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.io.File;
7   import java.io.FileNotFoundException;
8   import java.io.IOException;
9   import java.util.ArrayList;
10  import java.util.HashSet;
11  import java.util.Iterator;
12  import java.util.List;
13  import java.util.Map;
14  import java.util.Set;
15  import java.util.TreeMap;
16  import java.util.logging.Level;
17  import java.util.logging.Logger;
18  
19  import net.sourceforge.pmd.lang.ast.TokenMgrError;
20  import net.sourceforge.pmd.util.FileFinder;
21  import net.sourceforge.pmd.util.database.DBMSMetadata;
22  import net.sourceforge.pmd.util.database.DBURI;
23  import net.sourceforge.pmd.util.database.SourceObject;
24  
25  import org.apache.commons.io.FilenameUtils;
26  
27  public class CPD {
28      private final static Logger LOGGER = Logger.getLogger(CPD.class.getName()); 
29  
30  	private CPDConfiguration configuration;
31  
32  	private Map<String, SourceCode> source = new TreeMap<String, SourceCode>();
33      private CPDListener listener = new CPDNullListener();
34      private Tokens tokens = new Tokens();
35      private MatchAlgorithm matchAlgorithm;
36  
37      public CPD(CPDConfiguration theConfiguration) {
38      	configuration = theConfiguration;
39          // before we start any tokenizing (add(File...)), we need to reset the static TokenEntry status
40          TokenEntry.clearImages();
41      }
42  
43      public void setCpdListener(CPDListener cpdListener) {
44          this.listener = cpdListener;
45      }
46  
47      public void go() {
48          matchAlgorithm = new MatchAlgorithm(source, tokens,configuration.getMinimumTileSize(),listener);
49          matchAlgorithm.findMatches();
50      }
51  
52      public Iterator<Match> getMatches() {
53          return matchAlgorithm.matches();
54      }
55  
56      public void addAllInDirectory(String dir) throws IOException {
57          addDirectory(dir, false);
58      }
59  
60      public void addRecursively(String dir) throws IOException {
61          addDirectory(dir, true);
62      }
63  
64      public void add(List<File> files) throws IOException {
65          for (File f: files) {
66              add(f);
67          }
68      }
69  
70      private void addDirectory(String dir, boolean recurse) throws IOException {
71          if (!(new File(dir)).exists()) {
72              throw new FileNotFoundException("Couldn't find directory " + dir);
73          }
74          FileFinder finder = new FileFinder();
75          // TODO - could use SourceFileSelector here
76          add(finder.findFilesFrom(dir, configuration.filenameFilter(), recurse));
77      }
78  
79      private Set<String> current = new HashSet<String>();
80  
81      public void add(File file) throws IOException {
82  
83          if (configuration.isSkipDuplicates()) {
84              // TODO refactor this thing into a separate class
85              String signature = file.getName() + '_' + file.length();
86              if (current.contains(signature)) {
87                  System.err.println("Skipping " + file.getAbsolutePath() + " since it appears to be a duplicate file and --skip-duplicate-files is set");
88                  return;
89              }
90              current.add(signature);
91          }
92  
93          if (!FilenameUtils.equalsNormalizedOnSystem(file.getAbsoluteFile().getCanonicalPath(), file.getAbsolutePath())) {
94              System.err.println("Skipping " + file + " since it appears to be a symlink");
95              return;
96          }
97  
98          if (!file.exists()) {
99              System.err.println("Skipping " + file + " since it doesn't exist (broken symlink?)");
100             return;
101         }
102 
103         SourceCode sourceCode = configuration.sourceCodeFor(file);
104         add(sourceCode);
105     }
106 
107     public void add(DBURI dburi) throws IOException {
108 
109       try 
110       {
111         DBMSMetadata dbmsmetadata = new DBMSMetadata(dburi) ; 
112 
113         List<SourceObject> sourceObjectList = dbmsmetadata.getSourceObjectList ();
114         LOGGER.log(Level.FINER, "Located {0} database source objects", sourceObjectList.size());
115 
116         for (SourceObject sourceObject: sourceObjectList )
117         {
118           // Add DBURI as a faux-file 
119           String falseFilePath =  sourceObject.getPseudoFileName();
120           LOGGER.log(Level.FINEST, "Adding database source object {0}", falseFilePath);
121 
122           SourceCode sourceCode = configuration.sourceCodeFor( dbmsmetadata.getSourceCode(sourceObject) 
123                                                                ,falseFilePath
124                                                              );
125           add(sourceCode);
126         }
127       }
128       catch (Exception sqlException)
129       {
130         LOGGER.log(Level.SEVERE, "Problem with Input URI", sqlException);
131         throw new RuntimeException("Problem with DBURI: "+dburi , sqlException ) ; 
132       }
133     }
134 
135     private void add(SourceCode sourceCode) throws IOException {
136         if (configuration.isSkipLexicalErrors()) {
137             addAndSkipLexicalErrors(sourceCode);
138         } else {
139             addAndThrowLexicalError(sourceCode);
140         }
141     }
142 
143     private void addAndThrowLexicalError(SourceCode sourceCode) throws IOException {
144         configuration.tokenizer().tokenize(sourceCode, tokens);
145         listener.addedFile(1,  new File(sourceCode.getFileName()));
146         source.put(sourceCode.getFileName(), sourceCode);
147     }
148 
149     private void addAndSkipLexicalErrors(SourceCode sourceCode) throws IOException {
150         TokenEntry.State savedTokenEntry = new TokenEntry.State(tokens.getTokens());
151         try {
152             addAndThrowLexicalError(sourceCode);
153         } catch (TokenMgrError e) {
154             System.err.println("Skipping " + e.getMessage());
155             tokens.getTokens().clear();
156             tokens.getTokens().addAll(savedTokenEntry.restore());
157         }
158     }
159 
160     /**
161      * List names/paths of each source to be processed.
162      * 
163      * @return names of sources to be processed 
164      */
165     public List<String> getSourcePaths() {
166         return new ArrayList<String>(source.keySet());  
167     }
168 
169     /**
170      * Get each Source to be processed.
171      * 
172      * @return all Sources to be processed 
173      */
174     public List<SourceCode> getSources() {
175         return new ArrayList<SourceCode>(source.values());  
176     }
177     
178     
179 	public static void main(String[] args) {
180 		CPDCommandLineInterface.main(args);
181 	}
182 }