001/* 002 * Copyright (c) 2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2018-01-15 20:27:04 +0000 (Mon, 15 Jan 2018) $' 007 * '$Revision: 34649 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.kar.karxml; 031 032import java.io.File; 033import java.io.IOException; 034import java.io.InputStream; 035import java.io.StringReader; 036import java.util.Iterator; 037import java.util.jar.Attributes; 038import java.util.jar.Attributes.Name; 039import java.util.jar.Manifest; 040import java.util.regex.Pattern; 041import java.util.zip.ZipEntry; 042 043import org.apache.commons.logging.Log; 044import org.apache.commons.logging.LogFactory; 045import org.kepler.configuration.ConfigurationProperty; 046import org.kepler.kar.KAREntry; 047import org.kepler.kar.KARFile; 048import org.kepler.util.FileUtil; 049import org.w3c.dom.Document; 050import org.xml.sax.SAXException; 051 052/** 053 * The KarXmlGenerator class reads in a KARFile and generates an xml string that 054 * can be used to upload the KAR file to Metacat. 055 * 056 * @author Aaron Schultz 057 */ 058public class KarXmlGenerator { 059 060 private static final Log log = LogFactory.getLog(KarXmlGenerator.class 061 .getName()); 062 private static final boolean isDebugging = log.isDebugEnabled(); 063 private String _schemaUrl = null; 064 private String _XSNamespace = "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""; 065 private String _namespace = null; 066 private String _schemaLocation = null; 067 private Name _manifestVersion = new Name("Manifest-Version"); 068 069 private KARFile _karFile; 070 private StringBuffer karxml; 071 private boolean hasReportLayout = false; 072 073 public static final String nl = System.getProperty("line.separator"); 074 public static final String tab = "\t"; 075 076 /** 077 * Empty Constructor 078 */ 079 public KarXmlGenerator() { 080 } 081 082 /** 083 * Constructor that takes a KARFile as a parameter. This will be the KARFile 084 * read in during the call to getKarXml() 085 * 086 * @param karFile 087 */ 088 public KarXmlGenerator(KARFile karFile) { 089 setKarFile(karFile); 090 } 091 092 /** 093 * Set the KARFile object to generate the karxml from. 094 * 095 * @param karFile 096 * @return 097 */ 098 public void setKarFile(KARFile karFile) { 099 _karFile = karFile; 100 setKARVersionSpecificInfo(_karFile.getKARVersion()); 101 } 102 103 /** 104 * Set _namespace, _schemaUrl, and _schemaLocation for KAR version 105 * @param karFileVersion 106 */ 107 public void setKARVersionSpecificInfo(String karFileVersion) { 108 109 ConfigurationProperty KARVersionsProp = KARFile 110 .getKARVersionsConfigProperty(); 111 112 Iterator<ConfigurationProperty> karVersionsItr = KARVersionsProp 113 .getProperties().iterator(); 114 while (karVersionsItr.hasNext()) { 115 ConfigurationProperty prop = karVersionsItr.next(); 116 if (prop.getName().equals(KARFile.KAR_VERSION_PROPERTY_NAME)) { 117 ConfigurationProperty version = prop.getProperty(KARFile.KAR_VERSION_VERSION_PROPERTY_NAME); 118 if (version.getValue().equals(karFileVersion)) { 119 ConfigurationProperty namespaceConfigProp = prop 120 .getProperty(KARFile.KAR_VERSION_NAMESPACE_PROPERTY_NAME); 121 ConfigurationProperty schemaUrlConfigProp = prop 122 .getProperty(KARFile.KAR_VERSION_SCHEMAURL_PROPERTY_NAME); 123 _namespace = namespaceConfigProp.getValue(); 124 _schemaUrl = schemaUrlConfigProp.getValue(); 125 _schemaLocation = "xsi:schemaLocation=\"" + _namespace 126 + " " + _schemaUrl + "\""; 127 } 128 } 129 } 130 131 } 132 133 /** 134 * Get an XML file representation for a KARFile. Pass in the KARFile to be 135 * used while generating the karxml. 136 * 137 * @param karFile 138 * @return 139 * @throws IOException 140 */ 141 public String getKarXml(KARFile karFile) throws IOException, SAXException{ 142 setKarFile(karFile); 143 String xml = getKarXml(); 144 return xml; 145 } 146 147 /** 148 * Get an XML file representation for a KARFile. Use the setKarFile(KARFile) 149 * method before calling this method. 150 * 151 * @return 152 * @throws IOException 153 */ 154 public String getKarXml() throws IOException, SAXException { 155 karxml = new StringBuffer(); 156 karxml.append("<?xml version=\"1.0\"?>" + nl); 157 karxml.append("<kar:kar xmlns:kar=\""+_namespace+"\""+" "+_XSNamespace+" "+_schemaLocation+">" 158 + nl); 159 160 // Adding this for Sean Riddle to facilitate using the kar filename 161 // in remote search results 162 File f = _karFile.getFileLocation(); 163 String filename = f.getName(); 164 karxml.append(tab + "<karFileName>" + nl); 165 karxml.append(tab + tab + filename + nl); 166 karxml.append(tab + "</karFileName>" + nl); 167 168 long fileSize = f.length(); 169 karxml.append(tab + "<karFileSize>" + nl); 170 karxml.append(tab + tab + fileSize + nl); 171 karxml.append(tab + "</karFileSize>" + nl); 172 173 appendXmlForMainAttributes(); 174 175 for (KAREntry entry : _karFile.karEntries()) { 176 if(entry != null && entry.isReportLayout()){ 177 hasReportLayout = true; 178 } 179 karxml.append(tab + "<karEntry>" + nl); 180 appendXmlForEntryAttributes(entry); 181 appendXmlFor(entry); 182 karxml.append(tab + "</karEntry>" + nl); 183 } 184 185 karxml.append("</kar:kar>" + nl); 186 String karXmlStr = karxml.toString(); 187 validateKarXml(karXmlStr); 188 return karXmlStr; 189 } 190 191 /** 192 * Determine if the generated kar xml has a report layout. 193 * This method can only be called after calling getKarXml() 194 * @return 195 */ 196 public boolean hasReportLayout(){ 197 return hasReportLayout; 198 } 199 200 /* 201 * validate the generated kar xml 202 */ 203 private void validateKarXml(String xml) throws SAXException 204 { 205 if(xml != null) 206 { 207 Document document = KarXml.parseXml(new StringReader(xml)); 208 if(document == null) 209 { 210 throw new SAXException("Kepler couldn't transform the generated kar xml to a DOM tree model. So the generated kar xml is not valid"); 211 } 212 else 213 { 214 boolean isValid = KarXml.validateDocument(document); 215 if(!isValid) 216 { 217 throw new SAXException("The generated kar xml is not valid"); 218 } 219 } 220 } 221 else 222 { 223 throw new SAXException("The generated kar xml is null and it is invalid"); 224 } 225 } 226 227 /** 228 * Read in the main attributes of the KAR and append them to the xml. 229 * 230 * @throws IOException 231 */ 232 private void appendXmlForMainAttributes() throws IOException { 233 234 karxml.append(tab + "<mainAttributes>" + nl); 235 236 // gets the manifest 237 Manifest mf = _karFile.getManifest(); 238 239 // gets the main attributes in the manifest 240 Attributes atts = mf.getMainAttributes(); 241 if(atts != null){ 242 String lsid = atts.getValue(KARFile.LSID); 243 if(lsid != null){ 244 karxml.append(tab + tab + "<" + KARFile.LSID + ">" + nl); 245 karxml.append(tab + tab + tab + lsid + nl); 246 karxml.append(tab + tab + "</" + KARFile.LSID + ">" + nl); 247 } 248 String moduleDependencies = atts.getValue(KARFile.MOD_DEPEND); 249 if(moduleDependencies != null){ 250 karxml.append(tab + tab + "<" + KARFile.MOD_DEPEND + ">" + nl); 251 karxml.append(tab + tab + tab + moduleDependencies + nl); 252 karxml.append(tab + tab + "</" + KARFile.MOD_DEPEND + ">" + nl); 253 } 254 String karVersion = atts.getValue(KARFile.KAR_VERSION); 255 if(karVersion != null){ 256 karxml.append(tab + tab + "<" + KARFile.KAR_VERSION + ">" + nl); 257 karxml.append(tab + tab + tab + karVersion + nl); 258 karxml.append(tab + tab + "</" + KARFile.KAR_VERSION + ">" + nl); 259 } 260 String manifestVersion = atts.getValue(_manifestVersion); 261 if(manifestVersion != null){ 262 karxml.append(tab + tab + "<" + _manifestVersion + ">" + nl); 263 karxml.append(tab + tab + tab + manifestVersion + nl); 264 karxml.append(tab + tab + "</" + _manifestVersion + ">" + nl); 265 } 266 } 267 // Loop through the attributes 268 /*for (Object att : atts.keySet()) { 269 270 if (att instanceof Name) { 271 Name attrName = (Name) att; 272 String attrValue = atts.getValue(attrName); 273 274 karxml.append(tab + tab + "<" + attrName + ">" + nl); 275 karxml.append(tab + tab + tab + attrValue + nl); 276 karxml.append(tab + tab + "</" + attrName + ">" + nl); 277 } else { 278 throw new IOException("Unrecognized Main Attribute"); 279 } 280 }*/ 281 282 karxml.append(tab + "</mainAttributes>" + nl); 283 //System.out.println("karxml is ==========\n "+karxml.toString()); 284 } 285 286 /** 287 * Read in all attributes for the entry and append them to the xml. 288 * 289 * @param entry 290 */ 291 private void appendXmlForEntryAttributes(KAREntry entry) { 292 293 karxml.append(tab + tab + "<karEntryAttributes>" + nl); 294 295 // the name of a KAREntry is not found in it's attributes 296 // must get it directly 297 String entryName = entry.getName(); 298 karxml.append(tab + tab + tab + "<Name>" + nl); 299 karxml.append(tab + tab + tab + tab + entryName + nl); 300 karxml.append(tab + tab + tab + "</Name>" + nl); 301 302 Attributes atts = entry.getAttributes(); 303 304 for (Object att : atts.keySet()) { 305 // System.out.println( att.toString() ); 306 if (att instanceof Name) { 307 308 Name attrName = (Name) att; 309 String value = atts.getValue(attrName); 310 311 karxml.append(tab + tab + tab + "<" + attrName + ">" + nl); 312 karxml.append(tab + tab + tab + tab + value + nl); 313 karxml.append(tab + tab + tab + "</" + attrName + ">" + nl); 314 315 } 316 } 317 318 karxml.append(tab + tab + "</karEntryAttributes>" + nl); 319 320 } 321 322 /** 323 * Decide what to do depending on what kind of entry this is. 324 * 325 * @param entry 326 * @throws IOException 327 */ 328 private void appendXmlFor(KAREntry entry) throws IOException { 329 330 if (isXmlEntry(entry)) { 331 appendXmlEntryContents(entry); 332 } else { 333 // maybe at some point we'll want to include this 334 // for now, only include the kar entry attributes 335 // for generic (aka non-xml) files 336 // appendGenericEntryXml(entry); 337 } 338 339 } 340 341 /** 342 * Checks to see if the entry ends with the xml extension. This method is 343 * case insensitive. 344 * 345 * @param entry 346 * @return 347 */ 348 private boolean isXmlEntry(KAREntry entry) { 349 350 String entryName = entry.getName(); 351 String lowerCaseEntryName = entryName.toLowerCase(); 352 if (lowerCaseEntryName.endsWith(".xml")) 353 return true; 354 355 return false; 356 } 357 358 /** 359 * For an XML file we convert the contents to a string and append it. 360 * 361 * @param entry 362 * @throws IOException 363 */ 364 private void appendXmlEntryContents(KAREntry entry) throws IOException { 365 karxml.append(tab + tab + "<karEntryXML>" + nl); 366 367 InputStream is = _karFile.getInputStream((ZipEntry) entry); 368 369 String contents = FileUtil.convertStreamToString(is); 370 contents = processXmlEntryContents(contents); 371 372 karxml.append(contents + nl); 373 374 karxml.append(tab + tab + "</karEntryXML>" + nl); 375 } 376 377 /** 378 * Strip out the "<?xml" tag from the xml. Also removes a header DOCTYPE, if present. 379 * 380 * @param xml 381 * @return 382 */ 383 public String processXmlEntryContents(String xml) { 384 385 xml = xml.replaceAll("<\\?xml.*?>", ""); 386 xml = Pattern.compile("<!DOCTYPE.*?>", Pattern.DOTALL).matcher(xml).replaceAll(""); 387 return xml; 388 } 389 390 /** 391 * For a file that is not XML maybe we want to add something here. Or maybe 392 * the attributes for the entry are enough. 393 * 394 * @param entry 395 * 396 * private void appendGenericEntryXml(KAREntry entry) { 397 * 398 * karxml.append(tab + tab + "<genericKarEntry>" + nl); 399 * 400 * karxml.append(tab + tab + "</genericKarEntry>" + nl); } 401 */ 402 403}