org.odftoolkit.odfdom.changes.Component Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of odfdom-java Show documentation
Show all versions of odfdom-java Show documentation
ODFDOM is an OpenDocument Format (ODF) framework. Its purpose
is to provide an easy common way to create, access and
manipulate ODF files, without requiring detailed knowledge of
the ODF specification. It is designed to provide the ODF
developer community with an easy lightwork programming API
portable to any object-oriented language.
The current reference implementation is written in Java.
/*
* Copyright 2012 The Apache Software Foundation.
*
* 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 org.odftoolkit.odfdom.changes;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.odftoolkit.odfdom.dom.element.OdfStylableElement;
import org.odftoolkit.odfdom.dom.element.dr3d.Dr3dSceneElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawCaptionElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawCircleElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawConnectorElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawControlElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawCustomShapeElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawEllipseElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawFrameElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawGElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawLineElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawPageThumbnailElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawPathElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawPolygonElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawPolylineElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawRectElement;
import org.odftoolkit.odfdom.dom.element.draw.DrawRegularPolygonElement;
import org.odftoolkit.odfdom.dom.element.form.FormConnectionResourceElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeAnnotationElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeAnnotationEndElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeChartElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeDatabaseElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeDrawingElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeImageElement;
import org.odftoolkit.odfdom.dom.element.office.OfficePresentationElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeSpreadsheetElement;
import org.odftoolkit.odfdom.dom.element.office.OfficeTextElement;
import org.odftoolkit.odfdom.dom.element.style.StyleFooterElement;
import org.odftoolkit.odfdom.dom.element.style.StyleFooterLeftElement;
import org.odftoolkit.odfdom.dom.element.style.StyleHeaderElement;
import org.odftoolkit.odfdom.dom.element.style.StyleHeaderLeftElement;
import org.odftoolkit.odfdom.dom.element.table.TableCoveredTableCellElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableCellElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableColumnGroupElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableColumnsElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableRowElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableRowGroupElement;
import org.odftoolkit.odfdom.dom.element.table.TableTableRowsElement;
import org.odftoolkit.odfdom.dom.element.text.TextAElement;
import org.odftoolkit.odfdom.dom.element.text.TextAuthorInitialsElement;
import org.odftoolkit.odfdom.dom.element.text.TextBookmarkElement;
import org.odftoolkit.odfdom.dom.element.text.TextHElement;
import org.odftoolkit.odfdom.dom.element.text.TextLineBreakElement;
import org.odftoolkit.odfdom.dom.element.text.TextListElement;
import org.odftoolkit.odfdom.dom.element.text.TextListHeaderElement;
import org.odftoolkit.odfdom.dom.element.text.TextListItemElement;
import org.odftoolkit.odfdom.dom.element.text.TextPElement;
import org.odftoolkit.odfdom.dom.element.text.TextParagraphElementBase;
import org.odftoolkit.odfdom.dom.element.text.TextSElement;
import org.odftoolkit.odfdom.dom.element.text.TextSpanElement;
import org.odftoolkit.odfdom.dom.element.text.TextTabElement;
import org.odftoolkit.odfdom.dom.style.OdfStyleFamily;
import org.odftoolkit.odfdom.dom.style.props.OdfStylePropertiesSet;
import org.odftoolkit.odfdom.pkg.OdfElement;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* The component is a logical modular entity, to abstract from the implementation details of the
* XML.
*
* @author svante.schubertATgmail.com
*/
public class Component {
private static final Logger LOG = Logger.getLogger(Component.class.getName());
//
// public enum OPERATION {
// INSERT_PARAGRAPH ("insertParagraph", 2),
// INSERT_TEXT ("insertText", 3);
//
// private final String name; // function name
// private final int parameterCount; // number of arguments
// Operation(String name, int parameterCount) {
// this.name = name;
// this.parameterCount = parameterCount;
// }
// public String name() { return parameterCount; }
// public name parameterCount() { return parameterCount; }
//
// public double surfaceGravity() {
// return G * mass / (radius * radius);
// }
// public double surfaceWeight(double otherMass) {
// return otherMass * surfaceGravity();
// }
// }
private static final String LIBRE_OFFICE_MS_INTEROP_NAMESPACE =
"urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0";
private static final String LIBRE_OFFICE_MS_INTEROP_LOCALNAME = "fieldmark";
/**
* Tests if the given element is the start of a component
*
* @return true if the given element is the root of an ODF component
*/
public static boolean isComponentRoot(Element element) {
boolean isComponent = false;
if (element instanceof OdfElement) {
isComponent =
isComponentRoot(
((OdfElement) element).getNamespaceURI(), ((OdfElement) element).getLocalName());
}
return isComponent;
}
/**
* Tests if the given element is the start of a component
*
* @return true if the given element is the root of an ODF component
*/
public static boolean isComponentRoot(String uri, String localName) {
boolean isComponent = false;
if (uri != null) {
if (uri.equals(TextPElement.ELEMENT_NAME.getUri())) {
if (localName.equals(TextPElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
} else if (localName.equals(TextHElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
} else if (localName.equals(TextTabElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
} else if (localName.equals(TextLineBreakElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
}
} else if (uri.equals(TableTableElement.ELEMENT_NAME.getUri())) {
if (localName.equals(TableTableElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
} else if (localName.equals(TableTableRowElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
} else if (localName.equals(TableTableCellElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
}
} else if (uri.equals(DrawFrameElement.ELEMENT_NAME.getUri())
&& localName.equals(DrawFrameElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
} else if (isShapeElement(uri, localName)) {
isComponent = true;
} else if ((uri.equals(OfficeAnnotationElement.ELEMENT_NAME.getUri())
&& localName.equals(OfficeAnnotationElement.ELEMENT_NAME.getLocalName()))
|| (uri.equals(OfficeAnnotationEndElement.ELEMENT_NAME.getUri())
&& localName.equals(OfficeAnnotationEndElement.ELEMENT_NAME.getLocalName()))) {
isComponent = true;
}
if (isField(uri, localName)) {
isComponent = true;
}
}
return isComponent;
}
/**
* Tests if the given element is the wrapper around a descendant component root element
*
* @return true if the given element is a potential wrapper around an ODF component
*/
public static boolean isComponentWrapper(Element element) {
boolean isWrapper = false;
if (element instanceof OdfElement) {
isWrapper =
isComponentWrapper(
((OdfElement) element).getNamespaceURI(), ((OdfElement) element).getLocalName());
}
return isWrapper;
}
/**
* Tests if the given element is the wrapper around a descendant component root element
*
* @return true if the given element is a potential wrapper around an ODF component
*/
public static boolean isComponentWrapper(String uri, String localName) {
boolean isWrapper = false;
if (uri != null && uri.equals(TextListElement.ELEMENT_NAME.getUri())) {
if (localName.equals(TextListElement.ELEMENT_NAME.getLocalName())) {
isWrapper = true;
} else if (localName.equals(TextListItemElement.ELEMENT_NAME.getLocalName())) {
isWrapper = true;
} else if (localName.equals(TextListHeaderElement.ELEMENT_NAME.getLocalName())) {
isWrapper = true;
} else if (localName.equals(TextBookmarkElement.ELEMENT_NAME.getLocalName())) {
isWrapper = true;
}
} else if (uri != null && uri.equals(TableTableElement.ELEMENT_NAME.getUri())) {
if (localName.equals(TableTableRowsElement.ELEMENT_NAME.getLocalName())) {
isWrapper = true;
} else if (localName.equals(TableTableRowGroupElement.ELEMENT_NAME.getLocalName())) {
isWrapper = true;
} else if (localName.equals(TableTableColumnsElement.ELEMENT_NAME.getLocalName())) {
isWrapper = true;
} else if (localName.equals(TableTableColumnGroupElement.ELEMENT_NAME.getLocalName())) {
isWrapper = true;
}
}
return isWrapper;
}
// /** Includes text delimiter */
// private boolean isTextElement(String uri, String localName) {
// boolean isTextElement = false;
// // Check for component root element by combination of URI and localName
// if (uri.equals(TextPElement.ELEMENT_NAME.getUri()) &&
// (localName.equals(TextPElement.ELEMENT_NAME.getLocalName())
// || localName.equals(TextSpanElement.ELEMENT_NAME.getLocalName())
// || localName.equals(TextHElement.ELEMENT_NAME.getLocalName()))) {
// isTextElement = true;
// }
// return isTextElement;
// }
/**
* Returns true if the Node is an TextPElement or TextHElement. Both are the root elements of text
* containers. Text container have special handline of whitespace, see
* http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#White-space_Characters
*/
public static boolean isTextComponentRoot(Node textContainer) {
boolean isTextElement = false;
if (textContainer instanceof TextPElement || textContainer instanceof TextHElement) {
isTextElement = true;
}
return isTextElement;
}
public static boolean isTextComponentRoot(String uri, String localName) {
boolean isComponent = false;
if (uri != null && uri.equals(TextPElement.ELEMENT_NAME.getUri())) {
if (localName.equals(TextPElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
} else if (localName.equals(TextHElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
}
}
return isComponent;
}
public static boolean isRowComponentRoot(Node textContainer) {
boolean isRowElement = false;
if (textContainer instanceof TableTableRowElement) {
isRowElement = true;
}
return isRowElement;
}
public static boolean isRowComponentRoot(String uri, String localName) {
boolean isComponent = false;
if (uri != null && uri.equals(TableTableRowElement.ELEMENT_NAME.getUri())) {
if (localName.equals(TableTableRowElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
} else if (localName.equals(TextHElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
}
}
return isComponent;
}
public static boolean isField(String uri, String localName) {
boolean isField = false;
if (uri != null && uri.equals(TextAuthorInitialsElement.ELEMENT_NAME.getUri())) {
if (localName.equals("author-initials")
|| localName.equals("author-name")
|| localName.equals("bookmark-ref")
|| localName.equals("chapter")
|| localName.equals("character-count")
|| localName.equals("conditional-text")
|| localName.equals("creation-date")
|| localName.equals("creation-time")
|| localName.equals("creator")
|| localName.equals("database-display")
|| localName.equals("database-name")
|| localName.equals("database-row-number")
|| localName.equals("date")
|| localName.equals("dde-connection")
|| localName.equals("description")
|| localName.equals("editing-cycles")
|| localName.equals("editing-duration")
|| localName.equals("execute-macro")
|| localName.equals("expression")
|| localName.equals("file-name")
|| localName.equals("hidden-paragraph")
|| localName.equals("hidden-text")
|| localName.equals("image-count")
|| localName.equals("initial-creator")
|| localName.equals("keywords")
|| localName.equals("measure")
|| localName.equals("meta-field")
|| localName.equals("modification-date")
|| localName.equals("modification-time")
|| localName.equals("note-ref")
|| localName.equals("object-count")
|| localName.equals("page-continuation")
|| localName.equals("page-count")
|| localName.equals("page-number")
|| localName.equals("page-variable-get")
|| localName.equals("page-variable-set")
|| localName.equals("paragraph-count")
|| localName.equals("placeholder")
|| localName.equals("print-date")
|| localName.equals("print-time")
|| localName.equals("printed-by")
|| localName.equals("reference-ref")
|| localName.equals("script")
|| localName.equals("sender-city")
|| localName.equals("sender-company")
|| localName.equals("sender-country")
|| localName.equals("sender-email")
|| localName.equals("sender-fax")
|| localName.equals("sender-firstname")
|| localName.equals("sender-initials")
|| localName.equals("sender-lastname")
|| localName.equals("sender-phone-private")
|| localName.equals("sender-phone-work")
|| localName.equals("sender-position")
|| localName.equals("sender-postal-code")
|| localName.equals("sender-state-or-province")
|| localName.equals("sender-street")
|| localName.equals("sender-title")
|| localName.equals("sequence-ref")
|| localName.equals("sequence")
|| localName.equals("sheet-name")
|| localName.equals("subject")
|| localName.equals("table-count")
|| localName.equals("template-name")
|| localName.equals("text-input")
|| localName.equals("time")
|| localName.equals("title")
|| localName.equals("user-defined")
|| localName.equals("user-field-get")
|| localName.equals("user-field-input")
|| localName.equals("variable-get")
|| localName.equals("variable-input")
|| localName.equals("variable-set")
|| localName.equals("word-count")) {
isField = true;
}
} else if (uri != null && uri.equals(FormConnectionResourceElement.ELEMENT_NAME.getUri())) {
if (localName.equals("connection-resource")) {
isField = true;
}
} else if (uri != null && uri.equals(LIBRE_OFFICE_MS_INTEROP_NAMESPACE)) {
if (localName.equals(LIBRE_OFFICE_MS_INTEROP_LOCALNAME)) {
isField = true;
}
}
return isField;
}
/**
* Tests if the given element is the start of a document
*
* @return true if the given element is the root of an ODF document (e.g. office:text)
*/
public static boolean isDocumentRoot(String uri, String localName) {
boolean isRoot = false;
if (uri.equals(OfficeTextElement.ELEMENT_NAME.getUri())) {
if (localName.equals(OfficeTextElement.ELEMENT_NAME.getLocalName())) {
isRoot = true;
} else if (localName.equals(OfficeSpreadsheetElement.ELEMENT_NAME.getLocalName())) {
isRoot = true;
} else if (localName.equals(OfficePresentationElement.ELEMENT_NAME.getLocalName())) {
isRoot = true;
} else if (localName.equals(OfficeChartElement.ELEMENT_NAME.getLocalName())) {
isRoot = true;
} else if (localName.equals(OfficeDrawingElement.ELEMENT_NAME.getLocalName())) {
isRoot = true;
} else if (localName.equals(OfficeImageElement.ELEMENT_NAME.getLocalName())) {
isRoot = true;
} else if (localName.equals(OfficeDatabaseElement.ELEMENT_NAME.getLocalName())) {
isRoot = true;
}
} else if (isHeaderRoot(uri, localName) || isFooterRoot(uri, localName)) {
isRoot = true;
}
return isRoot;
}
/**
* Tests if the given element is the start of a header within a page style. The content of a
* header is equal to the content of a usual ODT text file (ie. ).
*
* @return true if the given element is the root of a header (i.e. style:header)
*/
public static boolean isHeaderRoot(String uri, String localName) {
boolean isRoot = false;
if (uri != null && uri.equals(StyleHeaderElement.ELEMENT_NAME.getUri())) {
// style:header
if (localName.equals(StyleHeaderElement.ELEMENT_NAME.getLocalName())) {
isRoot = true;
}
}
if (uri != null && uri.equals(StyleHeaderLeftElement.ELEMENT_NAME.getUri())) {
// style:header-left
if (localName.equals(StyleHeaderLeftElement.ELEMENT_NAME.getLocalName())) {
isRoot = true;
}
}
if (uri != null && uri.equals(StyleHeaderLeftElement.ELEMENT_NAME.getUri())) {
// style:header-first
if (localName.equals("header-first")) {
isRoot = true;
}
}
return isRoot;
}
/**
* Tests if the given element is the start of a footer within a page style. The content of a
* footer is equal to the content of a usual ODT text file (ie. ).
*
* @return true if the given element is the root of a footer (i.e. style:footer)
*/
public static boolean isFooterRoot(String uri, String localName) {
boolean isRoot = false;
if (uri != null && uri.equals(StyleFooterElement.ELEMENT_NAME.getUri())) {
// style:footer
if (localName.equals(StyleFooterElement.ELEMENT_NAME.getLocalName())) {
isRoot = true;
}
}
if (uri != null && uri.equals(StyleFooterLeftElement.ELEMENT_NAME.getUri())) {
// style:footer-left
if (localName.equals(StyleFooterLeftElement.ELEMENT_NAME.getLocalName())) {
isRoot = true;
}
}
if (uri != null && uri.equals(StyleFooterLeftElement.ELEMENT_NAME.getUri())) {
// style:footer-first
if (localName.equals("footer-first")) {
isRoot = true;
}
}
return isRoot;
}
/**
* Tests if the given element is a shape element Shapes are in general those with elements with a
*
* @svg:width and
* @text:anchor-type. With the exception of office:annotation, usually viewed aside the document
* as note. The list of placeholder elements is therefore: dr3d:scene draw:caption draw:circle
* draw:control draw:custom-shape draw:ellipse draw:page-thumbnail draw:path draw:polygon
* draw:polyline draw:rect draw:regular-polygon
* @return true if the given element is the root of an ODF shape element
*/
public static boolean isShapeElement(String uri, String localName) {
boolean isShape = false;
if (uri != null && localName != null) {
if (uri.equals(DrawRectElement.ELEMENT_NAME.getUri())
&& (localName.equals(DrawCaptionElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawCircleElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawControlElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawCustomShapeElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawEllipseElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawGElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawPageThumbnailElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawPathElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawPolylineElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawPolygonElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawRectElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawRegularPolygonElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawLineElement.ELEMENT_NAME.getLocalName())
|| localName.equals(DrawConnectorElement.ELEMENT_NAME.getLocalName()))) {
isShape = true;
}
if (uri.equals(Dr3dSceneElement.ELEMENT_NAME.getUri())) {
if (localName.equals(Dr3dSceneElement.ELEMENT_NAME.getLocalName())) {
isShape = true;
}
}
}
return isShape;
}
/**
* Tests if the given element is a whitespace element
*
* @return true if the given element is an ODF whitespace element
*/
public static boolean isWhiteSpaceElement(String uri, String localName) {
boolean isWhiteSpace = false;
if (uri.equals(TextSElement.ELEMENT_NAME.getUri())) {
if (localName.equals(TextSElement.ELEMENT_NAME.getLocalName())) {
isWhiteSpace = true;
}
}
return isWhiteSpace;
}
public static boolean isCoveredComponentRoot(String uri, String localName) {
boolean isComponent = false;
if (uri.equals(TableTableElement.ELEMENT_NAME.getUri())) {
if (localName.equals(TableCoveredTableCellElement.ELEMENT_NAME.getLocalName())) {
isComponent = true;
}
}
return isComponent;
}
/** @return true if the node is a text delimiter element */
public static boolean isTextSelection(Node textSelection) {
boolean isTextElement = false;
if (textSelection instanceof TextSpanElement || textSelection instanceof TextAElement) {
isTextElement = true;
}
return isTextElement;
}
/**
* Only being used to create the root of all components, representing the document without a
* parent element
*/
public Component(OdfElement componentElement) {
mRootElement = componentElement;
componentElement.setComponent(this);
}
protected Component(OdfElement componentElement, Component parent) {
mRootElement = componentElement;
componentElement.setComponent(this);
mParent = parent;
}
// All component children
List mChildren;
// the root XML element of the component
public OdfElement mRootElement;
// the parent component
private Component mParent;
private Component mRootComponent;
/** if a repeated attribute was set at the component. In this case the positioning will change. */
boolean mHasRepeated = false;
/** Returns the parent component */
public Component getParent() {
return mParent;
}
/**
* Sometimes (e.g. if the child is a paragraph within list elements). The parent root element of
* the child component root element will not be directly children. It will be checked if there is
* a child element or list level 10 has reached.
*/
public static OdfElement getCorrectStartElementOfChild(
OdfElement parentElement, OdfElement existingChildElement) {
// element usually used for positioning of insertion
// lists elements are boilerplate between paragraph parent and child component (paragraph),
OdfElement existingParentElement = (OdfElement) existingChildElement.getParentNode();
// if the existing component is a paragraph with list properities
if ((existingParentElement instanceof TextListItemElement
|| existingParentElement instanceof TextListHeaderElement)
&& existingChildElement instanceof TextParagraphElementBase) {
TextParagraphElementBase existingParagraph = (TextParagraphElementBase) existingChildElement;
JsonOperationConsumer.isolateListParagraph(existingParagraph);
OdfElement potentialParent = (OdfElement) existingParagraph.getParentNode();
while (potentialParent != null && !parentElement.equals(potentialParent)) {
potentialParent = (OdfElement) potentialParent.getParentNode();
if (parentElement.equals(potentialParent)) {
break;
} else {
existingChildElement = potentialParent;
}
}
}
return existingChildElement;
}
public Component getLastChild() {
Component lastChild = null;
if (mChildren != null) {
lastChild = mChildren.get(mChildren.size());
}
return lastChild;
}
public Document getOwnerDocument() {
return mRootElement.getOwnerDocument();
}
/** @return the child starting from the given position and at cut at the endPosition */
public Node getChildNode(int startPosition, int endPosition) {
return getChildNode(startPosition);
}
/** @return the child at the given position */
public Node getChildNode(int position) {
Node rootElement = null;
Component c = null;
if (mChildren != null && mChildren.size() > position) {
c = mChildren.get(position);
}
if (c != null) {
rootElement = c.getRootElement();
}
return rootElement;
}
public Component get(JSONArray position) {
return get(position, false, false, 0);
}
/**
* Get descendant component by its relative position to this component. Counting starts with 0.
*
* @param position relative position of the desired component relative to the current component
* @param needParent if true the parent of the given position is returned
* @param needFollowingSibling if true the next sibling of the given position is returned
* (exclusive to getPositionsFollowingSibling)
*/
protected Component get(
JSONArray position, boolean needParent, boolean needFollowingSibling, int depth) {
// check recursion end conditions
Component c = null;
final int maxDepth = position.length() - 1;
// if not the correct depth is reached, go deeper
if (!needParent && maxDepth > depth || (needParent && (maxDepth - 1 != depth))) {
try {
// get from this level the currect component
Node startNode = getChildNode(position.getInt(depth));
if (startNode instanceof OdfElement) {
c = ((OdfElement) startNode).getComponent();
}
// call recursive this method with an additional depth for getting its child
if (c != null) {
c = c.get(position, needParent, needFollowingSibling, depth + 1);
} else {
// we need a special component for table row and cell, to expand if necessary!
// take the last existing row/cell and insert as many empty before the last one.
// get the position of the last XML and the last XML as well
// split up repeated
// do not split up covered, but .. hmm... get an empty cell back
// shall we track the position on-the-fly via DOM events?
// OdfElement lastElement = mRootElement.getLastChildElement();
// Component c2 = lastElement.getComponent();
// String pos = c2.getPosition(c2);
LOG.fine("Component yet missing!");
}
} catch (JSONException ex) {
Logger.getLogger(Component.class.getName()).log(Level.SEVERE, null, ex);
}
} else {
try {
// get the desired child
if (needFollowingSibling) {
Node startNode = getChildNode(position.getInt(depth) + 1);
if (startNode instanceof OdfElement) {
c = ((OdfElement) startNode).getComponent();
}
} else {
Node startNode = getChildNode(position.getInt(depth));
if (startNode instanceof OdfElement) {
c = ((OdfElement) startNode).getComponent();
Component parent = c.getParent();
parent.getPosition(c);
}
}
// call recursive this method with an additional depth for getting its child
if (c == null) {
// we need a special component for table row and cell, to expand if necessary!
// take the last existing row/cell and insert as many empty before the last one.
// get the position of the last XML and the last XML as well
// split up repeated
// do not split up covered, but .. hmm... get an empty cell back
// shall we track the position on-the-fly via DOM events?
// OdfElement lastElement = mRootElement.getLastChildElement();
// Component c2 = lastElement.getComponent();
// String pos = c2.getPosition(c2);
LOG.fine("Component yet missing!");
}
} catch (JSONException ex) {
Logger.getLogger(Component.class.getName()).log(Level.SEVERE, null, ex);
}
}
return c;
}
/** Get next sibling component of the given position. Counting start with 0. */
public Component getNextSiblingOf(JSONArray position) {
return get(position, false, true, 0);
}
/** Get parent component of the given position */
public Component getParentOf(JSONArray position) {
Component c = null;
if (position.length() == 1) {
c = getRootComponent();
} else {
c = get(position, true, false, 0);
}
return c;
}
public Component getRootComponent() {
if (mRootComponent == null) {
Component parent = this;
while (parent != null) {
mRootComponent = parent;
parent = parent.getParent();
}
}
return mRootComponent;
}
public List getChildren() {
return mChildren;
}
/** @return the root element of the component */
public OdfElement getRootElement() {
return mRootElement;
}
/**
* @param odfElement the new root element of the component Used after splitting a list containing
* paragraphs and assigning the new cloned paragraph elements to the existing components.
*/
void setRootElement(OdfElement odfElement) {
mRootElement = odfElement;
}
/** Appending a child element to the component */
public Component createChildComponent(OdfElement componentRoot) {
componentRoot.markAsComponentRoot(true);
return createChildComponent(-1, this, componentRoot);
}
/**
* Inserts a component at the given position as child
*
* @param position of the component, a -1 is going to append the element
*/
public static Component createChildComponent(
int position, Component parentComponent, OdfElement newChildElement) {
Component c = createComponent(parentComponent, newChildElement);
if (!(parentComponent instanceof Table
|| parentComponent instanceof Row
|| parentComponent instanceof Cell
|| parentComponent instanceof TextContainer)) {
addComponent(position, parentComponent, c);
}
LOG.log(
Level.FINEST,
"***\n*** New Component: {0}\n*** {1}\n***",
new Object[] {parentComponent.getPosition(c), newChildElement.toString()});
return c;
}
public static Component createComponent(Component parentComponent, OdfElement newChildElement) {
Component c;
// Mark the element as component, so for instance an ODF Frame with image can be recognized as
// component
newChildElement.markAsComponentRoot(true);
// if the component is a table container
if (newChildElement instanceof TableTableElement) {
c = new Table(newChildElement, parentComponent);
} else if (newChildElement instanceof TableTableRowElement) {
c = new Row(newChildElement, parentComponent);
} else if (newChildElement instanceof TableTableCellElement
|| newChildElement instanceof TableCoveredTableCellElement) {
c = new Cell(newChildElement, parentComponent);
} else if (isTextComponentRoot(newChildElement)) {
c = new TextContainer(newChildElement, parentComponent);
// if the component is a text container (have to deal with text nodes and elements)
} else if (newChildElement instanceof OfficeAnnotationElement) {
c = new Annotation(newChildElement, parentComponent);
} else {
c = new Component(newChildElement, parentComponent);
}
newChildElement.setComponent(c);
return c;
}
/**
* Adds the given component as new child component. No XML elements are being changed!
*
* @param pos starting with 0 representing the position of the child, if -1 the new child will be
* appended
*/
static void addComponent(int pos, Component parent, Component child) {
parent.addChild(pos, child);
}
/**
* Adds the given component as new child component. No XML elements are being changed!
*
* @param index starting with 0 representing the position of the child, if -1 the new child will
* be appended
*/
public void addChild(int index, Component c) {
if (mChildren == null) {
if (mChildren == null) {
mChildren = new LinkedList();
}
}
if (index >= 0) {
mChildren.add(index, c);
} else {
mChildren.add(c);
}
}
/** Only removes from the component list, not from the DOM */
public Node remove(int position) {
Node n = null;
if (mChildren != null) {
Component c = mChildren.remove(position);
if (c != null) {
n = c.getRootElement();
}
}
return n;
}
/** Returns the number of child components */
public int size() {
int size = 0;
if (mChildren != null) {
size = mChildren.size();
}
return size;
}
public void hasRepeated(boolean hasRepeated) {
mHasRepeated = hasRepeated;
}
public boolean hasRepeated() {
return mHasRepeated;
}
/** @return the position as a slash separated string */
protected String getPosition(Component c) {
String s;
List position;
int childPos;
if (c.mParent != null) {
position = new LinkedList();
Component parent;
while ((parent = c.getParent()) != null) {
childPos = parent.indexOf(c);
// if (childPos < 0) {
// childPos = indexOf(c);
// }
position.add(childPos);
c = parent;
}
StringBuilder sb = new StringBuilder();
for (int i = position.size() - 1; i >= 0; i--) {
sb.append("/").append(position.get(i));
}
s = sb.toString();
} else {
s = "/";
}
return s;
}
/** @return the position as a slash separated string */
protected static String getPositionString(Component c) {
String s;
List position;
int childPos;
if (c.mParent != null) {
position = new LinkedList();
Component parent;
while ((parent = c.getParent()) != null) {
childPos = parent.indexOf(c);
// if (childPos < 0) {
// childPos = indexOf(c);
// }
position.add(childPos);
c = parent;
}
StringBuilder sb = new StringBuilder();
for (int i = position.size() - 1; i >= 0; i--) {
sb.append("/").append(position.get(i));
}
s = sb.toString();
} else {
s = "/";
}
return s;
}
/**
* @returns the position of the child component c or given Node within the parents children list.
* Returns -1 if it is not a child.
*/
public int indexOf(Object o) {
int position = -1;
// Either use the previous way of creating lists of child components
if (mChildren != null) {
position = mChildren.indexOf(o);
} else {
// Either use the previous way of creating lists of child components
Node targetNode = null;
OdfElement parentNode = null;
if (o instanceof Component) {
Component c = ((Component) o);
targetNode = c.getRootElement();
parentNode = c.getParent().getRootElement();
} else if (o instanceof Node) {
targetNode = (Node) o;
parentNode = (OdfElement) targetNode.getParentNode();
}
if (targetNode != null && targetNode instanceof OdfElement) {
if (parentNode != null && parentNode.equals(mRootElement)) {
position = findPosition(parentNode, (OdfElement) targetNode);
}
}
}
return position;
}
/** Recursive traverse the text container and count the children */
private Integer findPosition(Element parentComponentElement, OdfElement targetNode) {
int pos = -1;
// Only start the recursion if the parameters are not null and in an ancestor relationship
if (parentComponentElement != null
&& targetNode != null
&& targetNode.hasAncestor(parentComponentElement)) {
findChild(0, parentComponentElement, targetNode);
}
return pos;
}
/** Recursive traverse the text container and count the children */
private Integer findChild(Integer pos, Element parentComponentElement, Node child) {
// Traverse first horizontal backwards and if no further sibling, traverse up (e.g. for
// paragraphs within list required)
if (child != null) {
if (child instanceof OdfElement) {
OdfElement childElement = ((OdfElement) child);
if (childElement.isComponentRoot()) {
// if it is a component we found one (even ourselves in the beginning) at one
pos += childElement.getRepetition();
pos = findChild(pos, parentComponentElement, childElement.getPreviousSibling());
} else if (Component.isComponentWrapper(childElement)) {
pos = findChild(pos, parentComponentElement, childElement.getLastChildElement());
} else {
pos = findChild(pos, parentComponentElement, childElement.getPreviousSibling());
}
} else {
// do the recursion even for text nodes, comments, etc.
pos = findChild(pos, parentComponentElement, child.getPreviousSibling());
}
}
return pos;
}
@Override
public String toString() {
String s = "POS:" + getPosition(this);
if (mRootElement != null) {
s += mRootElement.getPrefix() + ":" + mRootElement.getLocalName();
} else {
s += "NO ROOT ELEMENT!!!";
}
return s;
}
// These sets should be static initialized and a central place, best generated at the family
// superclass
public static Map getAllStyleGroupingIdProperties(
OdfStylableElement styleElement) {
return getAllStyleGroupingIdProperties(styleElement.getStyleFamily());
}
public static Map getAllStyleGroupingIdProperties(
OdfStyleFamily styleFamily) {
Map familyProperties =
new HashMap();
if (styleFamily.equals(OdfStyleFamily.Paragraph)) {
familyProperties.put("paragraph", OdfStylePropertiesSet.ParagraphProperties);
familyProperties.put("character", OdfStylePropertiesSet.TextProperties);
} else if (styleFamily.equals(OdfStyleFamily.Text)) {
familyProperties.put("character", OdfStylePropertiesSet.TextProperties);
} else if (styleFamily.equals(OdfStyleFamily.Table)) {
familyProperties.put("table", OdfStylePropertiesSet.TableProperties);
} else if (styleFamily.equals(OdfStyleFamily.TableRow)) {
familyProperties.put("row", OdfStylePropertiesSet.TableRowProperties);
} else if (styleFamily.equals(OdfStyleFamily.TableCell)) {
familyProperties.put("cell", OdfStylePropertiesSet.TableCellProperties);
familyProperties.put("paragraph", OdfStylePropertiesSet.ParagraphProperties);
familyProperties.put(
"character", OdfStylePropertiesSet.TextProperties); // changed from text to character
} else if (styleFamily.equals(OdfStyleFamily.TableColumn)) {
familyProperties.put("column", OdfStylePropertiesSet.TableColumnProperties);
} else if (styleFamily.equals(OdfStyleFamily.Section)) {
familyProperties.put("section", OdfStylePropertiesSet.SectionProperties);
} else if (styleFamily.equals(OdfStyleFamily.List)) {
familyProperties.put("list", OdfStylePropertiesSet.ListLevelProperties);
} else if (styleFamily.equals(OdfStyleFamily.Chart)) {
familyProperties.put("chart", OdfStylePropertiesSet.ChartProperties);
familyProperties.put(
"drawing", OdfStylePropertiesSet.GraphicProperties); // changed from graphic to drawing
familyProperties.put("paragraph", OdfStylePropertiesSet.ParagraphProperties);
familyProperties.put(
"character", OdfStylePropertiesSet.TextProperties); // changed to text from character
} else if (styleFamily.equals(OdfStyleFamily.Graphic)
|| styleFamily.equals(OdfStyleFamily.Presentation)) {
familyProperties.put(
"drawing", OdfStylePropertiesSet.GraphicProperties); // changed from graphic to drawing
familyProperties.put("paragraph", OdfStylePropertiesSet.ParagraphProperties);
familyProperties.put(
"character", OdfStylePropertiesSet.TextProperties); // changed from text to character
} else if (styleFamily.equals(OdfStyleFamily.DrawingPage)) {
familyProperties.put("drawing", OdfStylePropertiesSet.DrawingPageProperties);
} else if (styleFamily.equals(OdfStyleFamily.Ruby)) {
familyProperties.put("ruby", OdfStylePropertiesSet.RubyProperties);
}
return familyProperties;
}
// In the end, this meths should be better moved to the component class
public static String getFamilyID(OdfStylableElement styleElement) {
return getFamilyID(styleElement.getStyleFamily());
}
// In the end, this meths should be better moved to the component class
public static String getMainStyleGroupingId(OdfStylableElement styleElement) {
return Component.getMainStyleGroupingId(styleElement.getStyleFamily());
}
// In the end, this meths should be better moved to the component class
public static String getStyleNamePrefix(OdfStylableElement styleElement) {
return getStyleNamePrefix(styleElement.getStyleFamily());
}
// In the end, this meths should be better moved to the component class
public static String getMainStyleGroupingId(OdfStyleFamily styleFamily) {
String familyID = null;
if (styleFamily.equals(OdfStyleFamily.Paragraph)) {
familyID = "paragraph";
} else if (styleFamily.equals(OdfStyleFamily.Text)) {
familyID = "character";
} else if (styleFamily.equals(OdfStyleFamily.Table)) {
familyID = "table";
} else if (styleFamily.equals(OdfStyleFamily.TableRow)) {
familyID = "row";
} else if (styleFamily.equals(OdfStyleFamily.TableCell)) {
familyID = "cell";
} else if (styleFamily.equals(OdfStyleFamily.TableColumn)) {
familyID = "column";
} else if (styleFamily.equals(OdfStyleFamily.Section)) {
familyID = "section";
} else if (styleFamily.equals(OdfStyleFamily.List)) {
familyID = "list";
} else if (styleFamily.equals(OdfStyleFamily.Chart)) {
familyID = "chart";
} else if (styleFamily.equals(OdfStyleFamily.Graphic)
|| styleFamily.equals(OdfStyleFamily.Presentation)) {
familyID = "drawing";
} else if (styleFamily.equals(OdfStyleFamily.DrawingPage)) {
familyID = "drawing";
} else if (styleFamily.equals(OdfStyleFamily.Ruby)) {
familyID = "ruby";
}
return familyID;
}
// In the end, this meths should be better moved to the component class
public static String getStyleNamePrefix(OdfStyleFamily styleFamily) {
String familyID = null;
if (styleFamily.equals(OdfStyleFamily.Paragraph)
|| styleFamily.equals(OdfStyleFamily.Text)
|| styleFamily.equals(OdfStyleFamily.Section)
|| styleFamily.equals(OdfStyleFamily.List)
|| styleFamily.equals(OdfStyleFamily.Ruby)) {
familyID = "text";
} else if (styleFamily.equals(OdfStyleFamily.Table)
|| styleFamily.equals(OdfStyleFamily.TableRow)
|| styleFamily.equals(OdfStyleFamily.TableCell)
|| styleFamily.equals(OdfStyleFamily.TableColumn)) {
familyID = "table";
} else if (styleFamily.equals(OdfStyleFamily.Chart)) {
familyID = "chart";
} else if (styleFamily.equals(OdfStyleFamily.Graphic)
|| styleFamily.equals(OdfStyleFamily.Presentation)
|| styleFamily.equals(OdfStyleFamily.DrawingPage)) {
familyID = "draw";
} else if (styleFamily.equals(OdfStyleFamily.Presentation)) {
familyID = "presentation";
}
return familyID;
}
// In the end, this meths should be better moved to the component class
public static String getFamilyID(OdfStyleFamily styleFamily) {
String familyID = null;
if (styleFamily.equals(OdfStyleFamily.Paragraph)) {
familyID = "paragraph";
} else if (styleFamily.equals(OdfStyleFamily.Text)) {
familyID = "character";
} else if (styleFamily.equals(OdfStyleFamily.Table)) {
familyID = "table";
} else if (styleFamily.equals(OdfStyleFamily.TableRow)) {
familyID = "row";
} else if (styleFamily.equals(OdfStyleFamily.TableCell)) {
familyID = "cell";
} else if (styleFamily.equals(OdfStyleFamily.TableColumn)) {
familyID = "column";
} else if (styleFamily.equals(OdfStyleFamily.Section)) {
familyID = "section";
} else if (styleFamily.equals(OdfStyleFamily.List)) {
familyID = "list";
} else if (styleFamily.equals(OdfStyleFamily.Chart)) {
familyID = "chart";
} else if (styleFamily.equals(OdfStyleFamily.Graphic)
|| styleFamily.equals(OdfStyleFamily.Presentation)) {
familyID = "drawing";
} else if (styleFamily.equals(OdfStyleFamily.DrawingPage)) {
familyID = "drawing";
} else if (styleFamily.equals(OdfStyleFamily.Ruby)) {
familyID = "ruby";
}
return familyID;
}
// In the end, this meths should be better moved to the component class
public static String getFamilyDisplayName(OdfStyleFamily styleFamily) {
String familyID = null;
if (styleFamily.equals(OdfStyleFamily.Paragraph)) {
familyID = "Paragraph";
} else if (styleFamily.equals(OdfStyleFamily.Text)) {
familyID = "Character";
} else if (styleFamily.equals(OdfStyleFamily.Table)) {
familyID = "Table";
} else if (styleFamily.equals(OdfStyleFamily.TableRow)) {
familyID = "Row";
} else if (styleFamily.equals(OdfStyleFamily.TableCell)) {
familyID = "Cell";
} else if (styleFamily.equals(OdfStyleFamily.TableColumn)) {
familyID = "Column";
} else if (styleFamily.equals(OdfStyleFamily.Section)) {
familyID = "Section";
} else if (styleFamily.equals(OdfStyleFamily.List)) {
familyID = "List";
} else if (styleFamily.equals(OdfStyleFamily.Presentation)) {
familyID = "Presentation";
} else if (styleFamily.equals(OdfStyleFamily.Chart)) {
familyID = "Chart";
} else if (styleFamily.equals(OdfStyleFamily.Graphic)) {
familyID = "Graphic";
} else if (styleFamily.equals(OdfStyleFamily.DrawingPage)) {
familyID = "Drawing";
} else if (styleFamily.equals(OdfStyleFamily.Ruby)) {
familyID = "Ruby";
}
return familyID;
}
// In the end, this meths should be better moved to the component class
/**
* @return styleFamilyValue the String
value * * * * * * * of
* StyleFamilyAttribute
,
*/
public static String getFamilyName(String styleId) {
String familyID = null;
if (styleId.equals("paragraph")) {
familyID = OdfStyleFamily.Paragraph.getName();
} else if (styleId.equals("character")) {
familyID = OdfStyleFamily.Text.getName();
} else if (styleId.equals("table")) {
familyID = OdfStyleFamily.Table.getName();
} else if (styleId.equals("row")) {
familyID = OdfStyleFamily.TableRow.getName();
} else if (styleId.equals("cell")) {
familyID = OdfStyleFamily.TableCell.getName();
} else if (styleId.equals("column")) {
familyID = OdfStyleFamily.TableColumn.getName();
} else if (styleId.equals("graphic")) {
familyID = OdfStyleFamily.Graphic.getName();
}
return familyID;
}
// In the end, this meths should be better moved to the component class
/**
* @return styleFamily the OdfStyleFamily
representation * * * of
* StyleFamilyAttribute
,
*/
public static OdfStyleFamily getFamily(String styleId) {
OdfStyleFamily family = null;
if (styleId.equals("paragraph")) {
family = OdfStyleFamily.Paragraph;
} else if (styleId.equals("character")) {
family = OdfStyleFamily.Text;
} else if (styleId.equals("table")) {
family = OdfStyleFamily.Table;
} else if (styleId.equals("row")) {
family = OdfStyleFamily.TableRow;
} else if (styleId.equals("cell")) {
family = OdfStyleFamily.TableCell;
} else if (styleId.equals("column")) {
family = OdfStyleFamily.TableColumn;
} else if (styleId.equals("drawing")) {
family = OdfStyleFamily.Graphic;
}
return family;
}
/**
* A multiple components can be represented by a single XML element
*
* @return the number of components the elements represents
*/
public int repetition() {
return 1;
}
}