diff --git a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/ComputationalLaboratory.java b/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/ComputationalLaboratory.java deleted file mode 100644 index c1a46be9..00000000 --- a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/toolbox/ComputationalLaboratory.java +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.jsprit.analysis.toolbox; - -import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithm; -import com.graphhopper.jsprit.core.algorithm.VehicleRoutingAlgorithmFactory; -import com.graphhopper.jsprit.core.problem.VehicleRoutingProblem; -import com.graphhopper.jsprit.core.problem.solution.VehicleRoutingProblemSolution; -import com.graphhopper.jsprit.core.util.BenchmarkInstance; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - - -public class ComputationalLaboratory { - - public static interface LabListener { - - } - - /** - * Listener-interface to listen to calculation. - *

- *

Note that calculations are run concurrently, i.e. a unique task that is distributed to an available thread is - * {algorithm, instance, run}. - * - * @author schroeder - */ - public static interface CalculationListener extends LabListener { - - public void calculationStarts(final BenchmarkInstance p, final String algorithmName, final VehicleRoutingAlgorithm algorithm, final int run); - - public void calculationEnds(final BenchmarkInstance p, final String algorithmName, final VehicleRoutingAlgorithm algorithm, final int run, final Collection solutions); - - } - - public static interface LabStartsAndEndsListener extends LabListener { - - public void labStarts(List instances, int noAlgorithms, int runs); - - public void labEnds(); - } - - /** - * Collects whatever indicators you require by algorithmName, instanceName, run and indicator. - * - * @author schroeder - */ - public static class DataCollector { - - public static class Key { - private String instanceName; - private String algorithmName; - private int run; - private String indicatorName; - - public Key(String instanceName, String algorithmName, int run, String indicatorName) { - super(); - this.instanceName = instanceName; - this.algorithmName = algorithmName; - this.run = run; - this.indicatorName = indicatorName; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime - * result - + ((algorithmName == null) ? 0 : algorithmName - .hashCode()); - result = prime - * result - + ((indicatorName == null) ? 0 : indicatorName - .hashCode()); - result = prime - * result - + ((instanceName == null) ? 0 : instanceName.hashCode()); - result = prime * result + run; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Key other = (Key) obj; - if (algorithmName == null) { - if (other.algorithmName != null) - return false; - } else if (!algorithmName.equals(other.algorithmName)) - return false; - if (indicatorName == null) { - if (other.indicatorName != null) - return false; - } else if (!indicatorName.equals(other.indicatorName)) - return false; - if (instanceName == null) { - if (other.instanceName != null) - return false; - } else if (!instanceName.equals(other.instanceName)) - return false; - if (run != other.run) - return false; - return true; - } - - public String getInstanceName() { - return instanceName; - } - - public String getAlgorithmName() { - return algorithmName; - } - - public int getRun() { - return run; - } - - public String getIndicatorName() { - return indicatorName; - } - - @Override - public String toString() { - return "[algorithm=" + algorithmName + "][instance=" + instanceName + "][run=" + run + "][indicator=" + indicatorName + "]"; - } - - } - - private final static String SOLUTION_INDICATOR_NAME = "vehicle-routing-problem-solution"; - - private ConcurrentHashMap data = new ConcurrentHashMap(); - - private ConcurrentHashMap solutions = new ConcurrentHashMap(); - - /** - * Adds a single date by instanceName, algorithmName, run and indicatorName. - *

If there is already an entry for this instance, algorithm, run and indicatorName, it is overwritten. - * - * @param instanceName - * @param algorithmName - * @param run - * @param indicatorName - * @param value - */ - public void addDate(String instanceName, String algorithmName, int run, String indicatorName, double value) { - if (indicatorName.equals(SOLUTION_INDICATOR_NAME)) - throw new IllegalArgumentException(indicatorName + " is already used internally. please choose another indicator-name."); - Key key = new Key(instanceName, algorithmName, run, indicatorName); - data.put(key, value); - } - - public void addSolution(String instanceName, String algorithmName, int run, VehicleRoutingProblemSolution solution) { - Key key = new Key(instanceName, algorithmName, run, SOLUTION_INDICATOR_NAME); - solutions.put(key, solution); - } - - /** - * Returns a collections of indicator values representing the calculated values of individual runs. - * - * @param instanceName - * @param algorithmName - * @param indicator - * @return - */ - public Collection getData(String instanceName, String algorithmName, String indicator) { - List values = new ArrayList(); - for (Key key : data.keySet()) { - if (key.getAlgorithmName().equals(algorithmName) && key.getInstanceName().equals(instanceName) && key.getIndicatorName().equals(indicator)) { - values.add(data.get(key)); - } - } - return values; - } - - /** - * Returns indicator value. - * - * @param instanceName - * @param algorithmName - * @param run - * @param indicator - * @return - */ - public Double getDate(String instanceName, String algorithmName, int run, String indicator) { - return data.get(new Key(instanceName, algorithmName, run, indicator)); - } - - public VehicleRoutingProblemSolution getSolution(String instanceName, String algorithmName, int run) { - return solutions.get(new Key(instanceName, algorithmName, run, "solution")); - } - - /** - * Returns all keys that have been created. A key is a unique combination of algorithmName, instanceName, run and indicator. - * - * @return - */ - public Set getDataKeySet() { - return data.keySet(); - } - - public Set getSolutionKeySet() { - return solutions.keySet(); - } - - public VehicleRoutingProblemSolution getSolution(Key solutionKey) { - return solutions.get(solutionKey); - } - - public Collection getSolutions() { - return solutions.values(); - } - - /** - * Returns date associated to specified key. - * - * @param key - * @return - */ - public Double getData(Key key) { - return data.get(key); - } - - } - - - private static class Algorithm { - - private String name; - - private VehicleRoutingAlgorithmFactory factory; - - public Algorithm(String name, VehicleRoutingAlgorithmFactory factory) { - super(); - this.name = name; - this.factory = factory; - } - - } - - private List benchmarkInstances = new ArrayList(); - - private int runs = 1; - - private Collection listeners = new ArrayList(); - - private Collection startsAndEndslisteners = new ArrayList(); - - private List algorithms = new ArrayList(); - - private Set algorithmNames = new HashSet(); - - private Set instanceNames = new HashSet(); - - private int threads = 1; - - public ComputationalLaboratory() { - - } - - /** - * Adds algorithmFactory by name. - * - * @param name - * @param factory - * @throws IllegalStateException if there is already an algorithmFactory with the same name - */ - public void addAlgorithmFactory(String name, VehicleRoutingAlgorithmFactory factory) { - if (algorithmNames.contains(name)) - throw new IllegalStateException("there is already a algorithmFactory with the same name (algorithmName=" + name + "). unique names are required."); - algorithms.add(new Algorithm(name, factory)); - algorithmNames.add(name); - } - - public Collection getAlgorithmNames() { - return algorithmNames; - } - - public Collection getInstanceNames() { - return instanceNames; - } - - /** - * Adds instance by name. - * - * @param name - * @param problem - * @throws IllegalStateException if there is already an instance with the same name. - */ - public void addInstance(String name, VehicleRoutingProblem problem) { - if (benchmarkInstances.contains(name)) - throw new IllegalStateException("there is already an instance with the same name (instanceName=" + name + "). unique names are required."); - benchmarkInstances.add(new BenchmarkInstance(name, problem, null, null)); - instanceNames.add(name); - } - - /** - * Adds instance. - * - * @param instance the instance to be added - * @throws IllegalStateException if there is already an instance with the same name. - */ - public void addInstance(BenchmarkInstance instance) { - if (benchmarkInstances.contains(instance.name)) - throw new IllegalStateException("there is already an instance with the same name (instanceName=" + instance.name + "). unique names are required."); - benchmarkInstances.add(instance); - instanceNames.add(instance.name); - } - - /** - * Adds collection of instances. - * - * @param instances collection of instances to be added - * @throws IllegalStateException if there is already an instance with the same name. - */ - public void addAllInstances(Collection instances) { - for (BenchmarkInstance i : instances) { - addInstance(i); - } - } - - /** - * Adds instance by name, and with best known results. - * - * @param name - * @param problem - * @throws IllegalStateException if there is already an instance with the same name. - */ - public void addInstance(String name, VehicleRoutingProblem problem, Double bestKnownResult, Double bestKnownVehicles) { - addInstance(new BenchmarkInstance(name, problem, bestKnownResult, bestKnownVehicles)); - } - - /** - * Adds listener to listen computational experiments. - * - * @param listener - */ - public void addListener(LabListener listener) { - if (listener instanceof CalculationListener) { - listeners.add((CalculationListener) listener); - } - if (listener instanceof LabStartsAndEndsListener) { - startsAndEndslisteners.add((LabStartsAndEndsListener) listener); - } - } - - /** - * Sets nuOfRuns with same algorithm on same instance. - *

Default is 1 - * - * @param runs - */ - public void setNuOfRuns(int runs) { - this.runs = runs; - } - - /** - * Runs experiments. - *

- *

If nuThreads > 1 it runs them concurrently, i.e. individual runs are distributed to available threads. Therefore - * a unique task is defined by its algorithmName, instanceName and its runNumber. - *

If you have one algorithm called "myAlgorithm" and one instance called "myInstance", and you need to run "myAlgorithm" on "myInstance" three times - * with three threads then "myAlgorithm","myInstance",run1 runs on the first thread, "myAlgorithm", "myInstance", run2 on the second etc. - *

You can register whatever analysisTool you require by implementing and registering CalculationListener. Then your tool is informed just - * before a calculation starts as well as just after a calculation has been finished. - * - * @throws IllegalStateException if either no algorithm or no instance has been specified - * @see CalculationListener - */ - public void run() { - if (algorithms.isEmpty()) { - throw new IllegalStateException("no algorithm specified. at least one algorithm needs to be specified."); - } - if (benchmarkInstances.isEmpty()) { - throw new IllegalStateException("no instance specified. at least one instance needs to be specified."); - } - informStart(); - System.out.println("start benchmarking [nuAlgorithms=" + algorithms.size() + "][nuInstances=" + benchmarkInstances.size() + "][runsPerInstance=" + runs + "]"); - double startTime = System.currentTimeMillis(); - ExecutorService executor = Executors.newFixedThreadPool(threads); - for (final Algorithm algorithm : algorithms) { - for (final BenchmarkInstance p : benchmarkInstances) { - for (int run = 0; run < runs; run++) { - final int r = run; - try { - executor.submit(new Runnable() { - - @Override - public void run() { - runAlgorithm(p, algorithm, r + 1); - } - - }); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - } - try { - executor.shutdown(); - executor.awaitTermination(Long.MAX_VALUE, TimeUnit.MINUTES); - - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - System.out.println("benchmarking done [time=" + (System.currentTimeMillis() - startTime) / 1000 + "sec]"); - informEnd(); - } - - private void informEnd() { - for (LabStartsAndEndsListener l : startsAndEndslisteners) { - l.labEnds(); - } - } - - private void informStart() { - for (LabStartsAndEndsListener l : startsAndEndslisteners) { - l.labStarts(benchmarkInstances, algorithms.size(), runs); - } - } - - /** - * Sets number of threads. - *

By default: nuThreads = Runtime.getRuntime().availableProcessors()+1 - * - * @param threads - */ - public void setThreads(int threads) { - this.threads = threads; - } - - private void runAlgorithm(BenchmarkInstance p, Algorithm algorithm, int run) { - System.out.println("[algorithm=" + algorithm.name + "][instance=" + p.name + "][run=" + run + "][status=start]"); - VehicleRoutingAlgorithm vra = algorithm.factory.createAlgorithm(p.vrp); - informCalculationStarts(p, algorithm.name, vra, run); - Collection solutions = vra.searchSolutions(); - System.out.println("[algorithm=" + algorithm.name + "][instance=" + p.name + "][run=" + run + "][status=finished]"); - informCalculationsEnds(p, algorithm.name, vra, run, solutions); - } - - private void informCalculationStarts(BenchmarkInstance p, String name, VehicleRoutingAlgorithm vra, int run) { - for (CalculationListener l : listeners) l.calculationStarts(p, name, vra, run); - } - - private void informCalculationsEnds(BenchmarkInstance p, String name, VehicleRoutingAlgorithm vra, int run, - Collection solutions) { - for (CalculationListener l : listeners) l.calculationEnds(p, name, vra, run, solutions); - } - -} diff --git a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/BenchmarkWriter.java b/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/BenchmarkWriter.java deleted file mode 100644 index 7555f8cc..00000000 --- a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/BenchmarkWriter.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.jsprit.analysis.util; - -import com.graphhopper.jsprit.core.util.BenchmarkResult; - -import java.util.Collection; - -public interface BenchmarkWriter { - public void write(Collection results); -} diff --git a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/HtmlBenchmarkTableWriter.java b/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/HtmlBenchmarkTableWriter.java deleted file mode 100644 index b8520e7e..00000000 --- a/jsprit-analysis/src/main/java/com/graphhopper/jsprit/analysis/util/HtmlBenchmarkTableWriter.java +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Licensed to GraphHopper GmbH under one or more contributor - * license agreements. See the NOTICE file distributed with this work for - * additional information regarding copyright ownership. - * - * GraphHopper GmbH licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.graphhopper.jsprit.analysis.util; - -import com.graphhopper.jsprit.core.util.BenchmarkResult; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.Collection; - -public class HtmlBenchmarkTableWriter implements BenchmarkWriter { - - private String filename; - - public HtmlBenchmarkTableWriter(String filename) { - this.filename = filename; - } - - @Override - public void write(Collection results) { - - try { - BufferedWriter writer = new BufferedWriter(new FileWriter(new File(filename))); - writer.write(openTable() + newline()); - //table head - writer.write(openRow() + newline()); - writer.write(head("inst") + newline()); - writer.write(head("runs") + newline()); - writer.write(head("Ø time [sec]") + newline()); - writer.write(head("results", 4)); - writer.write(head("vehicles", 4)); - writer.write(head("res*") + newline()); - writer.write(head("veh*") + newline()); - writer.write(closeRow() + newline()); - - writer.write(openRow() + newline()); - writer.write(head("") + newline()); - writer.write(head("") + newline()); - writer.write(head("") + newline()); - writer.write(head("best") + newline()); - writer.write(head("avg") + newline()); - writer.write(head("worst") + newline()); - writer.write(head("stdev") + newline()); - writer.write(head("best") + newline()); - writer.write(head("avg") + newline()); - writer.write(head("worst") + newline()); - writer.write(head("stdev") + newline()); - writer.write(head("") + newline()); - writer.write(head("") + newline()); - writer.write(closeRow() + newline()); - - //data - double sum_avg_time = 0.0; - double sum_best_result = 0.0; - double sum_avg_result = 0.0; - double sum_worst_result = 0.0; - double sum_dev_result = 0.0; - - double sum_best_veh = 0.0; - double sum_avg_veh = 0.0; - double sum_worst_veh = 0.0; - double sum_dev_veh = 0.0; - - Integer runs = null; - Double sum_res_star = null; - Double sum_veh_star = null; - - for (BenchmarkResult result : results) { - if (runs == null) runs = result.runs; - writer.write(openRow() + newline()); - writer.write(date(result.instance.name) + newline()); - writer.write(date(Integer.valueOf(result.runs).toString()) + newline()); - - Double avg_time = round(result.getTimesStats().getMean(), 2); - writer.write(date(Double.valueOf(avg_time).toString()) + newline()); - //bestRes - Double best_result = round(result.getResultStats().getMin(), 2); - writer.write(date(Double.valueOf(best_result).toString()) + newline()); - //avgRes - Double avg_result = round(result.getResultStats().getMean(), 2); - writer.write(date(Double.valueOf(avg_result).toString()) + newline()); - //worstRes - Double worst_result = round(result.getResultStats().getMax(), 2); - writer.write(date(Double.valueOf(worst_result).toString()) + newline()); - //stdevRes - Double std_result = round(result.getResultStats().getStandardDeviation(), 2); - writer.write(date(Double.valueOf(std_result).toString()) + newline()); - //bestVeh - Double best_vehicle = round(result.getVehicleStats().getMin(), 2); - writer.write(date(Double.valueOf(best_vehicle).toString()) + newline()); - //avgVeh - Double avg_vehicle = round(result.getVehicleStats().getMean(), 2); - writer.write(date(Double.valueOf(avg_vehicle).toString()) + newline()); - //worstVeh - Double worst_vehicle = round(result.getVehicleStats().getMax(), 2); - writer.write(date(Double.valueOf(worst_vehicle).toString()) + newline()); - //stdevVeh - Double std_vehicle = round(result.getVehicleStats().getStandardDeviation(), 2); - writer.write(date(Double.valueOf(std_vehicle).toString()) + newline()); - //bestKnownRes - writer.write(date("" + result.instance.bestKnownResult + newline())); - //bestKnownVeh - writer.write(date("" + result.instance.bestKnownVehicles + newline())); - writer.write(closeRow() + newline()); - - sum_avg_time += avg_time; - sum_best_result += best_result; - sum_avg_result += avg_result; - sum_worst_result += worst_result; - sum_dev_result += std_result; - - sum_best_veh += best_vehicle; - sum_avg_veh += avg_vehicle; - sum_worst_veh += worst_vehicle; - sum_dev_veh += std_vehicle; - - if (result.instance.bestKnownResult != null) { - if (sum_res_star == null) sum_res_star = result.instance.bestKnownResult; - else sum_res_star += result.instance.bestKnownResult; - } - if (result.instance.bestKnownVehicles != null) { - if (sum_veh_star == null) sum_veh_star = result.instance.bestKnownVehicles; - else sum_veh_star += result.instance.bestKnownVehicles; - } - - } - writer.write(openRow() + newline()); - writer.write(date("Ø") + newline()); - writer.write(date("" + runs) + newline()); - - Double average_time = round(sum_avg_time / (double) results.size(), 2); - writer.write(date(Double.valueOf(average_time).toString()) + newline()); - //bestRes - writer.write(date(Double.valueOf(round(sum_best_result / (double) results.size(), 2)).toString()) + newline()); - //avgRes - Double average_result = round(sum_avg_result / (double) results.size(), 2); - writer.write(date(Double.valueOf(average_result).toString()) + newline()); - //worstRes - writer.write(date(Double.valueOf(round(sum_worst_result / (double) results.size(), 2)).toString()) + newline()); - //stdevRes - writer.write(date(Double.valueOf(round(sum_dev_result / (double) results.size(), 2)).toString()) + newline()); - //bestVeh - writer.write(date(Double.valueOf(round(sum_best_veh / (double) results.size(), 2)).toString()) + newline()); - //avgVeh - Double average_vehicles = round(sum_avg_veh / (double) results.size(), 2); - writer.write(date(Double.valueOf(average_vehicles).toString()) + newline()); - //worstVeh - writer.write(date(Double.valueOf(round(sum_worst_veh / (double) results.size(), 2)).toString()) + newline()); - //stdevVeh - writer.write(date(Double.valueOf(round(sum_dev_veh / (double) results.size(), 2)).toString()) + newline()); - //bestKnownRes - Double delta_res = null; - if (sum_res_star != null) { - writer.write(date(Double.valueOf(round(sum_res_star.doubleValue() / (double) results.size(), 2)).toString()) + newline()); - delta_res = (sum_avg_result / sum_res_star - 1) * 100; - } else writer.write(date("null") + newline()); - //bestKnownVeh - Double delta_veh = null; - if (sum_veh_star != null) { - writer.write(date(Double.valueOf(round(sum_veh_star.doubleValue() / (double) results.size(), 2)).toString()) + newline()); - delta_veh = (sum_avg_veh - sum_veh_star) / (double) results.size(); - } else writer.write(date("null") + newline()); - writer.write(closeRow() + newline()); - - writer.write(closeTable() + newline()); - - writer.write("avg. percentage deviation to best-known result: " + round(delta_res, 2) + newline() + newline()); - writer.write("avg. absolute deviation to best-known vehicles: " + round(delta_veh, 2) + newline()); - - writer.write(openTable() + newline()); - writer.write(openRow() + newline()); - writer.write(date("") + newline()); - writer.write(date("") + newline()); - writer.write(date("") + newline()); - writer.write(date("") + newline()); - writer.write(date(Double.valueOf(average_time).toString(), "align=\"right\"") + newline()); - writer.write(date(Double.valueOf(average_result).toString(), "align=\"right\"") + newline()); - writer.write(date(Double.valueOf(average_vehicles).toString(), "align=\"right\"") + newline()); - if (delta_res != null) { - writer.write(date(Double.valueOf(round(delta_res, 2)).toString(), "align=\"right\"") + newline()); - } else writer.write(date("n.a.") + newline()); - if (delta_veh != null) { - writer.write(date(Double.valueOf(round(delta_veh, 2)).toString(), "align=\"right\"") + newline()); - } else writer.write(date("n.a.") + newline()); - writer.write(closeRow() + newline()); - writer.write(closeTable() + newline()); - - - writer.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } - - private String head(String string, int i) { - return "" + string + ""; - } - - private Double round(Double value, int i) { - if (value == null) return null; - long roundedVal = Math.round(value * Math.pow(10, i)); - return (double) roundedVal / (double) (Math.pow(10, i)); - } - - private String head(String head) { - return "" + head + ""; - } - - private String closeTable() { - return ""; - } - - private String openTable() { - return ""; - } - - private String closeRow() { - return ""; - } - - private String date(String date) { - return ""; - } - - private String date(String date, String metaData) { - return ""; - } - - private String newline() { - return "\n"; - } - - private String openRow() { - return ""; - } - - -}
" + date + "" + date + "