/* * Copyright 2014, The Sporting Exchange Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.betfair.cougar.marshalling.impl.databinding.xml; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.net.URI; import java.util.HashMap; import java.util.Map; import javax.xml.bind.JAXBContext; import javax.xml.bind.SchemaOutputResolver; import javax.xml.transform.Result; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.apache.commons.io.FileUtils; import org.xml.sax.SAXException; import com.betfair.cougar.core.api.exception.PanicInTheCougar; public class XMLUtils { public static Schema getSchema(JAXBContext context) { Result result = new ResultImplementation(); SchemaOutputResolver outputResolver = new SchemaOutputResolverImpl(result); File tempFile = null; try { context.generateSchema(outputResolver); String schemaFile = result.getSystemId(); if (schemaFile != null) { tempFile = new File(URI.create(schemaFile)); String content = FileUtils.readFileToString(tempFile); // JAXB nicely generates a schema with element refs, unfortunately it also adds the nillable attribute to the // referencing element, which is invalid, it has to go on the target. this string manipulation is to move the // nillable attribute to the correct place, preserving it's value. Map<String, Boolean> referenceElementsWithNillable = new HashMap<>(); BufferedReader br = new BufferedReader(new StringReader(content)); String line; while ((line = br.readLine()) != null) { if (line.contains("<xs:element") && line.contains(" ref=\"") && line.contains(" nillable=\"")) { // we've got a reference element with nillable set int refStartIndex = line.indexOf(" ref=\"") + 6; int refEndIndex = line.indexOf("\"", refStartIndex); int nillableStartIndex = line.indexOf(" nillable=\"") + 11; int nillableEndIndex = line.indexOf("\"",nillableStartIndex); String ref = line.substring(refStartIndex, refEndIndex); if (ref.startsWith("tns:")) { ref = ref.substring(4); } String nillable = line.substring(nillableStartIndex, nillableEndIndex); referenceElementsWithNillable.put(ref, Boolean.valueOf(nillable)); } } // if we got some hits, then we need to rewrite this schema if (!referenceElementsWithNillable.isEmpty()) { StringBuilder sb = new StringBuilder(); br = new BufferedReader(new StringReader(content)); while ((line = br.readLine()) != null) { // these we need to remove the nillable section from if (line.contains("<xs:element") && line.contains(" ref=\"") && line.contains(" nillable=\"")) { int nillableStartIndex = line.indexOf(" nillable=\""); int nillableEndIndex = line.indexOf("\"",nillableStartIndex+11); line = line.substring(0, nillableStartIndex) + line.substring(nillableEndIndex+1); } else if (line.contains("<xs:element name=\"")) { for (String key : referenceElementsWithNillable.keySet()) { // this we need to add the nillable back to String elementTagWithName = "<xs:element name=\""+key+"\""; if (line.contains(elementTagWithName)) { int endOfElementName = line.indexOf(elementTagWithName) + elementTagWithName.length(); line = line.substring(0, endOfElementName) + " nillable=\"" + referenceElementsWithNillable.get(key) + "\"" + line.substring(endOfElementName); break; } } } sb.append(line).append("\n"); } content = sb.toString(); } SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI); return sf.newSchema(new StreamSource(new StringReader(content))); } } catch (IOException e) { throw new PanicInTheCougar(e); } catch (SAXException e) { throw new PanicInTheCougar(e); } finally { if(tempFile!=null)tempFile.delete(); } return null; } private static final class SchemaOutputResolverImpl extends SchemaOutputResolver { private final Result result; public SchemaOutputResolverImpl(Result result) { this.result = result; } @Override public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException { File schemaFile = File.createTempFile(suggestedFileName, "xsd"); result.setSystemId(schemaFile.toURI().toString()); return result; } } private static final class ResultImplementation extends StreamResult implements Result { private String systemId; @Override public String getSystemId() { return systemId; } @Override public void setSystemId(String systemId) { this.systemId = systemId; } } }