org.netbeans.spi.settings.DOMConvertor Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.spi.settings;
import java.io.IOException;
import java.util.Map;
import org.openide.filesystems.FileObject;
import org.openide.util.Lookup;
import org.openide.xml.XMLUtil;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/** DOMConvertor extends the Convertor to allow to compose output of several
* convertors into one xml document. For more info see
* Composed Content.
*
* @author Jan Pokorsky
* @since 1.1
*/
public abstract class DOMConvertor extends Convertor {
/** an attribute containing public ID of DTD describing nested elements */
private final static String ATTR_PUBLIC_ID = "dtd_public_id"; // NOI18N
private final static String ATTR_ID = "id"; // NOI18N
private final static String ATTR_IDREF = "idref"; // NOI18N
/** element used to wrap output from non-DOMConvertor or to reference
* already written object.
* @see #ATTR_ID
* @see #ATTR_IDREF
*/
// Usage:
// <[!CDATA[...]]>
// or
// ...
// ...
// ...
//
private final static String ELM_DELEGATE = "domconvertor"; // NOI18N
private final static java.util.Map> refsCache =
new java.util.HashMap>();
/** cache of contexts */
private final static java.util.Map ctxCache = new java.util.HashMap();
private String publicID;
private String systemID;
private String rootElement;
/** Creat a DOMConvertor
* @param publicID public ID of DOCTYPE
* @param systemID system ID of DOCTYPE
* @param rootElement qualified name of root element
*/
protected DOMConvertor(String publicID, String systemID, String rootElement) {
this.publicID = publicID;
this.systemID = systemID;
this.rootElement = rootElement;
if (publicID == null) throw new NullPointerException("publicID"); // NOI18N
if (systemID == null) throw new NullPointerException("systemID"); // NOI18N
if (rootElement == null) throw new NullPointerException("rootElement"); // NOI18N
}
/** Read content from r
and delegate to {@link #readElement}
* passing parsed content as a root element of DOM document
* @param r stream containing stored object
* @return the read setting object
* @exception IOException if the object cannot be read
* @exception ClassNotFoundException if the object class cannot be resolved
* @since 1.1
*/
public final Object read(java.io.Reader r) throws java.io.IOException, ClassNotFoundException {
Document doc = null;
try {
InputSource is = new InputSource(r);
doc = XMLUtil.parse(is, false, false, null, org.openide.xml.EntityCatalog.getDefault());
setDocumentContext(doc, findContext(r));
return readElement(doc.getDocumentElement());
} catch (SAXException ex) {
IOException ioe = new IOException(ex.getLocalizedMessage());
ioe.initCause(ex);
throw ioe;
} finally {
if (doc != null) {
clearCashesForDocument(doc);
}
}
}
/** Write object described by DOM document filled by {@link #writeElement}
* @param w stream into which inst is written
* @param inst the setting object to be written
* @exception IOException if the object cannot be written
* @since 1.1
*/
public final void write(java.io.Writer w, Object inst) throws java.io.IOException {
Document doc = null;
try {
doc = XMLUtil.createDocument(rootElement, null, publicID, systemID);
setDocumentContext(doc, findContext(w));
writeElement(doc, doc.getDocumentElement(), inst);
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(1024);
XMLUtil.write(doc, baos, "UTF-8"); // NOI18N
w.write(baos.toString("UTF-8")); // NOI18N
} catch (org.w3c.dom.DOMException ex) {
IOException e = new IOException(ex.getLocalizedMessage());
e.initCause(ex);
throw e;
} finally {
if (doc != null) {
clearCashesForDocument(doc);
}
}
}
/** Provide an object constructed from the element.
* @param element represents a read object in a DOM document
* @return an setting object
* @exception IOException if the object cannot be read
* @exception ClassNotFoundException if the object class cannot be resolved
* @since 1.1
*/
protected abstract Object readElement(org.w3c.dom.Element element) throws java.io.IOException, ClassNotFoundException;
/** Fill a DOM element describing obj
with attributes or subelements.
* @param doc a DOM document allowing to create elements describing passed object
* @param element represents a written object in a DOM document
* @param obj an object to convert
* @exception IOException if the object cannot be written
* @exception org.w3c.dom.DOMException if an element construction failed
* @since 1.1
*/
protected abstract void writeElement(
org.w3c.dom.Document doc,
org.w3c.dom.Element element,
Object obj) throws java.io.IOException, org.w3c.dom.DOMException;
/** delegate the read operation to a convertor referenced by dtd_public_id
* @param element DOM element that should be read
* @return an setting object
* @exception IOException if the object cannot be read
* @exception ClassNotFoundException if the object class cannot be resolved
* @see #readElement
* @since 1.1
*/
protected final static Object delegateRead(org.w3c.dom.Element element) throws java.io.IOException, ClassNotFoundException {
Object obj;
// in case of a reference a cache of already read objects should
// be consulted instead of delegating
String idref = element.getAttribute(ATTR_IDREF);
if (idref.length() != 0) {
obj = getCache(element.getOwnerDocument(), idref.intern());
if (obj != null) {
return obj;
} else {
throw new IOException("broken reference: " + element + ", idref=" + idref); // NOI18N
}
}
// lookup convertor
String publicId = element.getAttribute(ATTR_PUBLIC_ID);
Convertor c = ConvertorResolver.getDefault().getConvertor(publicId);
if (c == null) throw new IOException("Convertor not found. publicId: " + publicId); // NOI18N
// read
if (element.getTagName().equals(ELM_DELEGATE)) {
// read CDATA block
org.w3c.dom.NodeList children = element.getChildNodes();
String content = null;
for (int i = 0, size = children.getLength(); i < size; i++) {
org.w3c.dom.Node n = children.item(i);
if (n.getNodeType() == org.w3c.dom.Node.CDATA_SECTION_NODE) {
content = n.getNodeValue();
break;
} else if (n.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {
// #62018: some serializers may not produce CDATA.
String text = n.getNodeValue().trim();
if (text.length() > 0) {
content = text;
break;
}
}
}
if (content == null) {
throw new IOException("Expected CDATA block under: " + // NOI18N
element.getTagName());
}
obj = readFromString(c, content, findContext(element.getOwnerDocument()));
} else if (c instanceof DOMConvertor) {
DOMConvertor dc = (DOMConvertor) c;
obj = dc.readElement(element);
} else {
throw new IOException(
"Missing DOMConvertor for publicId: " + publicId); // NOI18N
}
// cache reference
String id = element.getAttribute(ATTR_ID);
if (id.length() != 0) {
setCache(element.getOwnerDocument(), id, obj);
}
return obj;
}
/** delegate the write operation to a convertor able to write obj.
* @param doc a DOM document allowing to create elements describing passed object
* @param obj an object to convert
* @return a DOM element representation
* @exception IOException if the object cannot be written
* @exception org.w3c.dom.DOMException if an element construction failed
* @see #writeElement
* @since 1.1
*/
protected final static org.w3c.dom.Element delegateWrite(org.w3c.dom.Document doc, Object obj) throws java.io.IOException, org.w3c.dom.DOMException {
// first lookup a cache of already written objects to prevent
// storing of the same instance multiple times.
CacheRec cache = setCache(doc, obj);
if (cache.used) {
return writeReference(doc, cache);
}
ConvertorResolver res = ConvertorResolver.getDefault();
Class> clazz = obj.getClass();
Convertor c = res.getConvertor(clazz);
if (c == null) {
throw new IOException("Convertor not found for object: " + obj); // NOI18N
}
org.w3c.dom.Element el;
if (c instanceof DOMConvertor) {
DOMConvertor dc = (DOMConvertor) c;
el = doc.createElement(dc.rootElement);
dc.writeElement(doc, el, obj);
if (el.getAttribute(ATTR_PUBLIC_ID).length() == 0) {
el.setAttribute(ATTR_PUBLIC_ID, res.getPublicID(clazz));
}
} else {
// plain convertor -> wrap content to CDATA block
el = doc.createElement(ELM_DELEGATE);
el.setAttribute(ATTR_PUBLIC_ID, res.getPublicID(clazz));
el.appendChild(doc.createCDATASection(writeToString(c, obj, findContext(doc))));
}
// bind cached object with original element
cache.elm = el;
return el;
}
/** get a context associated with the document doc
. It can
* contain various info like a file location of the read document etc.
* @param doc a DOM document containing stored object
* @return a context associated with the document
* @since 1.2
*/
protected static org.openide.util.Lookup findContext(org.w3c.dom.Document doc) {
synchronized (ctxCache) {
Lookup ctx = ctxCache.get(doc);
return ctx == null? Lookup.EMPTY: ctx;
}
}
// private impl //////////////////////////////////////////////////////////////
/** remember context for document */
private static void setDocumentContext(Document doc, Lookup ctx) {
synchronized (ctxCache) {
ctxCache.put(doc, ctx);
}
}
/** write an object obj to String using Convertor.write() */
private static String writeToString(Convertor c, Object obj, Lookup ctx) throws IOException {
java.io.Writer caw = new java.io.CharArrayWriter(1024);
java.io.Writer w = caw;
FileObject fo = (FileObject) ctx.lookup(FileObject.class);
if (fo != null) {
w = org.netbeans.modules.settings.ContextProvider.createWriterContextProvider(caw, fo);
}
c.write(w, obj);
w.close();
return caw.toString();
}
/** read an object from String using Convertor.read() */
private static Object readFromString(Convertor c, String s, Lookup ctx) throws IOException, ClassNotFoundException {
java.io.Reader r = new java.io.StringReader(s);
FileObject fo = (FileObject) ctx.lookup(FileObject.class);
if (fo != null) {
r = org.netbeans.modules.settings.ContextProvider.createReaderContextProvider(r, fo);
}
return c.read(r);
}
/** create an element referencing an already stored object */
private static org.w3c.dom.Element writeReference(org.w3c.dom.Document doc, CacheRec cache) throws org.w3c.dom.DOMException {
org.w3c.dom.Element el = doc.createElement(cache.elm.getTagName());
el.setAttribute(ATTR_IDREF, (String) cache.value);
cache.elm.setAttribute(ATTR_ID, (String) cache.value);
return el;
}
/** remember an object obj being stored in document key and generate its ID
* in scope of the document. Used during write operations.
*/
private static CacheRec setCache(org.w3c.dom.Document key, Object obj) {
synchronized (refsCache) {
Map
© 2015 - 2025 Weber Informatics LLC | Privacy Policy