com.betfair.cougar.marshalling.impl.databinding.xml.XMLUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.betfair.cougar.marshalling.impl.databinding.xml.XMLUtils.java

Source

/*
 * 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;
        }
    }

}