/***************************************************************************** * Copyright (c) 2006-2013, Cloudsmith Inc. * The code, documentation and other materials contained herein have been * licensed under the Eclipse Public License - v 1.0 by the copyright holder * listed above, as the Initial Contributor under such license. The text of * such license is available at www.eclipse.org. *****************************************************************************/ package org.eclipse.buckminster.core.common.model; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.eclipse.buckminster.core.XMLConstants; import org.eclipse.buckminster.core.common.parser.DocumentationParser; import org.eclipse.buckminster.core.parser.IParser; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.sax.ISaxable; import org.eclipse.buckminster.sax.ISaxableElement; import org.eclipse.buckminster.sax.Utils; import org.eclipse.core.runtime.CoreException; import org.xml.sax.ContentHandler; import org.xml.sax.SAXException; /** * @author Thomas Hallgren */ public class Documentation extends FlowWithAttributes implements ISaxable { public static final String BM_TAG = "documentation"; //$NON-NLS-1$ public static final String XHTML_TAG = "div"; //$NON-NLS-1$ private static final String LEAD_IN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" //$NON-NLS-1$ + "<div xmlns=\"http://www.w3.org/1999/xhtml\">"; //$NON-NLS-1$ private static final String TAIL = "</div>"; //$NON-NLS-1$ /** * Parse a text snipped into a Documentation object. This method will first * wrap the snipped in a XHTML <div> tag and then parse it using the * {@link DocumentationParser} * * @param doc * The text to be parsed. * @return A documentation object * @throws CoreException */ public static Documentation parse(String doc) throws CoreException { try { ByteArrayInputStream input = new ByteArrayInputStream((LEAD_IN + doc + TAIL).getBytes("UTF-8")); //$NON-NLS-1$ IParser<Documentation> parser = new DocumentationParser(); return parser.parse("generated", input); //$NON-NLS-1$ } catch (Exception e) { throw BuckminsterException.wrap(e); } } public Documentation(ISaxableElement[] children, String[] keyNamePairs) { super(BM_TAG, children, keyNamePairs); } /** * Merge a documentation with another documentation. The new documentation * found in <code>doc</code> is appended to the end of this documentation. * * @param doc * @return The merged documentation. */ public Documentation merge(Documentation doc) { if (doc == null) return this; ISaxableElement[] thisChildren = getChildren(); ISaxableElement[] thatChildren = doc.getChildren(); ISaxableElement[] allChildren = new ISaxableElement[thisChildren.length + thatChildren.length]; System.arraycopy(thisChildren, 0, allChildren, 0, thisChildren.length); System.arraycopy(thatChildren, 0, allChildren, thisChildren.length, thatChildren.length); // Merge the element attributes if necessary, let attributes from doc // have // precedence in case of conflic // String[] thisAttrs = getKeyNamePairs(); String[] thatAttrs = doc.getKeyNamePairs(); String[] allAttrs; if (thisAttrs.length == 0) allAttrs = thatAttrs; else if (thatAttrs.length == 0) allAttrs = thisAttrs; else { HashMap<String, String> attrMap = new HashMap<String, String>(); for (int idx = 0; idx < thisAttrs.length; idx += 2) attrMap.put(thisAttrs[idx], thisAttrs[idx + 1]); for (int idx = 0; idx < thatAttrs.length; idx += 2) attrMap.put(thatAttrs[idx], thatAttrs[idx + 1]); ArrayList<String> attrList = new ArrayList<String>(); for (Map.Entry<String, String> entry : attrMap.entrySet()) { attrList.add(entry.getKey()); attrList.add(entry.getValue()); } allAttrs = attrList.toArray(new String[attrList.size()]); } return new Documentation(allChildren, allAttrs); } /** * Emits the SAX events for a an XML document with the XHTML <div> tag * as the root element. * * @param receiver * The content handler to receive the SAX events * @throws SAXException * if something goes wrong during the event generation */ @Override public void toSax(ContentHandler receiver) throws SAXException { receiver.startDocument(); this.toSax(receiver, XMLConstants.XHTML_NS, null, XHTML_TAG); receiver.endDocument(); } @Override public void toSax(ContentHandler receiver, String namespace, String prefix, String localName) throws SAXException { receiver.startPrefixMapping("", XMLConstants.XHTML_NS); //$NON-NLS-1$ super.toSax(receiver, namespace, prefix, localName); receiver.endPrefixMapping(""); //$NON-NLS-1$ } /** * Returns the XHTML content of top element in the form of a String. The tag * of the top element is not included. */ @Override public String toString() { try { ByteArrayOutputStream out = new ByteArrayOutputStream(); ContentHandler serializer = Utils.newSerializer(null, out, "UTF-8", -1, false); //$NON-NLS-1$ toSax(serializer); String xmlContent = new String(out.toByteArray(), "UTF-8"); //$NON-NLS-1$ int idx = xmlContent.indexOf("<" + XHTML_TAG); //$NON-NLS-1$ if (idx < 0) return null; int top = xmlContent.length(); for (idx += XHTML_TAG.length() + 1; idx < top; ++idx) if (xmlContent.charAt(idx) == '>') break; if (idx == top) return null; ++idx; int lastIdx = xmlContent.lastIndexOf("</" + XHTML_TAG + ">"); //$NON-NLS-1$ //$NON-NLS-2$ if (lastIdx < idx) return null; return xmlContent.substring(idx, lastIdx); } catch (SAXException e) { throw new RuntimeException(e); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } }