/*******************************************************************************
* Copyright (c) 2010 Stefan A. Tzeggai.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v2.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* Contributors:
* Stefan A. Tzeggai - initial API and implementation
******************************************************************************/
package org.geopublishing.geopublisher;
import java.awt.Font;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;
import javax.swing.tree.TreeNode;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.geopublishing.atlasViewer.AtlasCancelException;
import org.geopublishing.atlasViewer.AtlasConfig;
import org.geopublishing.atlasViewer.GpCoreUtil;
import org.geopublishing.atlasViewer.dp.DpEntry;
import org.geopublishing.atlasViewer.dp.DpRef;
import org.geopublishing.atlasViewer.dp.Group;
import org.geopublishing.atlasViewer.dp.layer.DpLayer;
import org.geopublishing.atlasViewer.dp.layer.DpLayerRaster;
import org.geopublishing.atlasViewer.dp.layer.DpLayerVectorFeatureSource;
import org.geopublishing.atlasViewer.dp.layer.LayerStyle;
import org.geopublishing.atlasViewer.dp.media.DpMediaPDF;
import org.geopublishing.atlasViewer.dp.media.DpMediaPICTURE;
import org.geopublishing.atlasViewer.dp.media.DpMediaVideo;
import org.geopublishing.atlasViewer.exceptions.AtlasException;
import org.geopublishing.atlasViewer.exceptions.AtlasFatalException;
import org.geopublishing.atlasViewer.internal.AMLUtil;
import org.geopublishing.atlasViewer.map.Map;
import org.geopublishing.atlasViewer.map.MapPool;
import org.geopublishing.atlasViewer.map.MapRef;
import org.geopublishing.atlasViewer.swing.AVSwingUtil;
import org.geopublishing.geopublisher.exceptions.AtlasExportException;
import org.geotools.data.DataUtilities;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.styling.Style;
import org.jfree.util.Log;
import org.opengis.filter.Filter;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import com.vividsolutions.jts.geom.Envelope;
import de.schmitzm.geotools.data.amd.AttributeMetadataImpl;
import de.schmitzm.geotools.io.GeoExportUtil;
import de.schmitzm.geotools.io.GeoImportUtil;
import de.schmitzm.geotools.styling.StylingUtil;
import de.schmitzm.i18n.Translation;
import de.schmitzm.io.FilterUtil;
import de.schmitzm.io.IOUtil;
import de.schmitzm.jfree.chart.style.ChartStyle;
import de.schmitzm.jfree.chart.style.ChartStyleUtil;
import de.schmitzm.jfree.feature.style.FeatureChartStyle;
import de.schmitzm.jfree.feature.style.FeatureChartUtil;
import de.schmitzm.swing.swingworker.AtlasStatusDialogInterface;
import de.schmitzm.versionnumber.ReleaseUtil;
/**
* This class can export the Atlas-objects to AtlasMarkupLanguage (AtlasML)
*
* @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons Tzeggai</a>
*
*/
public class AMLExporter {
private final String CHARTSTYLE_FILEENDING = ".chart";
final private Logger LOGGER = Logger.getLogger(AMLExporter.class);
/**
* This tag is replaced with the atlas base name when copying the build.xml
* template
**/
final static public String ATLASBASENAME_TAG_IN_BUILDXML = "ATLASBASENAME";
/**
* Creates a <code>build.xml</code> in the AWC folder, which allows
* automatic pblishing on http://atlas.geopublishing.org
*
* @param targetDir
* @param atlasBaseName
* @return
*/
public File writeBuildxml(File targetDir, String atlasBaseName) {
if (atlasBaseName == null
|| atlasBaseName.equals(AtlasConfig.DEFAULTBASENAME)
|| atlasBaseName.equals("null")) {
LOGGER.info("Not writing a build.xml for automatic publishing, because the basename is "
+ atlasBaseName + ". Please change it.");
return null;
}
// prepare the new content:
if (atlasBaseName.endsWith("/")) {
atlasBaseName = atlasBaseName.substring(0,
atlasBaseName.length() - 1);
}
String resLoc = "/autoPublish/build.xml";
URL templateBuildXml = GpUtil.class.getResource(resLoc);
if (templateBuildXml == null) {
LOGGER.error("Could not find " + resLoc
+ ". build.xml has not been created!");
return null;
}
String templString = IOUtil.readURLasString(templateBuildXml);
String buildXmlContent = templString.replaceAll(
Pattern.quote(ATLASBASENAME_TAG_IN_BUILDXML), atlasBaseName);
// Prepare for writing
targetDir.mkdirs();
File buildFile = new File(targetDir, "build.xml");
// Only recreat the file, if it would change to be more svn friendly
if (buildFile.exists()) {
String existing;
try {
existing = IOUtil.readFileAsString(buildFile);
if (buildXmlContent.equals(existing))
return buildFile;
} catch (IOException e) {
LOGGER.warn("Error reading existing build.xml, it will be recreated...");
}
// Recreate the build.xml file...
if (!buildFile.delete()) {
LOGGER.error("Could not delete exitsing "
+ buildFile.getAbsolutePath() + ", to update it.");
}
}
try {
FileWriter fw = new FileWriter(buildFile);
fw.write(buildXmlContent);
fw.flush();
fw.close();
} catch (Exception e) {
LOGGER.error(
"Could not write build.xml for automatic publishing on http://atlas.geopublishing.org",
e);
return null;
}
return buildFile;
}
/**
* If <code>true</code>, only layers that are acessible in the exported
* atlas will be described in the atlas.xml
**/
private boolean exportMode = false;
private AtlasStatusDialogInterface statusWindow = null;
/**
* The {@link AtlasConfigEditable} that is being exported by this
* {@link AMLExporter} instance
**/
private final AtlasConfigEditable ace;
private boolean atlasXmlhasBeenBackupped = false;
private File atlasXml = null;
void info(final String msg) {
if (statusWindow != null)
statusWindow.setDescription(msg);
}
void info(final Translation msg) {
info(msg.toString());
}
public AMLExporter(final AtlasConfigEditable ace) {
this.ace = ace;
}
/**
* Saves the {@link AtlasConfigEditable} to projdir/atlas.xml or whatever
* has been defined via {@link #setAtlasXml(File)}
*
* @return true if no exceptions where thrown.
*/
public boolean saveAtlasConfigEditable() throws Exception {
return saveAtlasConfigEditable(null);
}
/**
* Saves the {@link AtlasConfigEditable} to projdir/atlas.xml or whatever
* has been defined via {@link #setAtlasXml(File)}
*
* @return true if no exceptions where thrown.
*/
public boolean saveAtlasConfigEditable(
final AtlasStatusDialogInterface statusWindow) throws Exception {
this.statusWindow = statusWindow;
try {
// ****************************************************************************
// Trying to make a backup
// ****************************************************************************
atlasXmlhasBeenBackupped = backupAtlasXML();
/**
* Saves the default CRS of that atlas to a file called
* {@link AtlasConfig#DEFAULTCRS_FILENAME} in <code>ad</code>
* folder. Prefers to write the EPSG code of the CRS. Does not
* change the file it the contents are the same (SVN friendly).
*/
GeoExportUtil.writeProjectionFilePrefereEPSG(GeoImportUtil
.getDefaultCRS(), new File(getAce().getAd(),
AtlasConfig.DEFAULTCRS_FILENAME));
// Prepare the DOM document for writing
final Source source = new DOMSource(exportAtlasConfig());
if (!getAtlasXml().exists() && atlasXmlhasBeenBackupped) {
getAtlasXml().createNewFile();
}
FileOutputStream fileOutputStream = new FileOutputStream(
getAtlasXml());
try { // fileOutputStream.close();
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(
fileOutputStream, "utf-8");
try { // close outputStreamWriter.close();
// ****************************************************************************
// Create the XML
// ****************************************************************************
final Result result = new StreamResult(outputStreamWriter);
// with indenting to make it human-readable
final TransformerFactory tf = TransformerFactory
.newInstance();
// TODO Ging mit xerces, geht nicht mehr mit xalan
// tf.setAttribute("indent-number", new Integer(2));
final Transformer xformer = tf.newTransformer();
xformer.setOutputProperty(OutputKeys.INDENT, "yes");
// // TODO ??? Try with AMLURI, makes more sense...
// xformer.setOutputProperty(
// "{"+AMLUtil.AMLSCHEMALOCATION+"}indent-amount",
// "4");
xformer.setOutputProperty(
"{http://xml.apache.org/xalan}indent-amount", "2");
// Write the DOM document to the file
xformer.transform(source, result);
} finally {
outputStreamWriter.close();
}
} finally {
fileOutputStream.close();
}
// Creating build.xml
writeBuildxml(ace.getAtlasDir(), ace.getBaseName());
return true;
} catch (Exception e) {
if (atlasXmlhasBeenBackupped)
try {
LOGGER.warn("copying " + AtlasConfig.ATLAS_XML_FILENAME
+ ".bak to " + AtlasConfig.ATLAS_XML_FILENAME);
IOUtil.copyFile(LOGGER, new File(getAce().getAd(),
AtlasConfig.ATLAS_XML_FILENAME + ".bak"),
getAtlasXml(), false);
} catch (final IOException ioEx) {
LOGGER.error("error copying "
+ AtlasConfig.ATLAS_XML_FILENAME + ".bak back.",
ioEx);
throw (ioEx);
}
if (e instanceof AtlasCancelException)
return false;
else
throw e;
}
}
/**
* @return A XML {@link Document} that fully represents the
* {@link AtlasConfig} of this atlas
* @throws AtlasException
* @author Stefan Alfons Tzeggai
* @throws IOException
*/
private final Document exportAtlasConfig() throws Exception {
// Create a DOM builder and parse the fragment
final DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
Document document = factory.newDocumentBuilder().newDocument();
// XML root element
final Element atlas = document.createElementNS(AMLUtil.AMLURI, "atlas");
// Linking this XML to the AtlasML Schema
// final Attr namespaces = document.createAttributeNS(
// "http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
// namespaces.setValue(AMLUtil.AMLURI + " http://localhost:"
// + Webserver.DEFAULTPORT
// + "/AtlasML.xsd");
// atlas.setAttributeNode(namespaces);
// Storing the version this atlas.xml is being created with inside the
// atlas.xml
atlas.setAttribute(AMLUtil.ATT_majVersion,
String.valueOf(ReleaseUtil.getVersionMaj(GpCoreUtil.class)));
atlas.setAttribute(AMLUtil.ATT_minVersion,
String.valueOf(ReleaseUtil.getVersionMin(GpCoreUtil.class)));
atlas.setAttribute(AMLUtil.ATT_buildVersion,
String.valueOf(ReleaseUtil.getVersionBuild(GpCoreUtil.class)));
// Store the atlasBaseName attribute, since 1.7
atlas.setAttribute(AMLUtil.ATT_atlasBasename, getAce().getBaseName());
// Store the JWS export jnlp baseurl parameter, since 1.6
atlas.setAttribute(AMLUtil.ATT_jnlpBaseUrl,
String.valueOf(getAce().getJnlpBaseUrl()));
// Store the position of the maplogo, since 1.7
atlas.setAttribute(AMLUtil.ATT_maplogoPosition,
String.valueOf(getAce().getMaplogoPosition()));
// <aml:name, desc, creator, copyright
atlas.appendChild(exportTranslation(document, "name", getAce()
.getTitle()));
atlas.appendChild(exportTranslation(document, "desc", getAce()
.getDesc()));
atlas.appendChild(exportTranslation(document, "creator", getAce()
.getCreator()));
atlas.appendChild(exportTranslation(document, "copyright", getAce()
.getCopyright()));
// <aml:atlasversion>
final Element atlasversion = document.createElementNS(AMLUtil.AMLURI,
"atlasversion");
atlasversion.appendChild(document.createTextNode(getAce()
.getAtlasversion().toString()));
atlas.appendChild(atlasversion);
// <aml:gphoster auth=...>
Element gphosterauth = document.createElementNS(AMLUtil.AMLURI,
AMLUtil.TAG_GPHOSTER);
gphosterauth.setAttribute(AMLUtil.ATT_AUTH, getAce().getGpHosterAuth()
+ "");
atlas.appendChild(gphosterauth);
// <aml:supportedLanguages>
// Loops over List of supported Languagecodes
final Element supportedLanguages = document.createElementNS(
AMLUtil.AMLURI, "supportedLanguages");
for (final String langcode : getAce().getLanguages()) {
final Element language = document.createElementNS(AMLUtil.AMLURI,
"language");
language.setAttribute("lang", langcode);
supportedLanguages.appendChild(language);
}
atlas.appendChild(supportedLanguages);
List<DpEntry<? extends ChartStyle>> unusedDpes = getAce()
.getUnusedDpes();
// Loop over all data pool entries and add them to the AML Document
for (final DpEntry de : getAce().getDataPool().values()) {
if (exportMode && unusedDpes.contains(de))
continue;
Node exDpe = null;
checkCancel();
if (de instanceof DpLayer) {
// Save the SLD for this layer
final DpLayer dpl = (DpLayer) de;
try {
final Style style = dpl.getStyle();
StylingUtil.saveStyleToSld(style, DataUtilities
.urlToFile(DataUtilities.changeUrlExt(
AVSwingUtil.getUrl(dpl, statusWindow),
"sld")));
} catch (final Exception e) {
LOGGER.error("Could not transform Style for " + dpl, e);
if (statusWindow != null)
statusWindow.exceptionOccurred(e);
} finally {
// dpl.dispose();
}
}
if (de instanceof DpLayerVectorFeatureSource) {
exDpe = exportDatapoolLayerVector(document,
(DpLayerVectorFeatureSource) de);
} else if (de instanceof DpLayerRaster) {
exDpe = exportDatapoolLayerRaster(document, (DpLayerRaster) de);
} else if (de instanceof DpMediaVideo) {
exDpe = exportDatapoolMediaVideo(document, (DpMediaVideo) de);
} else if (de instanceof DpMediaPDF) {
exDpe = exportDatapoolMediaPdf(document, (DpMediaPDF) de);
} else if (de instanceof DpMediaPICTURE) {
exDpe = exportDatapoolMediaPicture(document, (DpMediaPICTURE) de);
}
atlas.appendChild(exDpe);
}
// The <aml:maps> tag
atlas.appendChild(exportMapPool(document));
// The <aml:group> tag
atlas.appendChild(exportGroup(document, getAce().getRootGroup()));
// The <aml:fonts> tag
atlas.appendChild(exportFonts(document));
document.appendChild(atlas);
// LOGGER.debug("AtlasConfig converted to JDOM Document");
return document;
}
private Node exportDatapoolMediaPicture(Document document, final DpMediaPICTURE dpe) {
// LOGGER.debug("exportDatapoolMediaPDF " + dpe + " to AML");
final Element element = document.createElementNS(AMLUtil.AMLURI,
"pictureMedia");
element.setAttribute("id", dpe.getId());
element.setAttribute("exportable", dpe.isExportable().toString());
// Creating a aml:name tag...
element.appendChild(exportTranslation(document, "name", dpe.getTitle()));
// Creating aml:desc tag
element.appendChild(exportTranslation(document, "desc", dpe.getDesc()));
// Creating optinal aml:keywords tag
if (!dpe.getKeywords().isEmpty())
element.appendChild(exportTranslation(document, "keywords",
dpe.getKeywords()));
// Creating a aml:dataDirname tag...
final Element datadirname = document.createElementNS(AMLUtil.AMLURI,
"dataDirname");
datadirname.appendChild(document.createTextNode(dpe.getDataDirname()));
element.appendChild(datadirname);
// Creating a aml:filename tag...
final Element filename = document.createElementNS(AMLUtil.AMLURI,
"filename");
filename.appendChild(document.createTextNode(dpe.getFilename()));
element.appendChild(filename);
return element;
}
/**
* Creates an aml:fonts tag that lists the names of all the fonts files in
* ad/font folder. The list of fonts is determined while
*/
protected Node exportFonts(Document document) {
final Element fontsElement = document.createElementNS(AMLUtil.AMLURI,
AMLUtil.TAG_FONTS);
File fontsDir = getAce().getFontsDir();
Collection<File> listFiles = FileUtils.listFiles(fontsDir,
GpUtil.FontsFilesFilter, FilterUtil.BlacklistedFoldersFilter);
for (File f : listFiles) {
// Test whether the font is readable
String relPath = f.getAbsolutePath().substring(
fontsDir.getAbsolutePath().length() + 1);
try {
Font.createFont(Font.TRUETYPE_FONT, f);
final Element fontElement = document.createElementNS(
AMLUtil.AMLURI, AMLUtil.TAG_FONT);
fontElement.setAttribute(AMLUtil.ATT_FONT_FILENAME, relPath);
fontsElement.appendChild(fontElement);
} catch (Exception e) {
LOGGER.info("Not exporting a reference to broken font "
+ relPath + " in " + fontsDir, e);
}
}
return fontsElement;
}
private void checkCancel() throws AtlasCancelException {
if (statusWindow != null && statusWindow.isCanceled())
throw new AtlasCancelException();
}
private Node exportDatapoolMediaPdf(final Document document,
final DpMediaPDF dpe) {
// LOGGER.debug("exportDatapoolMediaPDF " + dpe + " to AML");
final Element element = document.createElementNS(AMLUtil.AMLURI,
"pdfMedia");
element.setAttribute("id", dpe.getId());
element.setAttribute("exportable", dpe.isExportable().toString());
// Creating a aml:name tag...
element.appendChild(exportTranslation(document, "name", dpe.getTitle()));
// Creating aml:desc tag
element.appendChild(exportTranslation(document, "desc", dpe.getDesc()));
// Creating optinal aml:keywords tag
if (!dpe.getKeywords().isEmpty())
element.appendChild(exportTranslation(document, "keywords",
dpe.getKeywords()));
// Creating a aml:dataDirname tag...
final Element datadirname = document.createElementNS(AMLUtil.AMLURI,
"dataDirname");
datadirname.appendChild(document.createTextNode(dpe.getDataDirname()));
element.appendChild(datadirname);
// Creating a aml:filename tag...
final Element filename = document.createElementNS(AMLUtil.AMLURI,
"filename");
filename.appendChild(document.createTextNode(dpe.getFilename()));
element.appendChild(filename);
return element;
}
/**
* Exports all the {@link Map}s that are defined in {@link MapPool}
*
* @param document
* {@link Document} to create the element for
* @param mapPool
* {@link MapPool}
* @throws AtlasExportException
* @throws DOMException
* @throws AtlasCancelException
*/
private Node exportMapPool(final Document document) throws DOMException,
AtlasExportException, AtlasCancelException {
final MapPool mapPool = getAce().getMapPool();
final Element element = document.createElementNS(AMLUtil.AMLURI,
"mapPool");
// Test whether a start map exists. When no start-map is defined, the
// user can not export.
if (mapPool.getStartMapID() != null
&& mapPool.get(mapPool.getStartMapID()) != null) {
element.setAttribute("startMap", mapPool.getStartMapID());
} else {
if (exportMode)
throw new AtlasExportException(
GpUtil.R("ExportAtlas.NeedAtLeastOneMap"));
}
// maps MUST contain at least one map
final Collection<Map> maps = mapPool.values();
List<String> notReferencedDpeIDs = getAce()
.lisIdsNotReferencedInGroupTree();
for (final Map map : maps) {
checkCancel();
if (exportMode && notReferencedDpeIDs.contains(map.getId())
&& !map.getId().equals(mapPool.getStartMapID())) {
// Only export maps that are referenced in the group tree or are
// marked as the start map
continue;
}
element.appendChild(exportMap(document, map));
}
return element;
}
/**
* Exports a {@link Map} to AtlasML
*
* @param document
* @param map
* The {@link Map} to export
* @return
* @throws AtlasExportException
*/
private Node exportMap(final Document document, final Map map)
throws AtlasExportException {
final Element element = document.createElementNS(AMLUtil.AMLURI, "map");
element.setAttribute("id", map.getId());
// Save the ratio of the left and the right components of the mapview
final Double preferredRatio = map.getLeftRightRatio();
if (preferredRatio != null && preferredRatio > 0) {
element.setAttribute("leftRightRatio", preferredRatio.toString());
}
// whether any maxMapExtend should be applied in Geopublisher's
// MapComposer
element.setAttribute(AMLUtil.ATT_PREVIEW_MAX_MAPEXTEND_IN_GP,
String.valueOf(map.isPreviewMapExtendInGeopublisher()));
// Creating a aml:name tag...
element.appendChild(exportTranslation(document, "name", map.getTitle()));
// Creating aml:desc tag
element.appendChild(exportTranslation(document, "desc", map.getDesc()));
// Creating optinal aml:keywords tag
if (!map.getKeywords().isEmpty())
element.appendChild(exportTranslation(document, "keywords",
map.getKeywords()));
// Creating optional startViewEnvelope tag ...
if (map.getDefaultMapArea() != null) {
final Element startViewEnvelope = document.createElementNS(
AMLUtil.AMLURI, "startViewEnvelope");
final Envelope area = map.getDefaultMapArea();
startViewEnvelope.appendChild(document.createTextNode(area
.getMinX()
+ ","
+ area.getMinY()
+ " "
+ area.getMaxX()
+ "," + area.getMaxY()));
element.appendChild(startViewEnvelope);
}
// Creating optional maxMapExtend tag ...
if (map.getMaxExtend() != null) {
final Element maxExtend = document.createElementNS(AMLUtil.AMLURI,
"maxExtend");
final Envelope area = map.getMaxExtend();
maxExtend.appendChild(document.createTextNode(area.getMinX() + ","
+ area.getMinY() + " " + area.getMaxX() + ","
+ area.getMaxY()));
element.appendChild(maxExtend);
}
// Is the ScalePanel visible in this map?
element.setAttribute(AMLUtil.ATT_MAP_SCALE_VISIBLE,
Boolean.valueOf(map.isScaleVisible()).toString());
// The units used in the ScalePanel?
element.setAttribute(AMLUtil.ATT_MAP_SCALE_UNITS, map.getScaleUnits()
.toString());
// Are the vert. and hor. GridPanels visible in this map?
element.setAttribute("gridPanelVisible",
Boolean.valueOf(map.isGridPanelVisible()).toString());
// Which formatter to use for the map grid?
element.setAttribute("gridPanelFormatter", map.getGridPanelFormatter()
.getId());
// That .prj file store the CRS used in the map grid panel (de.:
// Kartenrand)
final File outputMapGridPanelPrjFile = new File(getAce().getHtmlDirFor(
map), Map.GRIDPANEL_CRS_FILENAME);
try {
GeoExportUtil.writeProjectionFilePrefereEPSG(map.getGridPanelCRS(),
outputMapGridPanelPrjFile);
} catch (final IOException e1) {
throw new AtlasExportException("Failed to save CRS: "
+ map.getGridPanelCRS() + " to "
+ outputMapGridPanelPrjFile, e1);
}
// Creating aml:datapoolRef tags for the layers and media of this map
for (final DpRef dpr : map.getLayers()) {
element.appendChild(exportDatapoolRef(document, dpr, map));
}
for (final DpRef dpr : map.getMedia()) {
element.appendChild(exportDatapoolRef(document, dpr));
}
/***********************************************************************
* Exporting map.getAdditionalStyles and the selected style ID s
*/
for (final String layerID : map.getAdditionalStyles().keySet()) {
final List<String> styles = map.getAdditionalStyles().get(layerID);
if (styles.size() == 0)
continue;
final Element additionalStyles = document.createElementNS(
AMLUtil.AMLURI, AMLUtil.TAG_ADDITIONAL_STYLES);
additionalStyles.setAttribute("layerID", layerID);
final String selectedStyleID = map.getSelectedStyleID(layerID);
if (selectedStyleID != null)
additionalStyles.setAttribute("selectedStyleID",
selectedStyleID);
for (final String styleID : styles) {
final Element styleidElement = document.createElementNS(
AMLUtil.AMLURI, "styleid");
styleidElement.appendChild(document.createTextNode(styleID));
additionalStyles.appendChild(styleidElement);
}
element.appendChild(additionalStyles);
}
/***********************************************************************
* Exporting map.getAvailableCharts
*/
for (final String layerID : map.getAvailableCharts().keySet()) {
if (!map.getAc().getDataPool().containsKey(layerID)) {
// The corresponing DPE has been deleted?!
continue;
}
final List<String> chartIDs = map.getAvailableChartIDsFor(layerID);
if (chartIDs.size() == 0)
continue;
final Element availableCharts = document.createElementNS(
AMLUtil.AMLURI, "availableCharts");
availableCharts.setAttribute("layerID", layerID);
for (final String chartID : chartIDs) {
final Element styleidElement = document.createElementNS(
AMLUtil.AMLURI, "chartID");
styleidElement.appendChild(document.createTextNode(chartID));
availableCharts.appendChild(styleidElement);
}
element.appendChild(availableCharts);
}
return element;
}
/**
* Creates an < aml:desc > JDOM {@link Node} for the given
* {@link Translation}
*/
private final Element exportTranslation(final Document document,
final String tagname, final Translation translation) {
final Element element = document.createElementNS(AMLUtil.AMLURI,
tagname);
// Creating a sequence of <aml:translation> tags, minOccurs=1
if (translation == null) {
final String warning = "No translation given for " + tagname
+ "\nAtlasMarkupLanguage will probably not be valid.";
if (statusWindow != null) {
statusWindow.warningOccurred(this.getClass().getSimpleName(),
null, warning);
} else
Log.warn(warning);
} else {
if (translation.size() == 0) {
for (final String code : getAce().getLanguages()) {
translation.put(code, "");
}
}
for (final String key : translation.keySet()) {
final Element descTranslation = document.createElementNS(
AMLUtil.AMLURI, "translation");
descTranslation.setAttribute("lang", key);
String string = translation.get(key);
if (string == null)
string = "";
descTranslation.appendChild(document.createTextNode(string));
element.appendChild(descTranslation);
}
}
return element;
}
private final Element exportDatapoolMediaVideo(final Document document,
final DpMediaVideo dpe) {
// LOGGER.debug("exportDatapoolMediaVideo " + dpe + " to AML");
final Element element = document.createElementNS(AMLUtil.AMLURI,
"videoMedia");
element.setAttribute("id", dpe.getId());
element.setAttribute("exportable", dpe.isExportable().toString());
// info(dpe.getTitle());
// Creating a aml:name tag...
element.appendChild(exportTranslation(document, "name", dpe.getTitle()));
// Creating aml:desc tag
element.appendChild(exportTranslation(document, "desc", dpe.getDesc()));
// Creating optinal aml:keywords tag
if (!dpe.getKeywords().isEmpty())
element.appendChild(exportTranslation(document, "keywords",
dpe.getKeywords()));
// Creating a aml:dataDirname tag...
final Element datadirname = document.createElementNS(AMLUtil.AMLURI,
"dataDirname");
datadirname.appendChild(document.createTextNode(dpe.getDataDirname()));
element.appendChild(datadirname);
// Creating a aml:filename tag...
final Element filename = document.createElementNS(AMLUtil.AMLURI,
"filename");
filename.appendChild(document.createTextNode(dpe.getFilename()));
element.appendChild(filename);
return element;
}
/**
* Exports the DatapoolEntry to an AtlasML (XML) document branch
*
* @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons Tzeggai</a>
* @throws IOException
* Thrown if e.g. saving the .cpg fails
*/
private final Element exportDatapoolLayerVector(final Document document,
final DpLayerVectorFeatureSource dpe) throws IOException {
if (dpe.isBroken()) {
LOGGER.info("Trying to save a broken layer..." + dpe);
}
/*
* Saving the Charset to a .cpg file
*/
GpUtil.writeCpg(dpe);
// Creating a aml:rasterLayer tag...
final Element element = document.createElementNS(AMLUtil.AMLURI,
"vectorLayer");
element.setAttribute("id", dpe.getId());
element.setAttribute("exportable", dpe.isExportable().toString());
/***********************************************************************
* The "showStylerInLegend" attribute is optional and defaults to true
***********************************************************************/
if (dpe.isStylerInLegend()) {
element.setAttribute("showStylerInLegend", "true");
} else {
element.setAttribute("showStylerInLegend", "false");
}
/***********************************************************************
* The "showTableInLegend" attribute is optional and defaults to false
***********************************************************************/
if (dpe.isTableVisibleInLegend()) {
element.setAttribute("showTableInLegend", "true");
} else {
element.setAttribute("showTableInLegend", "false");
}
/***********************************************************************
* The "filterInLegend" attribute is optional and defaults to false
***********************************************************************/
if (dpe.isFilterInLegend()) {
element.setAttribute("showFilterInLegend", "true");
} else {
element.setAttribute("showFilterInLegend", "false");
}
// Creating a aml:name tag...
element.appendChild(exportTranslation(document, "name", dpe.getTitle()));
// Creating aml:desc tag
element.appendChild(exportTranslation(document, "desc", dpe.getDesc()));
// Creating optinal aml:keywords tag
if (!dpe.getKeywords().isEmpty())
element.appendChild(exportTranslation(document, "keywords",
dpe.getKeywords()));
// Creating a aml:dataDirname tag...
final Element datadirname = document.createElementNS(AMLUtil.AMLURI,
"dataDirname");
datadirname.appendChild(document.createTextNode(dpe.getDataDirname()));
element.appendChild(datadirname);
// Creating a aml:filename tag...
final Element filename = document.createElementNS(AMLUtil.AMLURI,
"filename");
filename.appendChild(document.createTextNode(dpe.getFilename()));
element.appendChild(filename);
for (final AttributeMetadataImpl attrib : dpe.getAttributeMetaDataMap()
.values()) {
final Element att = exportAttributeMetadata(dpe, document, attrib);
if (att != null)
element.appendChild(att);
}
/**
* Exporting the additional and optional LayerStyles
*/
for (final LayerStyle ls : dpe.getLayerStyles()) {
element.appendChild(exportLayerStyle(document, ls));
}
/**
* This parameter is optional and is only created if needed.
*/
final Filter filter = dpe.getFilter();
if (filter != Filter.INCLUDE) {
// Creating a aml:filename tag...
final Element filterRuleElement = document.createElementNS(
AMLUtil.AMLURI, "filterRule");
filterRuleElement.appendChild(document.createTextNode(CQL
.toCQL(filter)));
element.appendChild(filterRuleElement);
}
/**
* Exporting the list of charts for this layer if any exist. This has to
* be called, AFTER the attribute meta data has been exported. (When
* parsing the XML, we need the NODATA values first)
*/
exportChartStyleDescriptions(document, dpe, element);
return element;
}
/**
* Exports the list of charts for this layer. Any old chart style files are
* first deleted.
*/
private void exportChartStyleDescriptions(final Document document,
final DpLayerVectorFeatureSource dpe, final Element element) {
final AtlasConfigEditable ace = (AtlasConfigEditable) dpe
.getAtlasConfig();
final File chartsFolder = new File(ace.getFileFor(dpe).getParentFile(),
"charts");
chartsFolder.mkdirs();
/*
* Delete all .cs file before saving the charts that actually exist
*/
{
final String[] oldChartFilenames = chartsFolder
.list(new FilenameFilter() {
@Override
public boolean accept(final File dir, final String name) {
if (name.endsWith(CHARTSTYLE_FILEENDING))
return true;
return false;
}
});
for (final String oldChartFilename : oldChartFilenames) {
final File chartSTyeFile = new File(chartsFolder,
oldChartFilename);
if (!chartSTyeFile.delete())
throw new IllegalArgumentException(
"Unable to delte the old chart description file "
+ chartSTyeFile.getAbsolutePath());
}
}
/* Iterate over all charts and create .chart files */
for (final FeatureChartStyle chartStyle : dpe.getCharts()) {
final String csFilename = chartStyle.getID()
+ CHARTSTYLE_FILEENDING;
final File chartFile = new File(chartsFolder, csFilename);
try {
/*
* Write the Chart to XML
*/
// System.out.println("when saving\n"+" "+chartStyle);
if (chartStyle instanceof FeatureChartStyle) {
FeatureChartUtil.FEATURE_CHART_STYLE_FACTORY
.writeStyleToFile(chartStyle, "chartStyle",
chartFile);
} else {
ChartStyleUtil.CHART_STYLE_FACTORY.writeStyleToFile(
chartStyle, "chartStyle", chartFile);
}
} catch (final Exception e) {
LOGGER.error("Error writing the ChartStyle to XML ", e);
}
// Create an aml:chart tag...
final Element chartElement = document.createElementNS(
AMLUtil.AMLURI, "chart");
chartElement.setAttribute("filename", csFilename);
element.appendChild(chartElement);
}
}
/**
* Exports a <layerStyle> tag
*
* @param ac
* @param document
* @param ls
* {@link LayerStyle} to export
* @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons Tzeggai</a>
*/
private Element exportLayerStyle(final Document document,
final LayerStyle ls) {
final Element element = document.createElementNS(AMLUtil.AMLURI,
AMLUtil.TAG_ADDITIONAL_STYLE);
element.setAttribute("filename", ls.getFilename());
element.setAttribute("id", ls.getFilename());
element.appendChild(exportTranslation(document, "title", ls.getTitle()));
element.appendChild(exportTranslation(document, "desc", ls.getDesc()));
return element;
}
/**
* Exports one single {@link AttributeMetadataImpl} to an aml:dataAttribute
* tag.
*
* @return {@link org.w3c.dom.Element} that represents the XML tag
*
* @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons Tzeggai</a>
*/
private Element exportAttributeMetadata(
final DpLayerVectorFeatureSource dpe, final Document document,
final AttributeMetadataImpl attrib) {
final Element element = document.createElementNS(AMLUtil.AMLURI,
AMLUtil.TAG_attributeMetadata);
element.setAttribute(AMLUtil.ATT_namespace, attrib.getName()
.getNamespaceURI());
element.setAttribute(AMLUtil.ATT_localname, attrib.getName()
.getLocalPart());
element.setAttribute(AMLUtil.ATT_weight,
new Integer(new Double(attrib.getWeight()).intValue())
.toString());
element.setAttribute(AMLUtil.ATT_functionA,
new Double(attrib.getFunctionA()).toString());
element.setAttribute(AMLUtil.ATT_functionX,
new Double(attrib.getFunctionX()).toString());
element.setAttribute("visible", String.valueOf(attrib.isVisible()));
if (attrib.getUnit() != null && !attrib.getUnit().isEmpty())
element.setAttribute("unit", attrib.getUnit());
// Creating a aml:name tag...
element.appendChild(exportTranslation(document, "name",
attrib.getTitle()));
// Creating a aml:desc tag...
element.appendChild(exportTranslation(document, "desc",
attrib.getDesc()));
// Storing the NODATA values
for (Object nodatavalue : attrib.getNodataValues()) {
if (nodatavalue == null)
continue;
Element ndValue = document.createElementNS(AMLUtil.AMLURI,
AMLUtil.TAG_nodataValue);
ndValue.setTextContent(nodatavalue.toString());
element.appendChild(ndValue);
}
return element;
}
/**
* Exports the {@link DpLayerRaster} to a AtlasML (XML) Document branch
*
* @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons Tzeggai</a>
*/
private final Element exportDatapoolLayerRaster(final Document document,
final DpLayerRaster<? extends Object, ChartStyle> dpe) {
// Creating a aml:rasterLayer tag...
final Element element = document.createElementNS(AMLUtil.AMLURI,
"rasterLayer");
element.setAttribute("id", dpe.getId());
element.setAttribute("exportable", dpe.isExportable().toString());
if (dpe.getNodataValue() != null)
element.setAttribute(AMLUtil.ATT_NODATA,
String.valueOf(dpe.getNodataValue()));
// Creating a aml:name tag...
element.appendChild(exportTranslation(document, "name", dpe.getTitle()));
// Creating aml:desc tag
element.appendChild(exportTranslation(document, "desc", dpe.getDesc()));
// Creating optinal aml:keywords tag
if (!dpe.getKeywords().isEmpty())
element.appendChild(exportTranslation(document, "keywords",
dpe.getKeywords()));
// Creating optional aml:bands tag
if (dpe.getBandNames().length != 0) {
Integer bandCount = 0;
for (Translation a : dpe.getBandNames()) {
Element translation = exportTranslation(document, "band", a);
translation.setAttribute("number", bandCount.toString());
element.appendChild(translation);
bandCount++;
}
}
// Creating a aml:dataDirname tag...
final Element datadirname = document.createElementNS(AMLUtil.AMLURI,
"dataDirname");
datadirname.appendChild(document.createTextNode(dpe.getDataDirname()));
element.appendChild(datadirname);
// Creating a aml:filename tag...
final Element filename = document.createElementNS(AMLUtil.AMLURI,
"filename");
filename.appendChild(document.createTextNode(dpe.getFilename()));
element.appendChild(filename);
/**
* Exporting the additional and optional LayerStyles
*/
for (final LayerStyle ls : dpe.getLayerStyles()) {
element.appendChild(exportLayerStyle(document, ls));
}
//
// // Creating aml:rasterLegendData
// element.appendChild(exportRasterLegendData(document,
// dpe.getLegendMetaData()));
return element;
}
// /**
// * Export a aml:rasterLegendData tag
// *
// * @param document
// * @param legendMetaData
// * @return
// *
// * @author <a href="mailto:skpublic@wikisquare.de">Stefan Alfons
// Tzeggai</a>
// */
// private Node exportRasterLegendData(final Document document,
// final RasterLegendData legendMetaData) {
// final Element element = document.createElementNS(AMLUtil.AMLURI,
// "rasterLegendData");
// element.setAttribute(AMLUtil.ATT_paintGaps, legendMetaData
// .isPaintGaps().toString());
//
// for (final Double key : legendMetaData.getSortedKeys()) {
// final Element item = document.createElementNS(AMLUtil.AMLURI,
// "rasterLegendItem");
// item.setAttribute("value", key.toString());
// item.appendChild(exportTranslation(document, "label",
// legendMetaData.get(key)));
// element.appendChild(item);
// }
// return element;
// }
/**
* Create a tree of <aml:group> and <aml:datapoolRef>
*
* @param group
* The {@link Group} to start with
* @throws AtlasFatalException
*/
private final Element exportGroup(final Document document, final Group group)
throws AtlasFatalException {
// LOGGER.debug("exportGroup " + group + " to AML");
final Element element = document.createElementNS(AMLUtil.AMLURI,
"group");
// Store whether this is marked as the Help menu in an optional
// attribute
if (group.isHelpMenu()) {
final String isHelpString = Boolean.valueOf(group.isHelpMenu())
.toString();
element.setAttribute("isHelpMenu", isHelpString);
}
// Store whether this is marked as the File menu in an optional
// attribute
if (group.isFileMenu()) {
final String isFileString = Boolean.valueOf(group.isFileMenu())
.toString();
element.setAttribute("isFileMenu", isFileString);
}
// Creating a aml:name tag...
element.appendChild(exportTranslation(document, "name",
group.getTitle()));
// Creating aml:desc tag
element.appendChild(exportTranslation(document, "desc", group.getDesc()));
// Creating optional aml:keywords tag
if (!group.getKeywords().isEmpty())
element.appendChild(exportTranslation(document, "keywords",
group.getKeywords()));
final Enumeration<TreeNode> children = group.children();
while (children.hasMoreElements()) {
final TreeNode nextElement = children.nextElement();
if (nextElement instanceof Group) {
final Group subGroup = (Group) nextElement;
element.appendChild(exportGroup(document, subGroup));
} else if (nextElement instanceof DpRef) {
final DpRef mref = (DpRef) nextElement;
element.appendChild(exportDatapoolRef(document, mref));
} else if (nextElement instanceof MapRef) {
final MapRef mref = (MapRef) nextElement;
final Element datapoolRef = document.createElementNS(
AMLUtil.AMLURI, "mapRef");
datapoolRef.setAttribute("id", mref.getTargetId());
element.appendChild(datapoolRef);
} else {
throw new AtlasFatalException("Can't export Group " + group
+ " because of an unknown TreeNode " + nextElement
+ " of class " + nextElement.getClass().getSimpleName());
}
}
return element;
}
/**
* Creates an aml:datapoolRef tag
*
* @param ref
* {@link DpRef}
* @return A node that can be inserted into XML
*/
private Element exportDatapoolRef(final Document document, final DpRef ref) {
final Element datapoolRef = document.createElementNS(AMLUtil.AMLURI,
"datapoolRef");
datapoolRef.setAttribute("id", ref.getTargetId());
return datapoolRef;
}
/**
* DataPoolRefs used inside a <code>aml:map</code> definition can have two
* more attributes which are saved inside a {@link java.util.Map} in the
* {@link Map}.
*
* @param document
* @param dpr
* @param map
* @return A node that can be inserted into XML
*/
private Node exportDatapoolRef(final Document document, final DpRef dpr,
final Map map) {
final Element datapoolRef = exportDatapoolRef(document, dpr);
{ // Add the optional hideInLegend attribute if it is set. false =
// default
final Boolean hideme = map.getHideInLegendMap().get(
dpr.getTargetId());
if (hideme != null && hideme == true) {
datapoolRef.setAttribute("hideInLegend", "true");
}
}
{ // Add the optional minimizeInLegend attribute if it is set. false =
// default
final Boolean minimizeMe = map.getMinimizedInLegendMap().get(
dpr.getTargetId());
if (minimizeMe != null && minimizeMe == true) {
datapoolRef.setAttribute("minimizeInLegend", "true");
}
}
{ // Add the optional hidden attribute if it is set. false =
// default
final Boolean hidden = map.getHiddenFor(dpr.getTargetId());
if (hidden != null && hidden == true) {
datapoolRef.setAttribute("hidden", "true");
}
}
{ // Add the optional selectable attribute if it is set. true =
// default
final Boolean selectable = map.isSelectableFor(dpr.getTargetId());
if (selectable != null && selectable == false) {
datapoolRef.setAttribute("selectable", "false");
}
}
return datapoolRef;
}
/**
* not in use since 2009
*/
private void copyAtlasMLSchemaFile() {
try {
// Copy Schema AtlasML.xsd to projectDir
LOGGER.debug("Copy Schema AtlasML.xsd into " + getAce().getAd());
URL resourceSchema = AtlasConfig.class
.getResource("resource/AtlasML.xsd");
LOGGER.debug("schemaURL = " + resourceSchema);
if (resourceSchema == null) {
LOGGER.debug("schemaURL == null, try the new way");
final String location = "skrueger/atlas/resource/AtlasML.xsd";
resourceSchema = getAce().getResLoMan().getResourceAsUrl(
location);
// LOGGER.debug("schemaURL (new) = " + resourceSchema);
}
// File schemaFile = new File(resourceSchema.toURI());
org.apache.commons.io.FileUtils.copyURLToFile(resourceSchema,
new File(getAtlasXml().getParentFile(), "AtlasML.xsd"));
} catch (final Exception e) {
LOGGER.debug(" Error while copying AtlasML.xsd... ignoring");
// if (statusWindow != null)statusWindow.exceptionOccurred(new
// AtlasException(e));
}
}
/**
* Keeps up to three three backups of the atlas.xml file. Returns true if
* atlas.xml existed.
*
* @throws IOException
* if files can't be created.
*/
private boolean backupAtlasXML() throws IOException {
if (isExportMode())
return false;
File atlasXml = getAtlasXml();
File bak1 = new File(atlasXml.getParentFile(), atlasXml.getName()
+ ".bak");
File bak2 = new File(atlasXml.getParentFile(), atlasXml.getName()
+ ".bak.bak");
File bak3 = new File(atlasXml.getParentFile(), atlasXml.getName()
+ ".bak.bak.bak");
if (bak2.exists())
IOUtil.copyFile(LOGGER, bak2, bak3, false);
if (bak1.exists())
IOUtil.copyFile(LOGGER, bak1, bak2, false);
if (atlasXml.exists()) {
IOUtil.copyFile(LOGGER, atlasXml, bak1, false);
return true;
}
return false;
}
/**
* If set to <code>true</code>, the created atlas.xml only describes maps
* and layers that are actually used/referenced in the atlas. To save the
* atlas as an 'atlas working copy' with all the information in it, it has
* to be set to <code>false</code>.
*/
public void setExportMode(boolean exportMode) {
this.exportMode = exportMode;
}
/**
* If set to <code>true</code>, the created atlas.xml only describes maps
* and layers that are actually used/referenced in the atlas. To save the
* atlas as an 'atlas working copy' with all the information in it, it has
* to be set to <code>false</code>.
*/
public boolean isExportMode() {
return exportMode;
}
/**
* The {@link File} to write the {@link AtlasConfig} to. Defaults to
* ad/atlas.xml in the {@link AtlasConfig}'s dir.
*/
public void setAtlasXml(File atlasXml) {
this.atlasXml = atlasXml;
}
/**
* @return The {@link File} the {@link AtlasConfig} will be written to in
* AtlasML. Defaults to ad/atlas.xml in the {@link AtlasConfig}'s
* dir.
*/
public File getAtlasXml() {
if (atlasXml == null) {
atlasXml = new File(getAce().getAd(),
AtlasConfig.ATLAS_XML_FILENAME);
}
return atlasXml;
}
/**
* Retusn the {@link AtlasConfigEditable} that is being exported by this
* {@link AMLExporter} instance
**/
public AtlasConfigEditable getAce() {
return ace;
}
}