/****************************************************************************** * Copyright (c) 2016 Oracle * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Konstantin Komissarchik - initial implementation and ongoing maintenance ******************************************************************************/ package org.eclipse.sapphire.modeling.xml; import static java.nio.charset.StandardCharsets.UTF_8; import static org.eclipse.sapphire.modeling.xml.XmlUtil.XML_DECLARATION; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.util.Locale; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.eclipse.sapphire.Element; import org.eclipse.sapphire.MasterConversionService; import org.eclipse.sapphire.Sapphire; import org.eclipse.sapphire.modeling.ByteArrayResourceStore; import org.eclipse.sapphire.modeling.FileResourceStore; import org.eclipse.sapphire.modeling.ResourceStore; import org.eclipse.sapphire.modeling.ResourceStoreException; import org.eclipse.sapphire.modeling.localization.LocalizationService; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; import org.w3c.dom.Node; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a> */ public class XmlResourceStore extends ResourceStore { private final ByteArrayResourceStore base; private Document document; public XmlResourceStore( final ByteArrayResourceStore baseModelStore ) { this.base = baseModelStore; this.document = null; if( this.base != null ) { try { byte[] contents = this.base.getContents(); if( contents.length > 0 ) { try( Reader reader = new InputStreamReader( new ByteArrayInputStream( contents ), UTF_8 ) ) { this.document = doc( reader ); } } } catch( Exception e ) { // Do nothing. } if( this.document == null ) { this.document = doc(); } this.document.setStrictErrorChecking( false ); } } public XmlResourceStore( final byte[] contents ) { this( new ByteArrayResourceStore( contents ) ); } public XmlResourceStore( final String contents ) { this( new ByteArrayResourceStore( contents ) ); } public XmlResourceStore( final InputStream contents ) throws ResourceStoreException { this( new ByteArrayResourceStore( contents ) ); } public XmlResourceStore( final File file ) throws ResourceStoreException { this( new FileResourceStore( file ) ); } public XmlResourceStore() { this( new byte[ 0 ] ); } public Document getDomDocument() { return this.document; } protected void setDomDocument( final Document document ) { this.document = document; } public boolean isXmlDeclarationNeeded() { return false; } @Override public <A> A adapt( final Class<A> adapterType ) { A result = Sapphire.service( MasterConversionService.class ).convert( this, adapterType ); if( result == null && this.base != null ) { result = this.base.adapt( adapterType ); } return result; } @Override public void save() throws ResourceStoreException { validateSave(); try { if( this.document.getDocumentElement() != null ) { final StringWriter sw = new StringWriter(); sw.append( XML_DECLARATION ); sw.append( '\n' ); final DOMSource source = new DOMSource( this.document ); final StreamResult result = new StreamResult( sw ); final TransformerFactory factory = TransformerFactory.newInstance(); final Transformer transformer = factory.newTransformer(); transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" ); final DocumentType doctype = this.document.getDoctype(); if (doctype != null) { if (doctype.getPublicId() != null) { transformer.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC, doctype.getPublicId()); } if (doctype.getSystemId() != null) { transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, doctype.getSystemId()); } } transformer.transform( source, result ); this.base.setContents( sw.toString().getBytes( UTF_8 ) ); } else { this.base.setContents( new byte[ 0 ] ); } this.base.save(); } catch( Exception e ) { throw new ResourceStoreException( e ); } } @Override public void validateEdit() { this.base.validateEdit(); } @Override public void validateSave() { this.base.validateSave(); } @Override public boolean isOutOfDate() { return this.base.isOutOfDate(); } @Override protected LocalizationService initLocalizationService( final Locale locale ) { return this.base.getLocalizationService( locale ); } public void registerRootModelElement( final Element rootModelElement ) { // The default implementation doesn't do anything. } public void registerModelElement( final Node xmlNode, final Element modelElement ) { // The default implementation doesn't do anything. } public void unregisterModelElement( final Node xmlNode, final Element modelElement ) { // The default implementation doesn't do anything. } private static Document doc( final Reader r ) { final DocumentBuilder docbuilder = docbuilder(); try { return docbuilder.parse( new InputSource( r ) ); } catch( IOException e ) { throw new RuntimeException( e ); } catch( SAXParseException e ) { throw new RuntimeException( e ); } catch( SAXException e ) { throw new RuntimeException( e ); } } private static Document doc() { return docbuilder().newDocument(); } private static DocumentBuilder docbuilder() { try { final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating( false ); factory.setNamespaceAware( true ); factory.setIgnoringComments( false ); final DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver ( new EntityResolver() { public InputSource resolveEntity( final String publicID, final String systemID ) { return new InputSource( new StringReader( "" ) ); } } ); return builder; } catch( ParserConfigurationException e ) { throw new RuntimeException( e ); } } }