The expiration time for new job artifacts in CI/CD pipelines is now 30 days (GitLab default). Previously generated artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit 27380b79 authored by Stefan Schuhbaeck's avatar Stefan Schuhbaeck
Browse files

rebase group_models to develop branch

parents 252ed8ea a9caddb3
......@@ -43,3 +43,4 @@ VadereGui/output/
#linux deskopt dotfiles
**/.directory
/VadereModelTests/TestStairs/output
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="source_icon.svg"
inkscape:export-filename="C:\Users\Benedikt\Repositories\Software\VadereGui\resources\icons\source_icon.png"
inkscape:export-xdpi="39.389999"
inkscape:export-ydpi="39.389999">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:cx="508.27116"
inkscape:cy="318.54973"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1680"
inkscape:window-height="987"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Ground">
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;stroke:none"
id="path2999"
sodipodi:cx="567.25"
sodipodi:cy="745.61218"
sodipodi:rx="29"
sodipodi:ry="12.25"
d="m 596.25,745.61218 a 29,12.25 0 1 1 -58,0 29,12.25 0 1 1 58,0 z"
transform="matrix(1.2543104,0,0,1.132653,-152.88254,-93.532738)" />
</g>
<g
inkscape:label="Arrow Out"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:#800000"
d="m 523.54785,718.71229 c 3.67872,-11.89789 14.75259,-23.56593 25.00868,-26.35049 15.48687,-4.20472 28.23708,5.48211 29.97739,22.77504 0.84941,8.44048 0.94863,8.5832 5.84654,8.41155 7.70502,-0.27004 6.43547,2.77305 -5.6306,13.4965 l -11.11785,9.88075 -8.90828,-10.02363 c -4.89957,-5.51302 -8.8233,-10.84316 -8.71949,-11.84479 0.10385,-1.00163 2.96952,-1.86734 6.36816,-1.9238 5.73649,-0.0953 6.21234,-0.42061 6.63925,-4.53837 0.68171,-6.57556 -6.18382,-13.29468 -12.96608,-12.68957 -6.25183,0.5578 -17.03698,9.10527 -23.24218,18.41998 -5.07932,7.6246 -6.55947,5.07255 -3.25554,-5.61317 z"
id="path2996"
inkscape:connector-curvature="0" />
</g>
</svg>
......@@ -220,6 +220,7 @@ PostVis.chbShowStairs.text=Show Stairs
PostVis.btnSnapshot.tooltip=Snapshot
PostVis.btnPNGSnapshot.tooltip=PNG Snapshot
PostVis.btnSVGSnapshot.tooltip=SVG Snapshot
PostVis.btnTikZSnapshot.tooltip=TikZ Snapshot
PostVis.menuFile.title=File
PostVis.menuSettings.title=Setting
PostVis.menuRecentFiles.title=Recent Files
......
......@@ -220,6 +220,7 @@ PostVis.chbShowStairs.text=Treppen anzeigen
PostVis.btnSnapshot.tooltip=Snapshot
PostVis.btnPNGSnapshot.tooltip=PNG-Snapshot
PostVis.btnSVGSnapshot.tooltip=SVG-Snapshot
PostVis.btnTikZSnapshot.tooltip=TikZ Snapshot
PostVis.menuFile.title=Datei
PostVis.menuSettings.title=Einstellungen
PostVis.menuRecentFiles.title=K\u00FCrzlich verwendete Dateien
......
......@@ -106,12 +106,14 @@ public class ScenarioElementView extends JPanel implements ISelectScenarioElemen
}
@Override
public void removeUpdate(DocumentEvent e) {
public void removeUpdate(DocumentEvent e)
{
updateModel();
}
@Override
public void changedUpdate(DocumentEvent e) {
updateModel();
}
};
......@@ -126,7 +128,7 @@ public class ScenarioElementView extends JPanel implements ISelectScenarioElemen
ScenarioElement element = panelModel.getSelectedElement();
if (element != null) {
String json = txtrTextfiletextarea.getText();
//logger.info(json);
if (json.length() == 0)
return;
......@@ -143,7 +145,7 @@ public class ScenarioElementView extends JPanel implements ISelectScenarioElemen
} else {
try {
Attributes attributes = StateJsonConverter.deserializeScenarioElementType(json, element.getType());
ReflectionAttributeModifier.setAttributes(element, attributes);
element.setAttributes(attributes); // Replaces previous AttributeModifier.setAttributes (see #91)
ScenarioPanel.removeJsonParsingErrorMsg();
ProjectView.getMainWindow().refreshScenarioNames();
jsonValidIndicator.setValid();
......@@ -184,8 +186,8 @@ public class ScenarioElementView extends JPanel implements ISelectScenarioElemen
} else if (scenarioElement instanceof Pedestrian) {
this.txtrTextfiletextarea.setText(StateJsonConverter.serializeObject(scenarioElement));
} else {
this.txtrTextfiletextarea.setText(StateJsonConverter
.serializeObject(ReflectionAttributeModifier.getAttributes(scenarioElement)));
this.txtrTextfiletextarea.setText(StateJsonConverter
.serializeObject(scenarioElement.getAttributes()));
}
}
}
......
......@@ -36,7 +36,7 @@ public class OnlineVisualization implements PassiveCallback {
private OnlineVisualisationWindow onlineVisualisationPanel;
private OnlineVisualizationModel model;
private Topography scenario;
private IPotentialFieldTarget potentialFieldTarget;
private @Nullable IPotentialFieldTarget potentialFieldTarget;
private boolean enableVisualization;
public OnlineVisualization(boolean enableVisualization) {
......@@ -94,7 +94,7 @@ public class OnlineVisualization implements PassiveCallback {
new ObservationAreaSnapshotData(
simTimeInSec,
scenario.clone(),
model.config.isShowPotentialField() ? potentialFieldTarget.copyFields() : null));
(model.config.isShowPotentialField() && potentialFieldTarget != null) ? potentialFieldTarget.copyFields() : null));
}
}
......
package org.vadere.gui.onlinevisualization.control;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.vadere.gui.components.model.DefaultSimulationConfig;
import org.vadere.gui.components.model.SimulationModel;
import org.vadere.gui.components.utils.Resources;
import org.vadere.gui.components.view.SimulationRenderer;
import org.vadere.gui.onlinevisualization.view.IRendererChangeListener;
import org.vadere.gui.postvisualization.PostVisualisation;
import org.vadere.gui.postvisualization.utils.SVGGenerator;
import org.vadere.gui.postvisualization.utils.TikzGenerator;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.prefs.Preferences;
public class ActionGenerateTikz extends AbstractAction implements IRendererChangeListener {
private static Logger logger = LogManager.getLogger(ActionGenerateTikz.class);
private static Resources resources = Resources.getInstance("postvisualization");
private final TikzGenerator tikzGenerator;
private final SimulationModel<? extends DefaultSimulationConfig> model;
public ActionGenerateTikz(final String name, final Icon icon, final SimulationRenderer renderer,
final SimulationModel<? extends DefaultSimulationConfig> model) {
super(name, icon);
this.tikzGenerator = new TikzGenerator(renderer, model);
this.model = model;
}
@Override
public void actionPerformed(ActionEvent e) {
Date todaysDate = new Date();
SimpleDateFormat formatter = new SimpleDateFormat(resources.getProperty("View.dataFormat"));
String formattedDate = formatter.format(todaysDate);
JFileChooser fileChooser = new JFileChooser(Preferences.userNodeForPackage(PostVisualisation.class).get("PostVis.snapshotDirectory.path", "."));
File outputFile = new File("pv_snapshot_" + formattedDate + ".tex");
fileChooser.setSelectedFile(outputFile);
int returnVal = fileChooser.showDialog(null, "Save");
if (returnVal == JFileChooser.APPROVE_OPTION) {
outputFile = fileChooser.getSelectedFile().toString().endsWith(".tex") ? fileChooser.getSelectedFile()
: new File(fileChooser.getSelectedFile().toString() + ".tex");
boolean completeDocument = true;
tikzGenerator.generateTikz(outputFile, completeDocument);
}
}
@Override
public void update(SimulationRenderer renderer) {}
}
......@@ -15,6 +15,7 @@ import org.vadere.gui.components.view.ScenarioScrollPane;
import org.vadere.gui.components.view.SimulationInfoPanel;
import org.vadere.gui.onlinevisualization.control.ActionGeneratePNG;
import org.vadere.gui.onlinevisualization.control.ActionGenerateSVG;
import org.vadere.gui.onlinevisualization.control.ActionGenerateTikz;
import org.vadere.gui.onlinevisualization.control.ActionOnlineVisMenu;
import org.vadere.gui.onlinevisualization.control.ActionShowPotentialField;
import org.vadere.gui.onlinevisualization.model.OnlineVisualizationModel;
......@@ -161,6 +162,12 @@ public class OnlineVisualisationWindow extends JPanel implements Observer {
new OnlinevisualizationRenderer(model),
model);
ActionGenerateTikz generateTikz = new ActionGenerateTikz(
"generateTikz",
resources.getIcon("camera_tikz.png", iconWidth, iconHeight),
new OnlinevisualizationRenderer(model),
model);
ActionShowPotentialField showPotentialField = new ActionShowPotentialField(
"showPotentialField",
resources.getIcon("potentialField.png", iconWidth, iconHeight),
......@@ -168,6 +175,7 @@ public class OnlineVisualisationWindow extends JPanel implements Observer {
mainPanel.addRendererChangeListener(generatePNG);
mainPanel.addRendererChangeListener(generateSVG);
mainPanel.addRendererChangeListener(generateTikz);
mainPanel.addRendererChangeListener(showPotentialField);
......@@ -190,6 +198,7 @@ public class OnlineVisualisationWindow extends JPanel implements Observer {
ArrayList<Action> imgOptions = new ArrayList<>();
imgOptions.add(generatePNG);
imgOptions.add(generateSVG);
imgOptions.add(generateTikz);
ActionOnlineVisMenu imgDialog = new ActionOnlineVisMenu(
"camera_menu",
......
package org.vadere.gui.postvisualization.control;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.vadere.gui.components.utils.Resources;
import org.vadere.gui.postvisualization.PostVisualisation;
import org.vadere.gui.postvisualization.utils.TikzGenerator;
import org.vadere.gui.postvisualization.view.PostvisualizationRenderer;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.prefs.Preferences;
public class ActionGenerateTikz extends ActionVisualization {
private static Logger logger = LogManager.getLogger(ActionGenerateTikz.class);
private static Resources resources = Resources.getInstance("postvisualization");
private final TikzGenerator tikzGenerator;
public ActionGenerateTikz(final String name, final Icon icon, final PostvisualizationRenderer renderer) {
super(name, icon, renderer.getModel());
this.tikzGenerator = new TikzGenerator(renderer, renderer.getModel());
}
@Override
public void actionPerformed(ActionEvent e) {
Date todaysDate = new Date();
SimpleDateFormat formatter = new SimpleDateFormat(resources.getProperty("View.dataFormat"));
String formattedDate = formatter.format(todaysDate);
JFileChooser fileChooser = new JFileChooser(Preferences.userNodeForPackage(PostVisualisation.class).get("PostVis.snapshotDirectory.path", "."));
File outputFile = new File("pv_snapshot_" + formattedDate + ".tex");
fileChooser.setSelectedFile(outputFile);
int returnVal = fileChooser.showDialog(null, "Save");
if (returnVal == JFileChooser.APPROVE_OPTION) {
outputFile = fileChooser.getSelectedFile().toString().endsWith(".tex") ? fileChooser.getSelectedFile()
: new File(fileChooser.getSelectedFile().toString() + ".tex");
boolean completeDocument = true;
tikzGenerator.generateTikz(outputFile, completeDocument);
}
}
}
package org.vadere.gui.postvisualization.utils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.vadere.gui.components.model.DefaultSimulationConfig;
import org.vadere.gui.components.model.SimulationModel;
import org.vadere.gui.components.view.SimulationRenderer;
import org.vadere.state.scenario.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.io.*;
import java.util.*;
import static java.awt.geom.PathIterator.*;
/**
* Convert the (Java) scenario description into a TikZ representation.
*
* Usually, each (Java) scenario element is represented as a path via
* @see PathIterator This PathSeparator must be converted into its TikZ
* representation.
*
* For example, traversing a Java path with PathIterator returns two
* segments:
*
*
* segment type= 0 (SEG_MOVETO) with coords: [1.0, 1.0, 0.0, 0.0, 0.0, 0.0]
* segment type= 3 (SEG_LINETO) with coords: [2.0, 2.0, 0.0, 0.0, 0.0, 0.0]
*
* This must be transformed to TikZ:
*
* (1.0,1.0) to (2.0,2.0)
*
* The TikZGenerator should also respect the GUI settings (e.g., enabled
* elements, colors etc.).
*/
public class TikzGenerator {
private final static Logger logger = LogManager.getLogger(TikzGenerator.class);
private final SimulationRenderer renderer;
private final SimulationModel<? extends DefaultSimulationConfig> model;
private final String[] translationTable;
public TikzGenerator(final SimulationRenderer renderer,
final SimulationModel<? extends DefaultSimulationConfig> model) {
this.renderer = renderer;
this.model = model;
this.translationTable = new String[SEG_CLOSE + 1];
initializeTranslationTable(translationTable);
}
private void initializeTranslationTable(String[] translationTable) {
// Map Java path commands to TikZ commands.
translationTable[SEG_MOVETO] = "(%f,%f) ";
translationTable[SEG_LINETO] = "to (%f,%f) ";
translationTable[SEG_QUADTO] = ".. controls (%f,%f) .. (%f,%f) ";
translationTable[SEG_CUBICTO] = ".. controls (%f,%f) and (%f,%f) .. (%f,%f) ";
translationTable[SEG_CLOSE] = "to cycle;";
}
public void generateTikz(final File file, final boolean generateCompleteDocument) {
String texTemplate = "" +
"\\documentclass{standalone}\n" +
"\\usepackage{tikz}\n\n" +
"\\begin{document}\n" +
"\\begin{tikzpicture}\n" +
"%s" +
"\\end{tikzpicture}\n" +
"\\end{document}\n";
String tikzCode = "";
tikzCode += generateTikzColorDefinitions(model);
tikzCode += convertScenarioElementsToTikz();
String output = (generateCompleteDocument) ? String.format(texTemplate, tikzCode) : tikzCode ;
// TODO: maybe uses Java's resources notation (in general, writing the file should be done by the caller not here).
try {
file.createNewFile();
Writer out = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
out.write(output);
out.flush();
logger.info("generate new TikZ: " + file.getAbsolutePath());
} catch (IOException e1) {
logger.error(e1.getMessage());
e1.printStackTrace();
}
}
private String generateTikzColorDefinitions(SimulationModel<? extends DefaultSimulationConfig> model) {
String colorDefinitions = "% Color Definitions\n";
String colorTextPattern = "\\definecolor{%s}{RGB}{%d,%d,%d}\n";
Color sourceColor = model.getConfig().getSourceColor();
colorDefinitions += String.format(colorTextPattern, "SourceColor", sourceColor.getRed(), sourceColor.getGreen(), sourceColor.getBlue());
Color targetColor = model.getConfig().getTargetColor();
colorDefinitions += String.format(colorTextPattern, "TargetColor", targetColor.getRed(), targetColor.getGreen(), targetColor.getBlue());
Color obstacleColor = model.getConfig().getObstacleColor();
colorDefinitions += String.format(colorTextPattern, "ObstacleColor", obstacleColor.getRed(), obstacleColor.getGreen(), obstacleColor.getBlue());
Color stairColor = model.getConfig().getStairColor();
colorDefinitions += String.format(colorTextPattern, "StairColor", stairColor.getRed(), stairColor.getGreen(), stairColor.getBlue());
Color agentColor = model.getConfig().getPedestrianDefaultColor();
colorDefinitions += String.format(colorTextPattern, "AgentColor", agentColor.getRed(), agentColor.getGreen(), agentColor.getBlue());
return colorDefinitions;
}
private String convertScenarioElementsToTikz() {
String generatedCode = "";
DefaultSimulationConfig config = model.getConfig();
Topography topography = model.getTopography();
// Draw background elements first, then agents.
generatedCode += "% Ground\n";
String groundTextPattern = (config.isShowGrid()) ? "\\draw[help lines] (%f,%f) grid (%f,%f);\n" : "\\fill[white] (%f,%f) rectangle (%f,%f);\n";
generatedCode += String.format(groundTextPattern,
topography.getBounds().x,
topography.getBounds().y,
topography.getBounds().x + topography.getBounds().width,
topography.getBounds().y + topography.getBounds().height);
if (config.isShowSources()) {
generatedCode += "% Sources\n";
for (Source source : topography.getSources()) {
generatedCode += String.format("\\fill[SourceColor] %s\n", generatePathForScenarioElement(source));
}
} else {
generatedCode += "% Sources (not enabled in config)\n";
}
if (config.isShowTargets()) {
generatedCode += "% Targets\n";
for (Target target : topography.getTargets()) {
generatedCode += String.format("\\fill[TargetColor] %s\n", generatePathForScenarioElement(target));
}
} else {
generatedCode += "% Targets (not enabled in config)\n";
}
if (config.isShowObstacles()) {
generatedCode += "% Obstacles\n";
for (Obstacle obstacle : topography.getObstacles()) {
generatedCode += String.format("\\fill[ObstacleColor] %s\n", generatePathForScenarioElement(obstacle));
}
} else {
generatedCode += "% Obstacles (not enabled in config)\n";
}
if (config.isShowStairs()) {
generatedCode += "% Stairs\n";
for (Stairs stair : topography.getStairs()) {
generatedCode += String.format("\\fill[StairColor] %s\n", generatePathForScenarioElement(stair));
}
} else {
generatedCode += "% Stairs (not enabled in config)\n";
}
// TODO: maybe, draw also trajectories.
if (config.isShowPedestrians()) {
generatedCode += "% Agents\n";
for (Agent agent : model.getAgents()) {
String agentTextPattern = "\\fill[AgentColor] (%f,%f) circle [radius=%fcm];\n";
generatedCode += String.format(agentTextPattern, agent.getPosition().x, agent.getPosition().y, agent.getRadius());
// Do not draw agents as path for performance reasons. Usually, agents have a circular shape.
// generatedCode += String.format("\\fill[AgentColor] %s\n", generatePathForScenarioElement(agent));
}
} else {
generatedCode += "% Agents (not enabled in config)\n";
}
return generatedCode;
}
private String generatePathForScenarioElement(ScenarioElement element) {
String generatedPath = "";
AffineTransform noTransformation = new AffineTransform();
PathIterator pathIterator = element.getShape().getPathIterator(noTransformation);
while (!pathIterator.isDone()) {
float[] coords = new float[6];
int type = pathIterator.currentSegment(coords);
generatedPath += convertJavaToTikzPath(type, coords);
pathIterator.next();
}
return generatedPath;
}
private String convertJavaToTikzPath(int type, float[] coords) {
if (type < SEG_MOVETO || type > SEG_CLOSE) {
throw new IllegalStateException(String.format("Cannot process path segment type: %d (coordinates: %s)", type, Arrays.toString(coords)));
}
String convertedPath = translationTable[type];
if (type == SEG_MOVETO) {
convertedPath = String.format(convertedPath, coords[0], coords[1]);
} else if (type == SEG_LINETO) {
convertedPath = String.format(convertedPath, coords[0], coords[1]);
} else if (type == SEG_QUADTO) {
convertedPath = String.format(convertedPath, coords[0], coords[1], coords[2], coords[3]);
} else if (type == SEG_CUBICTO) {
convertedPath = String.format(convertedPath, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]);
}
return convertedPath;
}
}
......@@ -13,19 +13,7 @@ import org.vadere.gui.components.utils.Resources;
import org.vadere.gui.components.utils.SwingUtils;
import org.vadere.gui.components.view.ScenarioElementView;
import org.vadere.gui.postvisualization.PostVisualisation;
import org.vadere.gui.postvisualization.control.ActionGeneratePNG;
import org.vadere.gui.postvisualization.control.ActionGenerateSVG;
import org.vadere.gui.postvisualization.control.ActionOpenFile;
import org.vadere.gui.postvisualization.control.ActionPause;
import org.vadere.gui.postvisualization.control.ActionPlay;
import org.vadere.gui.postvisualization.control.ActionRecording;
import org.vadere.gui.postvisualization.control.ActionRemoveFloorFieldFile;
import org.vadere.gui.postvisualization.control.ActionShowPotentialField;
import org.vadere.gui.postvisualization.control.ActionStop;
import org.vadere.gui.postvisualization.control.ActionSwapSelectionMode;
import org.vadere.gui.postvisualization.control.ActionVisualization;
import org.vadere.gui.postvisualization.control.ActionVisualizationMenu;
import org.vadere.gui.postvisualization.control.Player;
import org.vadere.gui.postvisualization.control.*;
import org.vadere.gui.postvisualization.model.PostvisualizationModel;
import org.vadere.gui.projectview.control.ActionDeselect;
import org.vadere.simulator.projects.Scenario;
......@@ -265,6 +253,8 @@ public class PostvisualizationWindow extends JPanel implements Observer {
renderer);
ActionVisualization svgImg = new ActionGenerateSVG(Messages.getString("PostVis.btnSVGSnapshot.tooltip"), resources.getIcon("camera_svg.png", iconWidth, iconHeight),
renderer);
ActionVisualization tikzImg = new ActionGenerateTikz("tikz_snapshot", resources.getIcon("camera_tikz.png", iconWidth, iconHeight),
renderer);
// add new ImageGenerator Action ...
imgOptions.add(pngImg);
......@@ -351,7 +341,6 @@ public class PostvisualizationWindow extends JPanel implements Observer {
public void loadOutputFile(final File trajectoryFile, final Scenario scenario) throws IOException {
Player.getInstance(model).stop();
//todo
model.init(IOOutput.readTrajectories(trajectoryFile.toPath(), scenario), scenario, trajectoryFile.getParent());
model.notifyObservers();
}
......
......@@ -6,6 +6,7 @@ import org.vadere.gui.projectview.view.ProjectView;
import org.vadere.state.attributes.models.AttributesBHM;
import org.vadere.util.io.IOUtils;