package uws.job.serializer; /* * This file is part of UWSLibrary. * * UWSLibrary is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * UWSLibrary is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. * * Copyright 2012 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS) */ import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Iterator; import uws.UWSException; import uws.job.ErrorSummary; import uws.job.JobList; import uws.job.Result; import uws.job.UWSJob; import uws.job.user.JobOwner; import uws.service.UWS; import uws.service.UWSUrl; /** * Lets serializing any UWS resource in XML. * * @author Grégory Mantelet (CDS) * @version 05/2012 */ public class XMLSerializer extends UWSSerializer { private static final long serialVersionUID = 1L; /** Tab to add just before each next XML node. */ protected String tabPrefix = ""; /** The path of the XSLT style-sheet. */ protected String xsltPath = null; /** * Builds a XML serializer. */ public XMLSerializer(){ ; } /** * Builds a XML serializer with a XSLT link. * * @param xsltPath Path of a XSLT style-sheet. */ public XMLSerializer(final String xsltPath){ this.xsltPath = xsltPath; } /** * Gets the path/URL of the XSLT style-sheet to use. * * @return XSLT path/url. */ public final String getXSLTPath(){ return xsltPath; } /** * Sets the path/URL of the XSLT style-sheet to use. * * @param path The new XSLT path/URL. */ public final void setXSLTPath(final String path){ if (path == null) xsltPath = null; else{ xsltPath = path.trim(); if (xsltPath.isEmpty()) xsltPath = null; } } /** * <p>Gets the XML file header (xml version, encoding and the xslt style-sheet link if any).</p> * <p>It is always called by the implementation of the UWSSerializer functions * if their boolean parameter (<i>root</i>) is <i>true</i>.</p> * * @return The XML file header. */ public String getHeader(){ StringBuffer xmlHeader = new StringBuffer("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); if (xsltPath != null) xmlHeader.append("<?xml-stylesheet type=\"text/xsl\" href=\"").append(escapeXMLAttribute(xsltPath)).append("\"?>\n"); return xmlHeader.toString(); } /** * Gets all UWS namespaces declarations needed for an XML representation of a UWS object. * * @return The UWS namespaces: <br /> (i.e. <i>= "xmlns:uws=[...] xmlns:xlink=[...] xmlns:xs=[...] xmlns:xsi=[...]"</i>). */ public String getUWSNamespace(){ return "xmlns:uws=\"http://www.ivoa.net/xml/UWS/v1.0\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""; } /** * Gets the node attributes which declare the UWS namespace. * * @param root <i>false</i> if the attribute to serialize will be included * in a top level serialization (for a job attribute: job), <i>true</i> otherwise. * * @return "" if <i>root</i> is <i>false</i>, " "+UWSNamespace otherwise. * * @see #getUWSNamespace() */ protected final String getUWSNamespace(boolean root){ if (root) return " " + getUWSNamespace(); else return ""; } @Override public final String getMimeType(){ return MIME_TYPE_XML; } @Override public String getUWS(final UWS uws, final JobOwner user){ String name = uws.getName(), description = uws.getDescription(); StringBuffer xml = new StringBuffer(getHeader()); xml.append("<uws").append(getUWSNamespace(true)); if (name != null) xml.append(" name=\"").append(escapeXMLAttribute(name)).append("\""); xml.append(">\n"); if (description != null) xml.append("\t<description>\n").append(escapeXMLData(description)).append("\n\t</description>\n"); xml.append("\t<jobLists>\n"); for(JobList jobList : uws){ UWSUrl jlUrl = jobList.getUrl(); xml.append("\t\t<jobListRef name=\"").append(escapeXMLAttribute(jobList.getName())).append("\" href=\""); if (jlUrl != null && jlUrl.getRequestURL() != null) xml.append(escapeURL(jlUrl.getRequestURL())); xml.append("\" />\n"); } xml.append("\t</jobLists>\n"); xml.append("</uws>\n"); return xml.toString(); } @Override public String getJobList(final JobList jobsList, final JobOwner owner, final boolean root) throws UWSException{ String name = jobsList.getName(); StringBuffer xml = new StringBuffer(getHeader()); xml.append("<uws:jobList").append(getUWSNamespace(true)); if (name != null) xml.append(" name=\"").append(escapeXMLAttribute(name)).append("\""); xml.append(">"); UWSUrl jobsListUrl = jobsList.getUrl(); Iterator<UWSJob> it = jobsList.getJobs(owner); while(it.hasNext()) xml.append("\n\t").append(getJobRef(it.next(), jobsListUrl)); xml.append("\n</uws:jobList>"); return xml.toString(); } @Override public String getJob(final UWSJob job, final boolean root){ StringBuffer xml = new StringBuffer(root ? getHeader() : ""); String newLine = "\n\t"; // general information: xml.append("<uws:job").append(getUWSNamespace(root)).append(">"); xml.append(newLine).append(getJobID(job, false)); xml.append(newLine).append(getRunID(job, false)); xml.append(newLine).append(getOwnerID(job, false)); xml.append(newLine).append(getPhase(job, false)); xml.append(newLine).append(getQuote(job, false)); xml.append(newLine).append(getStartTime(job, false)); xml.append(newLine).append(getEndTime(job, false)); xml.append(newLine).append(getExecutionDuration(job, false)); xml.append(newLine).append(getDestructionTime(job, false)); tabPrefix = "\t"; newLine = "\n"; // parameters: xml.append(newLine).append(getAdditionalParameters(job, false)); // results: xml.append(newLine).append(getResults(job, false)); // errorSummary: xml.append(newLine).append(getErrorSummary(job.getErrorSummary(), false)); tabPrefix = ""; return xml.append("\n</uws:job>").toString(); } @Override public String getJobRef(final UWSJob job, final UWSUrl jobsListUrl){ String url = null; if (jobsListUrl != null){ jobsListUrl.setJobId(job.getJobId()); url = jobsListUrl.getRequestURL(); } StringBuffer xml = new StringBuffer("<uws:jobRef id=\""); xml.append(escapeXMLAttribute(job.getJobId())); if (job.getRunId() != null && job.getRunId().length() > 0) xml.append("\" runId=\"").append(escapeXMLAttribute(job.getRunId())); xml.append("\" xlink:href=\""); if (url != null) xml.append(escapeURL(url)); xml.append("\">").append(getPhase(job, false)).append("</uws:jobRef>"); return xml.toString(); } @Override public String getJobID(final UWSJob job, final boolean root){ return (new StringBuffer(root ? getHeader() : "")).append("<uws:jobId").append(getUWSNamespace(root)).append(">").append(escapeXMLData(job.getJobId())).append("</uws:jobId>").toString(); } @Override public String getRunID(final UWSJob job, final boolean root){ StringBuffer xml = new StringBuffer(root ? getHeader() : ""); xml.append("<uws:runId").append(getUWSNamespace(root)); if (job.getRunId() == null) xml.append(" xsi:nil=\"true\" />"); else xml.append(">").append(escapeXMLData(job.getRunId())).append("</uws:runId>"); return xml.toString(); } @Override public String getOwnerID(final UWSJob job, final boolean root){ StringBuffer xml = new StringBuffer(root ? getHeader() : ""); xml.append("<uws:ownerId").append(getUWSNamespace(root)); if (job.getOwner() == null) xml.append(" xsi:nil=\"true\" />"); else xml.append(">").append(escapeXMLData(job.getOwner().getPseudo())).append("</uws:ownerId>"); return xml.toString(); } @Override public String getPhase(final UWSJob job, final boolean root){ return (new StringBuffer(root ? getHeader() : "")).append("<uws:phase").append(getUWSNamespace(root)).append(">").append(job.getPhase()).append("</uws:phase>").toString(); } @Override public String getQuote(final UWSJob job, final boolean root){ StringBuffer xml = new StringBuffer(root ? getHeader() : ""); xml.append("<uws:quote").append(getUWSNamespace(root)); if (job.getQuote() <= 0) xml.append(" xsi:nil=\"true\" />"); else xml.append(">").append(job.getQuote()).append("</uws:quote>"); return xml.toString(); } @Override public String getStartTime(final UWSJob job, final boolean root){ StringBuffer xml = new StringBuffer(root ? getHeader() : ""); xml.append("<uws:startTime").append(getUWSNamespace(root)); if (job.getStartTime() == null) xml.append(" xsi:nil=\"true\" />"); else xml.append(">").append(UWSJob.dateFormat.format(job.getStartTime())).append("</uws:startTime>"); return xml.toString(); } @Override public String getEndTime(final UWSJob job, final boolean root){ StringBuffer xml = new StringBuffer(root ? getHeader() : ""); xml.append("<uws:endTime").append(getUWSNamespace(root)); if (job.getEndTime() == null) xml.append(" xsi:nil=\"true\" />"); else xml.append(">").append(UWSJob.dateFormat.format(job.getEndTime())).append("</uws:endTime>"); return xml.toString(); } @Override public String getDestructionTime(final UWSJob job, final boolean root){ StringBuffer xml = new StringBuffer(root ? getHeader() : ""); xml.append("<uws:destruction").append(getUWSNamespace(root)); if (job.getDestructionTime() == null) xml.append(" xsi:nil=\"true\" />"); else xml.append(">").append(UWSJob.dateFormat.format(job.getDestructionTime())).append("</uws:destruction>"); return xml.toString(); } @Override public String getExecutionDuration(final UWSJob job, final boolean root){ return (new StringBuffer(root ? getHeader() : "")).append("<uws:executionDuration").append(getUWSNamespace(root)).append(">").append(job.getExecutionDuration()).append("</uws:executionDuration>").toString(); } @Override public String getErrorSummary(final ErrorSummary error, final boolean root){ StringBuffer xml = new StringBuffer(root ? getHeader() : ""); xml.append(tabPrefix).append("<uws:errorSummary").append(getUWSNamespace(root)); if (error != null){ xml.append(" type=\"").append(error.getType()).append("\"").append(" hasDetail=\"").append(error.hasDetail()).append("\">"); xml.append("\n\t").append(tabPrefix).append("<uws:message>").append(escapeXMLData(error.getMessage())).append("</uws:message>"); xml.append("\n").append(tabPrefix).append("</uws:errorSummary>"); }else xml.append(" xsi:nil=\"true\" />"); return xml.toString(); } @Override public String getAdditionalParameters(final UWSJob job, final boolean root){ StringBuffer xml = new StringBuffer(root ? getHeader() : ""); xml.append(tabPrefix).append("<uws:parameters").append(getUWSNamespace(root)).append(">"); String newLine = "\n\t" + tabPrefix; for(String paramName : job.getAdditionalParameters()) xml.append(newLine).append(getAdditionalParameter(paramName, job.getAdditionalParameterValue(paramName), false)); xml.append("\n").append(tabPrefix).append("</uws:parameters>"); return xml.toString(); } @Override public String getAdditionalParameter(final String paramName, final Object paramValue, final boolean root){ if (paramName != null && paramValue != null){ if (root) return paramValue.toString(); else return (new StringBuffer("<uws:parameter")).append(getUWSNamespace(root)).append(" id=\"").append(escapeXMLAttribute(paramName)).append("\">").append(escapeXMLData(paramValue.toString())).append("</uws:parameter>").toString(); }else return ""; } @Override public String getResults(final UWSJob job, final boolean root){ StringBuffer xml = new StringBuffer(root ? getHeader() : ""); xml.append(tabPrefix).append("<uws:results").append(getUWSNamespace(root)).append(">"); Iterator<Result> it = job.getResults(); String newLine = "\n\t" + tabPrefix; while(it.hasNext()) xml.append(newLine).append(getResult(it.next(), false)); xml.append("\n").append(tabPrefix).append("</uws:results>"); return xml.toString(); } @Override public String getResult(final Result result, final boolean root){ StringBuffer xml = new StringBuffer(root ? getHeader() : ""); xml.append("<uws:result").append(getUWSNamespace(root)).append(" id=\"").append(escapeXMLAttribute(result.getId())).append("\""); if (result.getHref() != null){ if (result.getType() != null) xml.append(" xlink:type=\"").append(escapeXMLAttribute(result.getType())).append("\""); xml.append(" xlink:href=\"").append(escapeURL(result.getHref())).append("\""); } if (result.getMimeType() != null) xml.append(" mime=\"").append(escapeXMLAttribute(result.getMimeType())).append("\""); if (result.getSize() >= 0) xml.append(" size=\"").append(result.getSize()).append("\""); return xml.append(" />").toString(); } /* ************** */ /* ESCAPE METHODS */ /* ************** */ /** * <p>Escapes the content of a node (data between the open and the close tags).</p> * * <p><i>By default: surrounds the given data by "<![CDATA[" and "]]>".</i></p> * * @param data Data to escape. * * @return Escaped data. */ public static String escapeXMLData(final String data){ return "<![CDATA[" + data + "]]>"; } /** * Escapes the given value of an XML attribute. * * @param value Value of an XML attribute. * * @return The escaped value. */ public static String escapeXMLAttribute(final String value){ StringBuffer encoded = new StringBuffer(); for(int i = 0; i < value.length(); i++){ char c = value.charAt(i); switch(c){ case '&': encoded.append("&"); break; case '<': encoded.append("<"); break; case '>': encoded.append(">"); break; case '"': encoded.append("""); break; case '\'': encoded.append("'"); break; default: encoded.append(c); } } return encoded.toString(); } /** * Escapes the given URL. * * @param url URL to escape. * * @return The escaped URL. * * @see URLEncoder * @see #escapeXMLAttribute(String) */ public static String escapeURL(final String url){ try{ return URLEncoder.encode(url, "UTF-8"); }catch(UnsupportedEncodingException e){ return escapeXMLAttribute(url); } } }