View Javadoc
1 /*** 2 * Analyzer.java 3 * 4 * Project: Dependency Tool 5 * 6 * WHEN WHO WHAT 7 * 06.06.2003 pko initial public release 8 * 22.01.2002 ctr modification 9 * 08.01.2002 ctr creation 10 * 11 * Copyright 2003 ELCA Informatique SA 12 * Av. de la Harpe 22-24, 1000 Lausanne 13, Switzerland 13 * www.elca.ch 14 * 15 * This library is free software; you can redistribute it and/or 16 * modify it under the terms of the GNU Lesser General Public License 17 * as published by the Free Software Foundation; either version 2.1 of 18 * the License, or (at your option) any later version. 19 * 20 * This library is distributed in the hope that it will be useful, but 21 * WITHOUT ANY WARRANTY; without even the implied warranty of 22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 * Lesser General Public License for more details. 24 * 25 * You should have received a copy of the GNU Lesser General Public 26 * License along with this library; if not, write to the Free Software 27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 28 * USA 29 */ 30 31 package ch.elca.dependency.core; 32 33 import org.apache.log4j.Logger; 34 import org.apache.regexp.RE; 35 import org.apache.regexp.RESyntaxException; 36 37 import java.io.*; 38 import java.util.*; 39 40 import ch.elca.dependency.core.classinfo.ClassInfo; 41 import ch.elca.dependency.core.classinfo.Reader; 42 import ch.elca.dependency.exception.AnalyseException; 43 import ch.elca.dependency.exception.ClassInfoException; 44 import ch.elca.dependency.exception.InvalidClassFileException; 45 import ch.elca.dependency.util.IOManager; 46 import ch.elca.dependency.util.UnZip; 47 48 /*** 49 * This class is the main responsible one for parsing the whole class structure 50 * in the directory tree. It uses the <code>Reader</code> class for reading the 51 * class files contained by the class structure. 52 * Every file in the directory structure is parsed recursively if it is a java 53 * class file. This is determined by looking at the file extension of the 54 * corresponding file. If a file has no file extension the type cannot be 55 * determined and so it will not be parsed as a java class file. 56 * Because sometimes in a large project there are many files with partially no 57 * file extension, there are two possibilities for handling this case.<p> 58 * 59 * The files with no file extension can be skipped and are not included in the 60 * analyse. Or the whole analyse can be canceled with a corresponding message 61 * to the user. 62 * This behaviour is specified by the third argument (boolean) in 63 * the constructor of this class. If the argument is true, file extensions are 64 * required and if there is a file without any, the application is stopped. If 65 * this argument is false all the files with no file extension are just skipped 66 * and the analyse is performed whithout these files. 67 * 68 * @see ch.elca.dependency.core.classinfo.Reader 69 * @see ch.elca.dependency.core.RawModel 70 * @see ch.elca.dependency.core.Filter 71 * 72 * @author Christoph Trutmann 73 * @version 1.0-beta 74 */ 75 public class Analyzer { 76 77 /*** 78 * Log4j Logger 79 */ 80 private static final Logger LOG = Logger.getLogger(Analyzer.class); 81 82 /*** 83 * To read the directory structure 84 */ 85 private Reader m_reader; 86 87 /*** 88 * <code>File</code> that points on the root of the directory 89 * structure with the java .class files. 90 */ 91 private File m_root; 92 93 /*** 94 * Load filter that specifies which packages are excluded at the 95 * beginning. 96 */ 97 private Filter m_filter; 98 99 /*** 100 * <code>HashMap</code> containing all the dependencies got from 101 * the ananlyse. 102 */ 103 private HashMap m_dependencies = new HashMap(); 104 105 /*** 106 * HashMap containing all the <code>Classinfo</code> objects of 107 * the analyzed project. They have all passed the load filter. The 108 * key of the map is the package name (String). 109 */ 110 private HashMap m_classInfos = new HashMap(); 111 112 /*** 113 * Relations classes / packages of all the artifacts of the project. 114 */ 115 private HashMap m_projClassPackageRel = new HashMap(); 116 117 /*** 118 * Relations classes / packages of all the artifacts which were analyzed. 119 */ 120 private HashMap m_analyzedClassPackageRel = new HashMap(); 121 122 /*** 123 * Statistic object containing all the statistical data gained from the 124 * analyse. 125 */ 126 private Statistic m_statistic; 127 128 /*** 129 * Variables for holding the statistical values during the analyse. 130 */ 131 private int m_projClass, m_projPackage, m_analyzedClass, m_usesDep = 0; 132 133 /*** 134 * This flag specifies whether the analyse was performed at least one time. 135 */ 136 private boolean m_analysed = false; 137 138 /*** 139 * Contains all the jars which were unpacked and analyzed. 140 */ 141 private ArrayList m_analysedJars = new ArrayList(); 142 143 /*** 144 * Helper class for unpacking jar and zip files. 145 */ 146 private UnZip m_unzipper = new UnZip(); 147 148 /*** 149 * Creates a new <code>Analyzer</code> instance. 150 * 151 * @param root a <code>File</code> value 152 * @param filter a <code>Filter</code> value 153 * @exception AnalyseException if an error occurs 154 */ 155 public Analyzer(File root, Filter filter) throws AnalyseException { 156 157 if( root == null ) { 158 throw new AnalyseException("There is no root specified."); 159 } 160 m_root = root; 161 162 try { 163 LOG.info("Root File to be analyzed: " + m_root.getCanonicalPath().toString()); 164 } catch (Exception e) { 165 ; 166 } 167 168 169 if ( filter == null) { 170 throw new AnalyseException("There is no filter specified."); 171 } 172 m_filter = filter; 173 174 LOG.debug("Filtering with filter: " + m_filter.getValue()); 175 176 m_reader = new Reader(); 177 m_statistic = new Statistic(); 178 } 179 180 /*** 181 * Performs the analyse on the specified directory root in the constructor. 182 */ 183 public void analyse() throws AnalyseException { 184 m_analysed = true; // shows that the analyse is performed 185 186 try{ 187 analyze(m_root); 188 } catch ( AnalyseException ae ) { 189 throw ae; 190 } catch ( Exception e ) { 191 e.printStackTrace(); 192 throw new AnalyseException(e); 193 } 194 } 195 196 /*** 197 * Gets the dependencies encountered by the analyse. 198 * 199 * @return A <code>HashTable</code> containing all the packages 200 * dependencies. 201 * @thorws AnalyseException If the 'analyse()' method was not called 202 * before. 203 */ 204 public HashMap getDependencies() throws AnalyseException { 205 if ( ! m_analysed ) { 206 throw new AnalyseException("The 'analyse()' method must be called before!"); 207 } 208 if ( m_dependencies.isEmpty() ) { 209 throw new AnalyseException("There is no .class file in the spec. directory structure.\n" 210 + "Specify the right \"root\" as argument."); 211 } else { 212 return m_dependencies; 213 } 214 } 215 216 217 /*** 218 * Gets all the <code>ClassInfo</code> objects which have passed the load 219 * filter. 220 * 221 * @return List containing all the <code>ClassInfo</code> objects of this 222 * project which have passed the load filter. 223 */ 224 public HashMap getClassInfos() { 225 return m_classInfos; 226 } 227 228 229 /*** 230 * Gets the Statistic object containing all the statistical data gained from 231 * the analyse. 232 * 233 * @return Statistic object contining all the statistcal data gained from 234 * the analyse. 235 * @exception AnalyseException If the 'analyse()' method was not called 236 * before. 237 */ 238 public Statistic getStatistics() throws AnalyseException { 239 if ( ! m_analysed ) { 240 throw new AnalyseException( 241 "The 'analyse()' method must be called before!"); 242 } 243 evaluateClassPackageRelations(); 244 return m_statistic; 245 } 246 247 248 /*** 249 * Helpermethod for performing the analyse of the directory structure. 250 */ 251 private void analyze(File file) 252 throws FileNotFoundException, IOException, InvalidClassFileException, 253 RESyntaxException, ClassInfoException, AnalyseException { 254 255 // it is a directory 256 // 257 if ( file.isDirectory() ) { 258 File[] fileArr = file.listFiles(); 259 int elems = fileArr.length, i=0; 260 boolean firstFileInDir = true, skipDir = false; 261 String fileName = file.getName(); 262 263 // if there is an extracted jar yet 264 // 265 if (file.equals(IOManager.getTempDir())) { 266 267 // directory with the extracted content is included yet 268 // 269 if ( m_analysedJars.contains(fileName) ) { 270 skipDir = true; 271 } else { 272 273 // mark the directory with the extracted content 274 // 275 m_analysedJars.add(fileName); 276 } 277 278 // mark the jar already extracted 279 // 280 // m_analysedJars.add(fileName.substring(10, fileName.length())); 281 } 282 283 // recursive calls for each abstract file in the current directory 284 // 285 while ( (elems > 0 && i<elems) && ! skipDir) { 286 287 // inquire whether it is a package 288 // 289 if ( isClassFile(fileArr[i]) && firstFileInDir ) { 290 m_projPackage++; 291 firstFileInDir = false; 292 } 293 analyze(fileArr[i]); 294 i++; 295 } 296 } 297 298 // it is a file 299 // 300 else { 301 302 // test if the file is a class file 303 304 // 305 // do not create so many readers 306 // one should be enough 307 // 308 309 if ( isClassFile(file) ) { 310 m_projClass++; // increment total class counter 311 FileInputStream fi = new FileInputStream(file); 312 Reader reader = new Reader(); 313 314 if ( ! reader.read(fi) ) { 315 LOG.error("Unable to read class file."); 316 System.exit(0); 317 } 318 319 // add the package to the hash table 320 registerClass(reader.getClassInfo()); 321 // test if the file is a jar file 322 // 323 } else if ( isJarFile(file) && 324 ! m_analysedJars.contains(file.getName()) ) { 325 326 // mark it as an analyzed jar file 327 // 328 m_analysedJars.add(file.getName()); 329 330 // unzip the jar 331 // 332 File out = IOManager.getTempDir(); 333 334 m_unzipper.extractFiles(file, out); 335 336 // recusive analyse the extracted files 337 // 338 analyze(out); 339 } 340 } 341 } 342 343 /*** 344 * Helper method for testing whether the specified abstract file is a class 345 * file. 346 * 347 * @param file Abstract file object pointing to the location where the 348 * physical location of the file to inqure is. 349 * @return True if it is a java .class file.<br> 350 * If the file has no file extension, false is returned. 351 */ 352 private boolean isClassFile(File file){ 353 String fileName = file.getName(); 354 int indexOfPoint = fileName.lastIndexOf("."); 355 356 // no file extension 357 if ( indexOfPoint == -1 ) { 358 return false; 359 } 360 361 String fileExt = fileName.substring(indexOfPoint); 362 if ( fileExt.equals( ".class" ) ) { 363 return true; 364 } 365 return false; 366 } 367 368 369 /*** 370 * Helper method for testing whether the specified abstract file is jar 371 * file. 372 * 373 * @param file Abstract file object pointing to the location where the 374 * physical location of the file to inqure is. 375 * @return True if it is a jar file.<br> 376 * If the file has no file extension, false is returned. 377 */ 378 private boolean isJarFile(File file) { 379 String fileName = file.getName(); 380 int indexOfPoint = fileName.lastIndexOf("."); 381 382 // no file extension 383 if ( indexOfPoint == -1 ) { 384 return false; 385 } 386 387 String fileExt = fileName.substring(indexOfPoint); 388 if ( fileExt.equals( ".jar" ) ) { 389 return true; 390 } 391 return false; 392 } 393 394 395 /*** 396 * Appends the package information to the hash maps.<br> 397 * On one side the informations for the graph construction on the other side 398 * the informations for the statistics. 399 * 400 * @param info <code>ClassInfo</code> object storing the dependency 401 * information of a .class file. 402 * @throws RESyntaxException If there is a problem with the regular expression 403 * used to determine the excluded packages. 404 * @throws ClassInfoException If the class informations are invalid. 405 * 406 * @tbd improve this method 407 */ 408 private void registerClass(ClassInfo info) 409 throws RESyntaxException, ClassInfoException { 410 411 ArrayList packages; // list of Strings 412 ArrayList classInfos; // list of ClassInfo objects 413 414 // get the regular expression with the load filter 415 // 416 RE regExp = new RE(m_filter.getValue()); 417 418 String packageName = info.getPackage(); 419 420 // register all the relations of the project 421 // use hashtable 422 // Integer classNum = null; 423 // if ( (classNum = (Integer)m_projClassPackageRel.get(packageName)) != null ) { 424 // 425 // 426 <b>if ( m_projClassPackageRel.containsKey(packageName) ) { 427 int numberOfClasses = ((Integer)m_projClassPackageRel 428 .get(packageName)).intValue(); 429 430 numberOfClasses++; 431 432 m_projClassPackageRel.put(packageName, 433 new Integer(numberOfClasses)); 434 // the first occurance of this package 435 // 436 } else { 437 m_projClassPackageRel.put(packageName, new Integer(1)); 438 } 439 440 // apply the load filter 441 // 442 <b>if ( regExp.match(packageName)) { 443 return; 444 } 445 m_analyzedClass++; // increments the analyzed class counter 446 447 // register all the relations of the analyse 448 <b>if ( m_analyzedClassPackageRel.containsKey(packageName) ) { 449 int numberOfClasses = ((Integer)m_analyzedClassPackageRel 450 .get(packageName)).intValue(); 451 452 numberOfClasses++; 453 454 m_analyzedClassPackageRel.put(packageName, 455 new Integer(numberOfClasses)); 456 // the first occurance of this package 457 // 458 } else { 459 m_analyzedClassPackageRel.put(packageName, new Integer(1)); 460 } 461 462 // gets the value (list) if it exists otherwise creates it 463 // 464 // use ternary operator here 465 // a = (map.get(key) != null ? map.get(key) : new ArrayList()); 466 // 467 <b>if ( m_dependencies.containsKey(packageName) ) { 468 packages = (ArrayList)m_dependencies.get(packageName); 469 classInfos = (ArrayList)m_classInfos.get(packageName); 470 } else { 471 packages = new ArrayList(); 472 classInfos = new ArrayList(); 473 } 474 475 ArrayList list = info.getNeededPackages(); 476 ListIterator iterator = list.listIterator(); 477 478 while ( iterator.hasNext() ) { 479 String tmp = (String)iterator.next(); 480 481 if ( ! packages.contains(tmp) && !regExp.match(tmp) ) { 482 packages.add(tmp); 483 484 if ( ! packageName.equals(tmp) ) { 485 m_usesDep++; // dont count reflexive dependencies 486 } 487 } 488 } 489 490 // add the package to the hash map 491 // 492 m_dependencies.put(packageName, packages); 493 494 // add the ClassInfo to the hash map 495 // 496 classInfos.add(info); 497 m_classInfos.put(packageName, classInfos); 498 } 499 500 /*** 501 * Helper method for evaluating the biggest package of the project and the 502 * biggest package which passed the load filter. 503 */ 504 private void evaluateClassPackageRelations(){ 505 506 // evaluate the biggest package which passed the load filter 507 Set keySet = m_analyzedClassPackageRel.keySet(); 508 Iterator keyIter = keySet.iterator(); 509 int maxVal = 0, totalVal = 0; 510 String keyOfMaxVal = ""; 511 512 // get the biggest package (max # classes) 513 while ( keyIter.hasNext() ) { 514 String keyTmp = (String)keyIter.next(); 515 516 int valTmp = ((Integer)m_analyzedClassPackageRel 517 .get(keyTmp)).intValue(); 518 totalVal += valTmp; 519 520 if ( valTmp > maxVal ) { 521 maxVal = valTmp; 522 keyOfMaxVal = keyTmp; 523 } else if ( valTmp == maxVal ) { 524 keyOfMaxVal.concat(", " + keyTmp); 525 } 526 } 527 m_statistic.setBiggestAnalyzedPackage(keyOfMaxVal); 528 529 // evaluate the biggest package of the project 530 // 531 maxVal = 0; 532 totalVal = 0; 533 keyOfMaxVal = ""; 534 keySet = m_projClassPackageRel.keySet(); 535 keyIter = keySet.iterator(); 536 537 // get the biggest package (max # classes) 538 while ( keyIter.hasNext() ) { 539 String keyTmp = (String)keyIter.next(); 540 541 int valTmp = ((Integer)m_projClassPackageRel 542 .get(keyTmp)).intValue(); 543 totalVal += valTmp; 544 545 if ( valTmp > maxVal ) { 546 maxVal = valTmp; 547 keyOfMaxVal = keyTmp; 548 } 549 else if ( valTmp == maxVal ) { 550 keyOfMaxVal.concat(", " + keyTmp); 551 } 552 } 553 m_statistic.setBiggestProjPackage(keyOfMaxVal); 554 555 // set up the rest of the statistic object 556 m_statistic.setAnalyzedClasses(m_analyzedClass); 557 m_statistic.setProjClasses(m_projClass); 558 m_statistic.setAnalyzedPackages(m_dependencies.keySet().size()); 559 m_statistic.setProjPackages(m_projPackage); 560 m_statistic.setNumberOfUsesDep(m_usesDep); 561 } 562 563 /*** 564 * Get the project root file. 565 * 566 * @return a <code>File</code> value 567 */ 568 public File getProjectRoot() { 569 return m_root; 570 } 571 }

This page was automatically generated by Maven