/* * Copyright 1998-2014 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.servlet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import thredds.catalog.*; import thredds.server.config.HtmlConfig; import thredds.server.config.TdsContext; import thredds.server.viewer.dataservice.ViewerService; import thredds.util.ContentType; import ucar.nc2.constants.AxisType; import ucar.nc2.dataset.CoordinateAxis; import ucar.nc2.dataset.NetcdfDataset; import ucar.nc2.dataset.VariableEnhanced; import ucar.nc2.dt.GridDataset; import ucar.nc2.dt.GridDatatype; import ucar.nc2.time.CalendarDate; import ucar.nc2.units.DateType; import ucar.unidata.util.Format; import ucar.unidata.util.StringUtil2; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import static thredds.servlet.ServletUtil.setResponseContentLength; /** * Provide methods to write HTML representations of a catalog, directory, or CDM dataset to an HTTP response. * <p/> * HtmlWriter is implemented as a singleton. Before HtmlWriter can be used it * must be initialized with init(...). The singleton instance can then be * obtained with getInstance(). * * @author edavis */ @Component public class HtmlWriter { static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(HtmlWriter.class); private static HtmlWriter singleton; @Autowired private TdsContext tdsContext; @Autowired private HtmlConfig htmlConfig; @Autowired private ViewerService viewerService; private HtmlWriter() { } @PostConstruct public void afterPropertiesSet() { singleton = this; } public static HtmlWriter getInstance() { return singleton; } public String getHtmlDoctypeAndOpenTag() { return "<!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'\n" + " 'http://www.w3.org/TR/html4/loose.dtd'>\n" + "<html>\n"; } public String getXHtmlDoctypeAndOpenTag() { return "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN'\n" + " 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>\n" + "<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>"; } // public static final String UNIDATA_CSS public String getUserCSS() { return new StringBuilder() .append("<link rel='stylesheet' href='") .append(this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getPageCssUrl())) .append("' type='text/css' >").toString(); } public String getGoogleTrackingContent() { if (this.htmlConfig.getGoogleTrackingCode().isEmpty()){ return ""; } else { // See https://developers.google.com/analytics/devguides/collection/analyticsjs/ return new StringBuilder() .append("<!-- Google Analytics -->\n") .append("<script>\n") .append("(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n") .append("(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),\n") .append("m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)\n") .append("})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');\n") .append('\n') .append("ga('create', '").append(this.htmlConfig.getGoogleTrackingCode()).append("', 'auto');\n") .append("ga('send', 'pageview');\n") .append("</script>\n") .append("<!-- End Google Analytics -->\n").toString(); } } public String getTdsCatalogCssLink() { return new StringBuilder() .append("<link rel='stylesheet' href='") .append(this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getCatalogCssUrl())) .append("' type='text/css' >").toString(); } public String getTdsPageCssLink() { return new StringBuilder() .append("<link rel='stylesheet' href='") .append(this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getPageCssUrl())) .append("' type='text/css' >").toString(); } // public static final String UNIDATA_HEAD public String getUserHead() { return new StringBuilder() .append("<table width='100%'><tr><td>\n") .append(" <img src='").append(this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getHostInstLogoUrl())) .append("'\n") .append(" alt='").append(this.htmlConfig.getHostInstLogoAlt()).append("'\n") .append(" align='left' valign='top'\n") .append(" hspace='10' vspace='2'>\n") .append(" <h3><strong>").append(this.tdsContext.getWebappName()).append("</strong></h3>\n") .append("</td></tr></table>\n") .toString(); } public String getOldStyleHeader() { StringBuilder sb = new StringBuilder(); appendOldStyleHeader(sb); return sb.toString(); } public void appendOldStyleHeader(StringBuilder sb) { appendOldStyleHeader(sb, this.htmlConfig.getWebappName(), this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getWebappUrl()), this.htmlConfig.getInstallLogoAlt(), this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getInstallLogoUrl()), this.htmlConfig.getInstallName(), this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getInstallUrl()), this.htmlConfig.getHostInstName(), this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getHostInstUrl())); } public void appendOldStyleHeader(StringBuilder sb, String webappName, String webappUrl, String logoAlt, String logoUrl, String installName, String installUrl, String hostName, String hostUrl) { // Table setup. sb.append("<table width='100%'>\n") .append("<tr><td>\n"); // Logo sb.append("<img src='").append(logoUrl) .append("' alt='").append(logoAlt) .append("' align='left' valign='top'") .append(" hspace='10' vspace='2'") .append(">\n"); // Installation name. sb.append("<h3><strong>") .append("<a href='").append(installUrl).append("'>") .append(installName).append("</a>") .append("</strong>"); if (false) sb.append(" at ").append(hostName); sb.append("</h3>\n"); // Webapp Name. sb.append("<h3><strong>") .append("<a href='").append(webappUrl).append("'>") .append(webappName).append("</a>") .append("</strong></h3>\n"); sb.append("</td></tr>\n") .append("</table>\n"); } public void appendTableHeader(StringBuilder stringBuilder, boolean includeInstall, boolean includeWebapp, boolean includeLogos) { // Table setup. stringBuilder .append("<table width='100%'>\n"); if (includeInstall) { stringBuilder.append("<tr><td>\n"); appendInstallationInfo(stringBuilder, includeLogos); stringBuilder.append("</td><td>\n"); appendHostInstInfo(stringBuilder, includeLogos); stringBuilder.append("</td></tr>\n"); } if (includeWebapp) { stringBuilder .append("<tr><td>\n"); appendWebappInfo(stringBuilder, includeLogos); stringBuilder.append("</td></tr>\n"); } stringBuilder.append("</table><hr>\n"); } private void appendWebappInfo(StringBuilder stringBuilder, boolean includeLogo) { // Include webapp info String webappUrl = this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getWebappUrl()); String webappLogoUrl = this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getWebappLogoUrl()); if (includeLogo && webappLogoUrl != null) stringBuilder .append("<img src='").append(webappLogoUrl) .append("' alt='").append(this.htmlConfig.getWebappLogoAlt()) .append("'> "); stringBuilder .append("<a href='").append(webappUrl).append("'>") .append(this.tdsContext.getWebappName()) .append("</a>"); } private void appendHostInstInfo(StringBuilder stringBuilder, boolean includeLogo) { // Include host institution information if (this.htmlConfig.getHostInstName() != null) { String hostInstUrl = this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getHostInstUrl()); String hostInstLogoUrl = this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getHostInstLogoUrl()); if (includeLogo && hostInstLogoUrl != null) stringBuilder .append("<img src='").append(hostInstLogoUrl) .append("' alt='").append(this.htmlConfig.getHostInstLogoAlt()) .append("'> "); if (hostInstUrl != null) stringBuilder.append("<a href='").append(hostInstUrl).append("'>"); stringBuilder.append(this.htmlConfig.getHostInstName()); if (hostInstUrl != null) stringBuilder.append("</a>"); } else stringBuilder.append("Unknown Host Institution"); } private void appendInstallationInfo(StringBuilder stringBuilder, boolean includeLogo) { // Include information on this intsallation. if (this.htmlConfig.getInstallName() != null) { String installUrl = this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getInstallUrl()); String installLogoUrl = this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getInstallLogoUrl()); if (includeLogo && installLogoUrl != null) stringBuilder .append("<img src='").append(installLogoUrl) .append("' alt='").append(this.htmlConfig.getInstallLogoAlt()) .append("'> "); if (installUrl != null) stringBuilder.append("<a href='").append(installUrl).append("'>"); stringBuilder.append(this.htmlConfig.getInstallName()); if (installUrl != null) stringBuilder.append("</a>"); } else { // This installation is not named. stringBuilder.append("Unnamed TDS Installation"); } } public void appendSimpleFooter(StringBuilder sb) { sb.append("<h3>"); if (this.htmlConfig.getInstallName() != null) { String installUrl = this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getInstallUrl()); if (installUrl != null) sb.append("<a href='").append(installUrl).append("'>"); sb.append(this.htmlConfig.getInstallName()); if (installUrl != null) sb.append("</a>"); } if (this.htmlConfig.getHostInstName() != null) { sb.append(" at "); String hostInstUrl = this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getHostInstUrl()); if (hostInstUrl != null) sb.append("<a href='").append(hostInstUrl).append("'>"); sb.append(this.htmlConfig.getHostInstName()); if (hostInstUrl != null) sb.append("</a>"); sb.append(String.format(" see <a href='%s/serverInfo.html'> Info </a>", tdsContext.getContextPath())); sb.append("<br>\n"); } sb.append( this.tdsContext.getWebappName() ) .append( " [Version " ).append( this.tdsContext.getVersionInfo() ); sb.append( "] <a href='" ) .append( this.htmlConfig.prepareUrlStringForHtml( this.htmlConfig.getWebappDocsUrl() ) ) .append( "'> Documentation</a>" ); sb.append( "</h3>\n" ); } private void appendWebappFooter( StringBuilder sb ) { sb.append( "<h3>" ) .append( this.tdsContext.getWebappName() ) .append( " [Version " ).append( this.tdsContext.getVersionInfo() ); sb.append( "] <a href='" ) .append( this.htmlConfig.prepareUrlStringForHtml( this.htmlConfig.getWebappDocsUrl() ) ) .append( "'> Documentation</a>" ); sb.append( "</h3>\n" ); } // private static final String TOMCAT_CSS private String getTomcatCSS() { return new StringBuilder("<STYLE type='text/css'><!--") .append("H1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} ") .append("H2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} ") .append("H3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} ") .append("BODY {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} ") .append("B {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} ") .append("P {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;}") .append("A {color : black;}") .append("A.name {color : black;}") .append("HR {color : #525D76;}") .append("--></STYLE>\r\n") .toString(); } /** * Write a file directory. * * @param res the HttpServletResponse on which to write the file directory response. * @param dir directory * @param path the URL path reletive to the base * @return the number of characters (Unicode code units) in the response. * @throws java.io.IOException if an I/O exception occurs. */ public int writeDirectory(HttpServletResponse res, File dir, String path) throws IOException { // error checking if (dir == null) { res.sendError(HttpServletResponse.SC_NOT_FOUND); return 0; } if (!dir.exists() || !dir.isDirectory()) { res.sendError(HttpServletResponse.SC_NOT_FOUND); return 0; } // Get directory as HTML String dirHtmlString = getDirectory(path, dir); res.setContentType(ContentType.html.getContentHeader()); thredds.servlet.ServletUtil.setResponseContentLength(res, dirHtmlString); PrintWriter writer = res.getWriter(); writer.write(dirHtmlString); writer.flush(); return dirHtmlString.length(); } private String getDirectory(String path, File dir) { StringBuilder sb = new StringBuilder(); // Render the page header sb.append(getHtmlDoctypeAndOpenTag()); // "<html>\n" ); sb.append("<head>\r\n"); sb.append("<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>"); sb.append("<title>"); sb.append("Directory listing for ").append(path); sb.append("</title>\r\n"); sb.append(this.getTdsCatalogCssLink()).append("\n"); sb.append(this.getGoogleTrackingContent()); sb.append("</head>\r\n"); sb.append("<body>\r\n"); sb.append("<h1>"); sb.append("Directory listing for ").append(path); // Render the link to our parent (if required) String parentDirectory = path; if (parentDirectory.endsWith("/")) { parentDirectory = parentDirectory.substring(0, parentDirectory.length() - 1); } int slash = parentDirectory.lastIndexOf('/'); if (slash >= 0) { String parent = parentDirectory.substring(0, slash); sb.append(" - <a href='"); if (parent.equals("")) { parent = "/"; } sb.append("../"); // sb.append(encode(parent)); //if (!parent.endsWith("/")) // sb.append("/"); sb.append("'>"); sb.append("<b>"); sb.append("Up to ").append(parent); sb.append("</b>"); sb.append("</a>"); } sb.append("</h1>\r\n"); sb.append("<HR size='1' noshade='noshade'>"); sb.append("<table width='100%' cellspacing='0'" + " cellpadding='5' align='center'>\r\n"); // Render the column headings sb.append("<tr>\r\n"); sb.append("<td align='left'><font size='+1'><strong>"); sb.append("Filename"); sb.append("</strong></font></td>\r\n"); sb.append("<td align='center'><font size='+1'><strong>"); sb.append("Size"); sb.append("</strong></font></td>\r\n"); sb.append("<td align='right'><font size='+1'><strong>"); sb.append("Last Modified"); sb.append("</strong></font></td>\r\n"); sb.append("</tr>"); // Render the directory entries within this directory boolean shade = false; File[] children = dir.listFiles(); List<File> fileList = (children == null) ? new ArrayList<File>() : Arrays.asList(children); Collections.sort(fileList); for (File child : fileList) { String childname = child.getName(); if (childname.equalsIgnoreCase("WEB-INF") || childname.equalsIgnoreCase("META-INF")) { continue; } if (child.isDirectory()) childname = childname + "/"; //if (!endsWithSlash) childname = path + "/" + childname; // client removes last path if no slash sb.append("<tr"); if (shade) { sb.append(" bgcolor='#eeeeee'"); } sb.append(">\r\n"); shade = !shade; sb.append("<td align='left'>  \r\n"); sb.append("<a href='"); //sb.append( encode(contextPath)); // resourceName = encode(path + resourceName); sb.append(childname); sb.append("'><tt>"); sb.append(childname); sb.append("</tt></a></td>\r\n"); sb.append("<td align='right'><tt>"); if (child.isDirectory()) { sb.append(" "); } else { sb.append(renderSize(child.length())); } sb.append("</tt></td>\r\n"); sb.append("<td align='right'><tt>"); sb.append(CalendarDate.of(child.lastModified()).toString()); sb.append("</tt></td>\r\n"); sb.append("</tr>\r\n"); } // Render the page footer sb.append("</table>\r\n"); sb.append("<HR size='1' noshade='noshade'>"); appendSimpleFooter(sb); sb.append("</body>\r\n"); sb.append("</html>\r\n"); return sb.toString(); } private String renderSize(long size) { long leftSide = size / 1024; long rightSide = (size % 1024) / 103; // Makes 1 digit if ((leftSide == 0) && (rightSide == 0) && (size > 0)) { rightSide = 1; } return ("" + leftSide + "." + rightSide + " kb"); } /** * Write an InvCatalogImpl to the HttpServletResponse, return the size in bytes of the catalog written to the response. * * @param req the HttpServletRequest * @param res the HttpServletResponse. * @param cat the InvCatalogImpl to write to the HttpServletResponse. * @param isLocalCatalog indicates whether this catalog is local to this server. * @return the size in bytes of the catalog written to the HttpServletResponse. * @throws IOException if problems writing the response. */ public int writeCatalog(HttpServletRequest req, HttpServletResponse res, InvCatalogImpl cat, boolean isLocalCatalog) throws IOException { String catHtmlAsString = convertCatalogToHtml(cat, isLocalCatalog); // Once this header is set, we know the encoding, and thus the actual // number of *bytes*, not characters, to encode res.setContentType(ContentType.html.getContentHeader()); int len = setResponseContentLength(res, catHtmlAsString); if (!req.getMethod().equals("HEAD")) { PrintWriter writer = res.getWriter(); writer.write(catHtmlAsString); writer.flush(); } return len; } /** * Write a catalog in HTML, make it look like a file directory. * * @param cat catalog to write */ String convertCatalogToHtml(InvCatalogImpl cat, boolean isLocalCatalog) { StringBuilder sb = new StringBuilder(10000); String catname = StringUtil2.quoteHtmlContent(cat.getUriString()); // Render the page header sb.append(getHtmlDoctypeAndOpenTag()); // "<html>\n" ); sb.append("<head>\r\n"); sb.append("<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>"); sb.append("<title>"); if (cat.isStatic()) sb.append("TdsStaticCatalog ").append(catname); // for searching else sb.append("Catalog ").append(catname); sb.append("</title>\r\n"); sb.append(this.getTdsCatalogCssLink()).append("\n"); sb.append(this.getGoogleTrackingContent()); sb.append("</head>\r\n"); sb.append("<body>"); sb.append("<h1>"); // Logo //String logoUrl = this.htmlConfig.getInstallLogoUrl(); String logoUrl = this.htmlConfig.prepareUrlStringForHtml(this.htmlConfig.getInstallLogoUrl()); if (logoUrl != null) { sb.append("<img src='").append(logoUrl); String logoAlt = this.htmlConfig.getInstallLogoAlt(); if (logoAlt != null) sb.append("' alt='").append(logoAlt); sb.append("' align='left' valign='top'") // .append(" hspace='10' vspace='2'") .append(">\n"); } sb.append("Catalog ").append(catname); sb.append("</h1>"); sb.append("<HR size='1' noshade='noshade'>"); sb.append("<table width='100%' cellspacing='0' cellpadding='5' align='center'>\r\n"); // Render the column headings sb.append("<tr>\r\n"); sb.append("<th align='left'><font size='+1'>"); sb.append("Dataset"); sb.append("</font></th>\r\n"); sb.append("<th align='center'><font size='+1'>"); sb.append("Size"); sb.append("</font></th>\r\n"); sb.append("<th align='right'><font size='+1'>"); sb.append("Last Modified"); sb.append("</font></th>\r\n"); sb.append("</tr>"); // Recursively render the datasets doDatasets(cat, cat.getDatasets(), sb, false, 0, isLocalCatalog); // Render the page footer sb.append("</table>\r\n"); sb.append("<HR size='1' noshade='noshade'>"); appendSimpleFooter(sb); sb.append("</body>\r\n"); sb.append("</html>\r\n"); return sb.toString(); } private boolean doDatasets(InvCatalogImpl cat, List<InvDataset> datasets, StringBuilder sb, boolean shade, int level, boolean isLocalCatalog) { //URI catURI = cat.getBaseURI(); String catHtml; if (!isLocalCatalog) { // Setup HREF url to link to HTML dataset page (more below). catHtml = this.tdsContext.getContextPath() + "/remoteCatalogService?command=subset&catalog=" + cat.getUriString() + "&"; // Can't be "/catalogServices?..." because subset decides on xml or html by trailing ".html" on URL path } else { // replace xml with html URI catURI = cat.getBaseURI(); // Get the catalog name - we want a relative URL catHtml = catURI.getPath(); if (catHtml == null) catHtml = cat.getUriString(); // if URI is a file int pos = catHtml.lastIndexOf("/"); if (pos != -1) catHtml = catHtml.substring(pos + 1); // change the ending to "catalog.html?" pos = catHtml.lastIndexOf('.'); if (pos < 0) catHtml = catHtml + "catalog.html?"; else catHtml = catHtml.substring(0, pos) + ".html?"; } for (InvDataset dataset : datasets) { InvDatasetImpl ds = (InvDatasetImpl) dataset; String name = StringUtil2.quoteHtmlContent(ds.getName()); sb.append("<tr"); if (shade) { sb.append(" bgcolor='#eeeeee'"); } sb.append(">\r\n"); shade = !shade; sb.append("<td align='left'>"); for (int j = 0; j <= level; j++) { sb.append("    "); } sb.append("\r\n"); if (ds instanceof InvCatalogRef) { InvCatalogRef catref = (InvCatalogRef) ds; String href = catref.getXlinkHref(); if (!isLocalCatalog) { URI hrefUri = cat.getBaseURI().resolve(href); href = hrefUri.toString(); } try { URI uri = new URI(href); if (uri.isAbsolute()) { // read default as set in threddsConfig.xml boolean defaultUseRemoteCatalogService = this.htmlConfig.getUseRemoteCatalogService(); // check to see if catalogRef contains tag that overrides default Boolean dsUseRemoteCatalogSerivce = ((InvCatalogRef) ds).useRemoteCatalogService(); // by default, use the option found in threddsConfig.xml boolean useRemoteCatalogService = defaultUseRemoteCatalogService; // if the dataset does not have the useRemoteDataset option set, opt for the default behavior if (dsUseRemoteCatalogSerivce == null) dsUseRemoteCatalogSerivce = defaultUseRemoteCatalogService; // if the default is not the same as what is defined in the catalog, go with the catalog option // as the user has explicitly overridden the default if (defaultUseRemoteCatalogService != dsUseRemoteCatalogSerivce) { useRemoteCatalogService = dsUseRemoteCatalogSerivce; } // now, do the right thing with using the remoteCatalogService, or not if (useRemoteCatalogService) { href = this.tdsContext.getContextPath() + "/remoteCatalogService?catalog=" + href; } else { int pos = href.lastIndexOf('.'); href = href.substring(0, pos) + ".html"; } } else { int pos = href.lastIndexOf('.'); href = href.substring(0, pos) + ".html"; } } catch (URISyntaxException e) { log.error(href, e); } sb.append("<img src='").append(htmlConfig.prepareUrlStringForHtml(htmlConfig.getFolderIconUrl())) .append("' alt='").append(htmlConfig.getFolderIconAlt()).append("'>  "); sb.append("<a href='"); sb.append(StringUtil2.quoteHtmlContent(href)); sb.append("'><tt>"); sb.append(name); sb.append("/</tt></a></td>\r\n"); } else // Not an InvCatalogRef { if (ds.hasNestedDatasets()) sb.append("<img src='").append(htmlConfig.prepareUrlStringForHtml(htmlConfig.getFolderIconUrl())) .append("' alt='").append(htmlConfig.getFolderIconAlt()).append("'>  "); // Check if dataset has single resolver service. if (ds.getAccess().size() == 1 && (ds.getAccess().get(0)).getService().getServiceType().equals(ServiceType.RESOLVER)) { InvAccess access = ds.getAccess().get(0); String accessUrlName = access.getUnresolvedUrlName(); int pos = accessUrlName.lastIndexOf(".xml"); if (accessUrlName.equalsIgnoreCase("latest.xml") && !isLocalCatalog) { String catBaseUriPath = ""; String catBaseUri = cat.getBaseURI().toString(); pos = catBaseUri.lastIndexOf("catalog.xml"); if (pos != -1) { catBaseUriPath = catBaseUri.substring(0,pos); } accessUrlName = this.tdsContext.getContextPath() + "/remoteCatalogService?catalog=" + catBaseUriPath + accessUrlName; } else if (pos != -1) { accessUrlName = accessUrlName.substring(0, pos) + ".html"; } sb.append("<a href='"); sb.append(StringUtil2.quoteHtmlContent(accessUrlName)); sb.append("'><tt>"); String tmpName = name; if (tmpName.endsWith(".xml")) { tmpName = tmpName.substring(0, tmpName.lastIndexOf('.')); } sb.append(tmpName); sb.append("</tt></a></td>\r\n"); } // Dataset with an ID. else if (ds.getID() != null) { // Write link to HTML dataset page. sb.append("<a href='"); sb.append(StringUtil2.quoteHtmlContent(catHtml)); sb.append("dataset="); sb.append(StringUtil2.replace(ds.getID(), '+', "%2B")); sb.append("'><tt>"); sb.append(name); sb.append("</tt></a></td>\r\n"); } // Dataset without an ID. else { sb.append("<tt>"); sb.append(name); sb.append("</tt></td>\r\n"); } } sb.append("<td align='right'><tt>"); double size = ds.getDataSize(); if ((size != 0.0) && !Double.isNaN(size)) { sb.append(Format.formatByteSize(size)); } else { sb.append(" "); } sb.append("</tt></td>\r\n"); sb.append("<td align='right'><tt>"); // Get last modified time. DateType lastModDateType = ds.getLastModifiedDate(); if (lastModDateType == null) { sb.append("--");// "Unknown"); } else { sb.append(lastModDateType.toDateTimeString()); } sb.append("</tt></td>\r\n"); sb.append("</tr>\r\n"); if (!(ds instanceof InvCatalogRef)) { shade = doDatasets(cat, ds.getDatasets(), sb, shade, level + 1, isLocalCatalog); } } return shade; } private String convertDatasetToHtml(String catURL, InvDatasetImpl dataset, HttpServletRequest request, boolean isLocalCatalog) { StringBuilder sb = new StringBuilder(10000); sb.append(this.getHtmlDoctypeAndOpenTag()); sb.append("<head>\r\n"); sb.append("<title> Catalog Services</title>\r\n"); sb.append("<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>\r\n"); sb.append(this.getTdsPageCssLink()).append("\n"); sb.append(this.getGoogleTrackingContent()); sb.append("</head>\r\n"); sb.append("<body>\r\n"); this.appendOldStyleHeader(sb); //this.appendTableHeader( sb, true, true, true ); sb.append("<h2> Catalog ").append(catURL).append("</h2>\r\n"); InvDatasetImpl.writeHtmlDescription(sb, dataset, false, true, false, false, !isLocalCatalog); // optional access through Viewers if (isLocalCatalog) viewerService.showViewers(sb, dataset, request); sb.append( "</body>\r\n" ); sb.append( "</html>\r\n" ); return sb.toString(); } public int showDataset(String catURL, InvDatasetImpl dataset, HttpServletRequest request, HttpServletResponse response, boolean isLocalCatalog) throws IOException { String datasetAsHtml = this.convertDatasetToHtml(catURL, dataset, request, isLocalCatalog); response.setStatus(HttpServletResponse.SC_OK); response.setContentType(ContentType.html.getContentHeader()); if (!request.getMethod().equals("HEAD")) { PrintWriter pw = response.getWriter(); pw.write(datasetAsHtml); pw.flush(); } return datasetAsHtml.length(); } /** * Show CDM compliance (coordinate systems, etc) of a NetcdfDataset. * * @param ds dataset to write */ public void showCDM(HttpServletResponse res, NetcdfDataset ds) throws IOException { String cdmAsString = getCDM(ds); res.setContentType(ContentType.html.getContentHeader()); thredds.servlet.ServletUtil.setResponseContentLength(res, cdmAsString); PrintWriter writer = res.getWriter(); writer.write(cdmAsString); writer.flush(); } private String getCDM(NetcdfDataset ds) throws IOException { StringBuilder sb = new StringBuilder(10000); String name = StringUtil2.quoteHtmlContent(ds.getLocation()); // Render the page header sb.append(getHtmlDoctypeAndOpenTag()); // "<html>\n" ); sb.append("<head>\r\n"); sb.append("<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>"); sb.append("<title>"); sb.append("Common Data Model"); sb.append("</title>\r\n"); sb.append(this.getTdsPageCssLink()).append("\n"); sb.append(this.getGoogleTrackingContent()); sb.append("</head>\r\n"); sb.append("<body>"); sb.append("<h1>"); sb.append("Dataset ").append(name); sb.append("</h1>"); sb.append("<HR size='1' noshade='noshade'>"); sb.append("<table width='100%' cellspacing='0'" + " cellpadding='5' align='center'>\r\n"); //////// Axis sb.append("<tr>\r\n"); sb.append("<td align='left'><font size='+1'><strong>"); sb.append("Axis"); sb.append("</strong></font></td>\r\n"); sb.append("<td align='left'><font size='+1'><strong>"); sb.append("Type"); sb.append("</strong></font></td>\r\n"); sb.append("<td align='left'><font size='+1'><strong>"); sb.append("Units"); sb.append("</strong></font></td>\r\n"); sb.append("</tr>"); // Show the coordinate axes boolean shade = false; for (CoordinateAxis axis : ds.getCoordinateAxes()) { showAxis(axis, sb, shade); shade = !shade; } ///////////// Grid GridDataset gds = new ucar.nc2.dt.grid.GridDataset(ds); // look for projections //List gridsets = gds.getGridsets(); sb.append("<tr>\r\n"); sb.append("<td align='left'><font size='+1'><strong>"); sb.append("GeoGrid"); sb.append("</strong></font></td>\r\n"); sb.append("<td align='left'><font size='+1'><strong>"); sb.append("Description"); sb.append("</strong></font></td>\r\n"); sb.append("<td align='left'><font size='+1'><strong>"); sb.append("Units"); sb.append("</strong></font></td>\r\n"); sb.append("</tr>"); // Show the grids shade = false; for (GridDatatype grid : gds.getGrids()) { showGrid(grid, sb, shade); shade = !shade; } // Render the page footer sb.append("</table>\r\n"); sb.append("<HR size='1' noshade='noshade'>"); appendSimpleFooter(sb); sb.append("</body>\r\n"); sb.append("</html>\r\n"); return (sb.toString()); } private void showAxis(CoordinateAxis axis, StringBuilder sb, boolean shade) { sb.append("<tr"); if (shade) { sb.append(" bgcolor='#eeeeee'"); } sb.append(">\r\n"); shade = !shade; sb.append("<td align='left'>"); sb.append("\r\n"); StringBuilder sbuff = new StringBuilder(); axis.getNameAndDimensions(sbuff); String name = StringUtil2.quoteHtmlContent(sbuff.toString()); sb.append(" "); sb.append(name); sb.append("</tt></a></td>\r\n"); sb.append("<td align='left'><tt>"); AxisType type = axis.getAxisType(); String stype = (type == null) ? "" : StringUtil2.quoteHtmlContent(type.toString()); sb.append(stype); sb.append("</tt></td>\r\n"); sb.append("<td align='left'><tt>"); String units = axis.getUnitsString(); String sunits = (units == null) ? "" : units; sb.append(sunits); sb.append("</tt></td>\r\n"); sb.append("</tr>\r\n"); } private void showGrid(GridDatatype grid, StringBuilder sb, boolean shade) { sb.append("<tr"); if (shade) { sb.append(" bgcolor='#eeeeee'"); } sb.append(">\r\n"); shade = !shade; sb.append("<td align='left'>"); sb.append("\r\n"); VariableEnhanced ve = grid.getVariable(); StringBuilder sbuff = new StringBuilder(); ve.getNameAndDimensions(new Formatter(sbuff), false, true); String name = StringUtil2.quoteHtmlContent(sbuff.toString()); sb.append(" "); sb.append(name); sb.append("</tt></a></td>\r\n"); sb.append("<td align='left'><tt>"); String desc = ve.getDescription(); String sdesc = (desc == null) ? "" : StringUtil2.quoteHtmlContent(desc); sb.append(sdesc); sb.append("</tt></td>\r\n"); sb.append("<td align='left'><tt>"); String units = ve.getUnitsString(); String sunits = (units == null) ? "" : units; sb.append(sunits); sb.append("</tt></td>\r\n"); sb.append("</tr>\r\n"); } }