/*
* Copyright 1998-2015 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation. Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package thredds.client.catalog.writer;
import thredds.client.catalog.*;
import ucar.nc2.units.DateRange;
import ucar.nc2.units.DateType;
import ucar.nc2.units.TimeDuration;
import ucar.unidata.util.Format;
import ucar.unidata.util.StringUtil2;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Formatter;
/**
* Create Html from a dataset
*
* @author caron
* @since 1/8/2015
*/
public class DatasetHtmlWriter {
/**
* Write an Html representation of the given dataset.
* <p> With datasetEvents, catrefEvents = true, this is used to construct an HTML page on the client
* (eg using HtmlPage); the client then detects URL clicks and processes.
* <p> With datasetEvents, catrefEvents = false, this is used to construct an HTML page on the server.
* (eg using HtmlPage); the client then detects URL clicks and processes.
*
* @param out put HTML here.
* @param ds the dataset.
* @param complete if true, add HTML header and ender so its a complete, valid HTML page.
* @param isServer if true, then we are in the thredds data server, so do the following: <ul>
* <li> append "html" to DODS Access URLs
* </ul>
* @param datasetEvents if true, prepend "dataset:" to any dataset access URLS
* @param catrefEvents if true, prepend "catref:" to any catref URLS
*/
public void writeHtmlDescription(Formatter out, Dataset ds,
boolean complete, boolean isServer,
boolean datasetEvents, boolean catrefEvents,
boolean resolveRelativeUrls) {
if (ds == null) return;
if (complete) {
out.format("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"%n");
out.format(" \"http://www.w3.org/TR/html4/loose.dtd\">%n");
out.format("<html>%n");
out.format("<head>%n");
out.format("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">%n");
out.format("</head>%n");
out.format("<body>%n");
}
out.format("<h2>Dataset: %s</h2>%n<ul>", ds.getName());
if (ds.getDataFormatName() != null)
out.format(" <li><em>Data format: </em>%s</li>%n", StringUtil2.quoteHtmlContent(ds.getDataFormatName()));
if ((ds.getDataSize() > 0))
out.format(" <li><em>Data size: </em>%s</li>%n", Format.formatByteSize(ds.getDataSize()));
if (ds.getFeatureTypeName() != null)
out.format(" <li><em>Feature type: </em>%s</li>%n", StringUtil2.quoteHtmlContent(ds.getFeatureTypeName()));
if (ds.getCollectionType() != null)
out.format(" <li><em>Collection type: </em>%s</li>%n", StringUtil2.quoteHtmlContent(ds.getCollectionType()));
if (ds.isHarvest())
out.format(" <li><em>Harvest:</em> true</li>%n");
if (ds.getAuthority() != null)
out.format(" <li><em>Naming Authority: </em>%s</li>%n%n", StringUtil2.quoteHtmlContent(ds.getAuthority()));
if (ds.getId() != null)
out.format(" <li><em>ID: </em>%s</li>%n", StringUtil2.quoteHtmlContent(ds.getId()));
if (ds.getRestrictAccess() != null)
out.format(" <li><em>RestrictAccess: </em>%s</li>%n", StringUtil2.quoteHtmlContent(ds.getRestrictAccess()));
if (ds instanceof CatalogRef) {
CatalogRef catref = (CatalogRef) ds;
String href = resolveRelativeUrls || catrefEvents
? resolve(ds, catref.getXlinkHref())
: catref.getXlinkHref();
if (catrefEvents) href = "catref:" + href;
out.format(" <li><em>CatalogRef: </em>%s</li>%n", makeHref(href, null));
}
out.format("</ul>%n");
java.util.List<Documentation> docs = ds.getDocumentation();
if (docs.size() > 0) {
out.format("<h3>Documentation:</h3>%n<ul>%n");
for (Documentation doc : docs) {
String type = (doc.getType() == null) ? "" : "<strong>" + StringUtil2.quoteHtmlContent(doc.getType()) + ":</strong> ";
String inline = doc.getInlineContent();
if ((inline != null) && (inline.length() > 0))
out.format(" <li>%s %s</li>%n", type, StringUtil2.quoteHtmlContent(inline));
if (doc.hasXlink()) {
out.format(" <li>%s %s</li>%n", type, makeHref(doc.getXlinkHref(), doc.getXlinkTitle()));
}
}
out.format("</ul>%n");
}
java.util.List<Access> access = ds.getAccess();
if (access.size() > 0) {
out.format("<h3>Access:</h3>%n<ol>%n");
for (Access a : access) {
Service s = a.getService();
String urlString = resolveRelativeUrls || datasetEvents
? a.getStandardUrlName()
: a.getUnresolvedUrlName();
String fullUrlString = urlString;
if (datasetEvents) fullUrlString = "dataset:" + fullUrlString;
if (isServer) {
ServiceType stype = s.getType();
if ((stype == ServiceType.OPENDAP) || (stype == ServiceType.DODS))
fullUrlString = fullUrlString + ".html";
else if (stype == ServiceType.DAP4)
fullUrlString = fullUrlString + ".dmr.xml";
else if (stype == ServiceType.WCS)
fullUrlString = fullUrlString + "?service=WCS&version=1.0.0&request=GetCapabilities";
else if (stype == ServiceType.WMS)
fullUrlString = fullUrlString + "?service=WMS&version=1.3.0&request=GetCapabilities";
//NGDC update 8/18/2011
else if (stype == ServiceType.NCML || stype == ServiceType.UDDC || stype == ServiceType.ISO) {
String catalogUrl = ds.getCatalogUrl();
String datasetId = ds.getId();
if (catalogUrl != null && datasetId != null) {
if (catalogUrl.indexOf('#') > 0)
catalogUrl = catalogUrl.substring(0, catalogUrl.lastIndexOf('#'));
try {
catalogUrl = URLEncoder.encode(catalogUrl, "UTF-8");
datasetId = URLEncoder.encode(datasetId, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
fullUrlString = fullUrlString + "?catalog=" + catalogUrl + "&dataset=" + datasetId;
}
} else if (stype == ServiceType.NetcdfSubset)
fullUrlString = fullUrlString + "/dataset.html";
else if ((stype == ServiceType.CdmRemote) || (stype == ServiceType.CdmrFeature))
fullUrlString = fullUrlString + "?req=form";
}
out.format(" <li> <b>%s:</b>%s</li>%n", s.getServiceTypeName(), makeHref(fullUrlString, urlString));
}
out.format("</ol>%n");
}
java.util.List<ThreddsMetadata.Contributor> contributors = ds.getContributors();
if (contributors.size() > 0) {
out.format("<h3>Contributors:</h3>%n<ul>%n");
for (ThreddsMetadata.Contributor t : contributors) {
String role = (t.getRole() == null) ? "" : "<strong> (" + StringUtil2.quoteHtmlContent(t.getRole()) + ")</strong> ";
out.format(" <li>%s %s</li>%n", StringUtil2.quoteHtmlContent(t.getName()), role);
}
out.format("</ul>%n");
}
java.util.List<ThreddsMetadata.Vocab> keywords = ds.getKeywords();
if (keywords.size() > 0) {
out.format("<h3>Keywords:</h3>%n<ul>%n");
for (ThreddsMetadata.Vocab t : keywords) {
String vocab = (t.getVocabulary() == null) ? "" : " <strong>(" + StringUtil2.quoteHtmlContent(t.getVocabulary()) + ")</strong> ";
out.format(" <li>%s %s</li>%n", StringUtil2.quoteHtmlContent(t.getText()), vocab);
}
out.format("</ul>%n");
}
java.util.List<DateType> dates = ds.getDates();
if (dates.size() > 0) {
out.format("<h3>Dates:</h3>%n<ul>%n");
for (DateType d : dates) {
String type = (d.getType() == null) ? "" : " <strong>(" + StringUtil2.quoteHtmlContent(d.getType()) + ")</strong> ";
out.format(" <li>%s %s</li>%n", StringUtil2.quoteHtmlContent(d.getText()), type);
}
out.format("</ul>%n");
}
java.util.List<ThreddsMetadata.Vocab> projects = ds.getProjects();
if (projects.size() > 0) {
out.format("<h3>Projects:</h3>%n<ul>%n");
for (ThreddsMetadata.Vocab t : projects) {
String vocab = (t.getVocabulary() == null) ? "" : " <strong>(" + StringUtil2.quoteHtmlContent(t.getVocabulary()) + ")</strong> ";
out.format(" <li>%s %s</li>%n", StringUtil2.quoteHtmlContent(t.getText()), vocab);
}
out.format("</ul>%n");
}
java.util.List<ThreddsMetadata.Source> creators = ds.getCreators();
if (creators.size() > 0) {
out.format("<h3>Creators:</h3>%n<ul>%n");
for (ThreddsMetadata.Source t : creators) {
out.format(" <li><strong>%s</strong><ul>%n", StringUtil2.quoteHtmlContent(t.getName()));
out.format(" <li><em>email: </em>%s</li>%n", StringUtil2.quoteHtmlContent(t.getEmail()));
if (t.getUrl() != null) {
String newUrl = resolveRelativeUrls
? makeHrefResolve(ds, t.getUrl(), null)
: makeHref(t.getUrl(), null);
out.format(" <li> <em>%s</em></li>%n", newUrl);
}
out.format(" </ul></li>%n");
}
out.format("</ul>%n");
}
java.util.List<ThreddsMetadata.Source> publishers = ds.getPublishers();
if (publishers.size() > 0) {
out.format("<h3>Publishers:</h3>%n<ul>%n");
for (ThreddsMetadata.Source t : publishers) {
out.format(" <li><strong>%s</strong><ul>%n", StringUtil2.quoteHtmlContent(t.getName()));
out.format(" <li><em>email: </em>%s%n", StringUtil2.quoteHtmlContent(t.getEmail()));
if (t.getUrl() != null) {
String urlLink = resolveRelativeUrls
? makeHrefResolve(ds, t.getUrl(), null)
: makeHref(t.getUrl(), null);
out.format(" <li> <em>%s</em></li>%n", urlLink);
}
out.format(" </ul>%n");
}
out.format("</ul>%n");
}
/*
4.2:
<h3>Variables:</h3>
<ul>
<li><em>Vocabulary</em> [DIF]:
<ul>
<li><strong>Reflectivity</strong> = <i></i> = EARTH SCIENCE > Spectral/Engineering > Radar > Radar Reflectivity (db)
<li><strong>Velocity</strong> = <i></i> = EARTH SCIENCE > Spectral/Engineering > Radar > Doppler Velocity (m/s)
<li><strong>SpectrumWidth</strong> = <i></i> = EARTH SCIENCE > Spectral/Engineering > Radar > Doppler Spectrum Width (m/s)
</ul>
</ul>
</ul>
4.3:
<h3>Variables:</h3>
<ul>
<li><em>Vocabulary</em> [CF-1.0]:
<ul>
<li><strong>d3d (meters) </strong> = <i>3D Depth at Nodes
<p> </i> = depth_at_nodes
<li><strong>depth (meters) </strong> = <i>Bathymetry</i> = depth
<li><strong>eta (m) </strong> = <i></i> =
<li><strong>temp (Celsius) </strong> = <i>Temperature
<p> </i> = sea_water_temperature
<li><strong>u (m/s) </strong> = <i>Eastward Water
<p> Velocity
<p> </i> = eastward_sea_water_velocity
<li><strong>v (m/s) </strong> = <i>Northward Water
<p> Velocity
<p> </i> = northward_sea_water_velocity
</ul>
</ul>
*/
java.util.List<ThreddsMetadata.VariableGroup> vars = ds.getVariables();
if (vars.size() > 0) {
out.format("<h3>Variables:</h3>%n<ul>%n");
for (ThreddsMetadata.VariableGroup t : vars) {
out.format("<li><em>Vocabulary</em> [");
if (t.getVocabUri() != null) {
ThreddsMetadata.UriResolved uri = t.getVocabUri();
String vocabLink = resolveRelativeUrls ? makeHref(uri.resolved.toString(), t.getVocabulary())
: makeHref(uri.href, t.getVocabulary());
out.format(vocabLink);
} else {
out.format(StringUtil2.quoteHtmlContent(t.getVocabulary()));
}
out.format("]:%n<ul>%n");
java.util.List<ThreddsMetadata.Variable> vlist = t.getVariableList();
if (vlist.size() > 0) {
for (ThreddsMetadata.Variable v : vlist) {
String units = (v.getUnits() == null || v.getUnits().length() == 0) ? "" : " (" + v.getUnits() + ") ";
out.format(" <li><strong>%s</strong> = ", StringUtil2.quoteHtmlContent(v.getName() + units));
if (v.getDescription() != null)
out.format(" <i>%s</i> = ", StringUtil2.quoteHtmlContent(v.getDescription()));
if (v.getVocabularyName() != null)
out.format("%s", StringUtil2.quoteHtmlContent(v.getVocabularyName()));
out.format("%n");
}
}
out.format("</ul>%n");
}
out.format("</ul>%n");
}
if (ds.getVariableMapLink() != null) {
out.format("<h3>Variables:</h3>%n");
ThreddsMetadata.UriResolved uri = ds.getVariableMapLink();
out.format("<ul><li>" + makeHref(uri.resolved.toASCIIString(), "VariableMap") + "</li></ul>%n");
}
ThreddsMetadata.GeospatialCoverage gc = ds.getGeospatialCoverage();
if (gc != null) {
out.format("<h3>GeospatialCoverage:</h3>%n<ul>%n");
if (gc.isGlobal())
out.format(" <li><em> Global </em>%n");
out.format(" <li><em> Longitude: </em> %s</li>%n", rangeString(gc.getEastWestRange()));
out.format(" <li><em> Latitude: </em> %s</li>%n", rangeString(gc.getNorthSouthRange()));
if (gc.getUpDownRange() != null) {
out.format(" <li><em> Altitude: </em> %s (positive is <strong>%s)</strong></li>%n", rangeString(gc.getUpDownRange()), gc.getZPositive());
}
java.util.List<ThreddsMetadata.Vocab> nlist = gc.getNames();
if ((nlist != null) && (nlist.size() > 0)) {
out.format(" <li><em> Names: </em> <ul>%n");
for (ThreddsMetadata.Vocab elem : nlist) {
out.format(" <li>%s</li>%n", StringUtil2.quoteHtmlContent(elem.getText()));
}
out.format(" </ul>%n");
}
out.format(" </ul>%n");
}
DateRange tc = ds.getTimeCoverage();
if (tc != null) {
out.format("<h3>TimeCoverage:</h3>%n<ul>%n");
DateType start = tc.getStart();
if (start != null)
out.format(" <li><em> Start: </em> %s</li>%n", start.toString());
DateType end = tc.getEnd();
if (end != null) {
out.format(" <li><em> End: </em> %s</li>%n", end.toString());
}
TimeDuration duration = tc.getDuration();
if (duration != null)
out.format(" <li><em> Duration: </em> %s</li>%n", StringUtil2.quoteHtmlContent(duration.toString()));
TimeDuration resolution = tc.getResolution();
if (resolution != null) {
out.format(" <li><em> Resolution: </em> %s</li>%n", StringUtil2.quoteHtmlContent(resolution.toString()));
}
out.format(" </ul>%n");
}
java.util.List<ThreddsMetadata.MetadataOther> metadata = ds.getMetadataOther();
boolean gotSomeMetadata = false;
for (ThreddsMetadata.MetadataOther m : metadata) {
if (m.getXlinkHref() != null) gotSomeMetadata = true;
}
if (gotSomeMetadata) {
out.format("<h3>Metadata:</h3>%n<ul>%n");
for (ThreddsMetadata.MetadataOther m : metadata) {
String type = (m.getType() == null) ? "" : m.getType();
if (m.getXlinkHref() != null) {
String title = (m.getTitle() == null) ? "Type " + type : m.getTitle();
String mdLink = resolveRelativeUrls
? makeHrefResolve(ds, m.getXlinkHref(), title)
: makeHref(m.getXlinkHref(), title);
out.format(" <li> %s</li>%n", mdLink);
} //else {
//out.format(" <li> <pre>"+m.getMetadataType()+" "+m.getContentObject()+"</pre>%n");
//}
}
out.format("</ul>%n");
}
java.util.List<Property> propsOrg = ds.getProperties();
java.util.List<Property> props = new ArrayList<>(ds.getProperties().size());
for (Property p : propsOrg) {
if (!p.getName().startsWith("viewer")) // eliminate the viewer properties from the html view
props.add(p);
}
if (props.size() > 0) {
out.format("<h3>Properties:</h3>%n<ul>%n");
for (Property p : props) {
if (p.getName().equals("attachments")) { // LOOK whats this ?
String attachLink = resolveRelativeUrls
? makeHrefResolve(ds, p.getValue(), p.getName())
: makeHref(p.getValue(), p.getName());
out.format(" <li>%s</li>%n", attachLink);
} else {
out.format(" <li>%s = \"%s\"</li>%n", StringUtil2.quoteHtmlContent(p.getName()), StringUtil2.quoteHtmlContent(p.getValue()));
}
}
out.format("</ul>%n");
}
if (complete) out.format("</body></html>");
}
private String rangeString(ThreddsMetadata.GeospatialRange r) {
if (r == null) return "";
String units = (r.getUnits() == null) ? "" : " " + r.getUnits();
String resolution = r.hasResolution() ? " Resolution=" + r.getResolution() : "";
return StringUtil2.quoteHtmlContent(r.getStart() + " to " + (r.getStart() + r.getSize()) + resolution + units);
}
/**
* resolve reletive URLS against the catalog URL.
*
* @param ds use ds parent catalog, if it exists
* @param href URL to resolve
* @return resolved URL
*/
public String resolve(Dataset ds, String href) {
Catalog cat = ds.getParentCatalog();
if (cat != null) {
try {
java.net.URI uri = cat.resolveUri(href);
href = uri.toString();
} catch (java.net.URISyntaxException e) {
return "DatasetHtmlWriter: error parsing URL= " + href;
}
}
return href;
}
private String makeHref(String href, String title) {
if (title == null) title = href;
return "<a href='" + StringUtil2.quoteHtmlContent(href) + "'>" + StringUtil2.quoteHtmlContent(title) + "</a>";
}
private String makeHrefResolve(Dataset ds, String href, String title) {
if (title == null) title = href;
href = resolve(ds, href);
return makeHref(href, title);
}
}