/**
   * This method generates the time series for the methods and puts it in an HTML string. The
   * methods are sorted according to the greatest average time for each method.
   *
   * @param dataBase the database to aggregate the statistics for
   * @return the html representing the series of timings for the method
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  protected String methodSeries(final IDataBase dataBase) {
    Comparator<Method> comparator =
        new Comparator<Method>() {
          public int compare(Method o1, Method o2) {
            Double o1Average = calculator.averageMethodTime(o1);
            Double o2Average = calculator.averageMethodTime(o2);
            // We want a descending table, i.e. the most expensive at the top
            return o2Average.compareTo(o1Average);
          }
        };
    Set<Method> sortedMethods = new TreeSet<>(comparator);
    List<Method> methods = dataBase.find(Method.class);
    sortedMethods.addAll(methods);

    List<Snapshot<?, ?>> snapshots =
        methods.size() > 0 ? methods.get(0).getSnapshots() : new ArrayList<Snapshot<?, ?>>();
    Element tableElement = tableElement(snapshots);

    for (Method method : sortedMethods) {
      Class<?, ?> klass = (Class<?, ?>) method.getParent();
      String className = klass.getName();
      String methodName = method.getName();

      Element rowElement = addElement(tableElement, "tr", null);
      addElement(rowElement, "td", className);
      addElement(rowElement, "td", methodName);
      addElement(rowElement, "td", Double.toString(calculator.averageMethodTime(method)));
      addElement(rowElement, "td", Double.toString(calculator.averageMethodNetTime(method)));
      addElement(rowElement, "td", Double.toString(calculator.totalMethodTime(method)));
      addElement(rowElement, "td", Double.toString(calculator.totalNetMethodTime(method)));
      addElement(rowElement, "td", Integer.toString(method.getInvocations()));

      Element dataElement = addElement(rowElement, "td", null);
      Element imageElement = addElement(dataElement, "img", null);
      // Add the method series graph for the average and total time for the method
      List<Double> methodSeries = calculator.methodSeries(method);
      String url = buildGraph(IConstants.METHOD_SERIES, method, methodSeries);
      addAttributes(imageElement, new String[] {"src"}, new String[] {url});

      dataElement = addElement(rowElement, "td", null);
      imageElement = addElement(dataElement, "img", null);
      // Add the method change graph, i.e. the change in the average time for the method
      List<Double> methodChangeSeries = calculator.methodChangeSeries(method);
      url = buildGraph(IConstants.METHOD_CHANGE_SERIES, method, methodChangeSeries);
      addAttributes(imageElement, new String[] {"src"}, new String[] {url});
    }

    Document document = tableElement.getDocument();
    return prettyPrint(document);
  }
  protected String buildGraph(String seriesDirectory, Method<?, ?> method, List<Double> datas) {
    XYSeries series = new XYSeries("XYGraph", false, false);

    double snapshot = 0;
    for (Double data : datas) {
      double seconds = TimeUnit.NANOSECONDS.toSeconds(data.intValue());
      series.add(snapshot++, seconds);
    }

    XYSeriesCollection seriesCollection = new XYSeriesCollection();
    seriesCollection.addSeries(series);
    JFreeChart chart =
        ChartFactory.createXYLineChart(
            null,
            "Snapshots",
            "Time",
            seriesCollection,
            PlotOrientation.VERTICAL,
            false,
            false,
            false);
    chart.setTitle(new TextTitle(method.getName(), new Font("Arial", Font.BOLD, 11)));

    XYPlot xyPlot = chart.getXYPlot();
    NumberAxis yAxis = (NumberAxis) xyPlot.getRangeAxis();
    yAxis.setAutoRange(true);
    yAxis.setAutoRangeIncludesZero(true);

    NumberAxis xAxis = (NumberAxis) xyPlot.getDomainAxis();
    xAxis.setAutoRange(true);
    xAxis.setAutoRangeIncludesZero(true);
    // xAxis.setTickUnit(new NumberTickUnit(1));

    StringBuilder builder = new StringBuilder(method.getClassName());
    builder.append(method.getName());
    builder.append(method.getDescription());

    String fileName = Long.toString(Toolkit.hash(builder.toString()));
    fileName += ".jpeg";

    File chartSeriesDirectory = new File(IConstants.chartDirectory, seriesDirectory);
    File chartFile = new File(chartSeriesDirectory, fileName);
    try {
      if (!IConstants.chartDirectory.exists()) {
        //noinspection ResultOfMethodCallIgnored
        IConstants.chartDirectory.mkdirs();
      }
      if (!chartSeriesDirectory.exists()) {
        //noinspection ResultOfMethodCallIgnored
        chartSeriesDirectory.mkdirs();
      }
      if (!chartFile.exists()) {
        //noinspection ResultOfMethodCallIgnored
        chartFile.createNewFile();
      }
      ChartUtilities.saveChartAsJPEG(chartFile, chart, 450, 150);
      builder = new StringBuilder(IConstants.CHARTS);
      builder.append(File.separatorChar);
      builder.append(seriesDirectory);
      builder.append(File.separatorChar);
      builder.append(fileName);

      return builder.toString();
    } catch (Exception e) {
      logger.error("Exception generating the graph", e);
    }
    return null;
  }
 @SuppressWarnings("unchecked")
 public static synchronized void copyDataBase(IDataBase sourceDataBase, IDataBase targetDataBase) {
   Collector.initialize(targetDataBase);
   List<Package> sourcePackages = sourceDataBase.find(Package.class);
   for (Package sourcePackage : sourcePackages) {
     List<Class> sourceClasses = sourcePackage.getChildren();
     for (Class sourceClass : sourceClasses) {
       Collector.collectAccess(sourceClass.getName(), sourceClass.getAccess());
       collectEfferentAndAfferent(sourceClass, sourcePackages);
       List<Class> sourceInnerClasses = sourceClass.getInnerClasses();
       for (Class sourceInnerClass : sourceInnerClasses) {
         Collector.collectInnerClass(sourceInnerClass.getName(), sourceClass.getName());
         Method sourceOuterMethod = sourceClass.getOuterMethod();
         if (sourceOuterMethod != null) {
           Collector.collectOuterClass(
               sourceInnerClass.getName(),
               sourceClass.getName(),
               sourceOuterMethod.getName(),
               sourceOuterMethod.getDescription());
         }
       }
       // Collector.collectSource(sourceClass.getName(), "source");
       List<Method> sourceMethods = sourceClass.getChildren();
       for (Method sourceMethod : sourceMethods) {
         Collector.collectComplexity(
             sourceClass.getName(),
             sourceMethod.getName(),
             sourceMethod.getDescription(),
             sourceMethod.getComplexity());
         Collector.collectAccess(
             sourceClass.getName(),
             sourceMethod.getName(),
             sourceMethod.getDescription(),
             sourceMethod.getAccess());
         List<Line> sourceLines = sourceMethod.getChildren();
         for (Line sourceLine : sourceLines) {
           Collector.collectLine(
               sourceClass.getName(),
               sourceMethod.getName(),
               sourceMethod.getDescription(),
               Integer.valueOf((int) sourceLine.getNumber()));
           for (int i = 0; i < sourceLine.getCounter(); i++) {
             Collector.collectCoverage(
                 sourceClass.getName(),
                 sourceMethod.getName(),
                 sourceMethod.getDescription(),
                 (int) sourceLine.getNumber());
           }
         }
       }
     }
   }
 }
 /**
  * Dumps the database to the output stream.
  *
  * @param dataBase the database to dump
  * @param criteria the criteria to match if the data for the composite must be written to the
  *     output
  */
 @SuppressWarnings("unchecked")
 public static synchronized void dump(IDataBase dataBase, ICriteria criteria, String message) {
   if (message != null) {
     logger.warn(message);
   }
   try {
     Object object = dataBase.find(Project.class, Toolkit.hash(Project.class.getName()));
     logger.info(object.toString());
     Project<?, ?> project =
         (Project<?, ?>) dataBase.find(Project.class, Toolkit.hash(Project.class.getName()));
     if (project != null) {
       logger.warn("Project : " + project.getName());
     }
   } catch (Exception e) {
     logger.error("Exception dumping the data for the project object.", e);
   }
   try {
     List<Package> packages = dataBase.find(Package.class);
     for (Package<?, ?> pakkage : packages) {
       log(
           criteria,
           pakkage,
           1,
           pakkage.getId()
               + " : "
               + pakkage.getName()
               + ", coverage : "
               + pakkage.getCoverage()
               + ", complexity : "
               + pakkage.getComplexity()
               + ", stability : "
               + pakkage.getStability());
       for (Class<?, ?> klass : ((List<Class<?, ?>>) pakkage.getChildren())) {
         log(
             criteria,
             klass,
             2,
             " : id : "
                 + klass.getId()
                 + " : name : "
                 + klass.getName()
                 + " : coverage : "
                 + klass.getCoverage()
                 + ", complexity : "
                 + klass.getComplexity()
                 + ", outer class : "
                 + klass.getOuterClass()
                 + ", outer method : "
                 + klass.getOuterMethod()
                 + ", lines : "
                 + klass.getChildren().size()
                 + ", inner classes : "
                 + klass.getInnerClasses());
         List<Efferent> efferents = klass.getEfferent();
         List<Afferent> afferents = klass.getAfferent();
         for (Efferent efferent : efferents) {
           log(criteria, efferent, 4, efferent.getName());
         }
         for (Afferent afferent : afferents) {
           log(criteria, afferent, 4, afferent.getName());
         }
         for (Method<?, ?> method : ((List<Method<?, ?>>) klass.getChildren())) {
           log(
               criteria,
               method,
               3,
               method.getId()
                   + " : name : "
                   + method.getName()
                   + " : description : "
                   + method.getDescription()
                   + " : coverage : "
                   + method.getCoverage()
                   + ", complexity : "
                   + method.getComplexity()
                   + ", start time : "
                   + method.getStartTime()
                   + ", end time : "
                   + method.getEndTime());
           for (Line<?, ?> line : ((List<Line<?, ?>>) method.getChildren())) {
             log(
                 criteria,
                 line,
                 4,
                 line.getId()
                     + " : number : "
                     + line.getNumber()
                     + ", counter : "
                     + line.getCounter());
           }
         }
       }
     }
   } catch (Exception e) {
     logger.error("Exception dumping the data for the database.", e);
   }
 }