OdfStyle.java
/**
* **********************************************************************
*
* <p>DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* <p>Copyright 2008, 2010 Oracle and/or its affiliates. All rights reserved.
*
* <p>Use is subject to license terms.
*
* <p>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. You can also obtain a copy of the License at
* http://odftoolkit.org/docs/license.txt
*
* <p>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.
*
* <p>See the License for the specific language governing permissions and limitations under the
* License.
*
* <p>**********************************************************************
*/
package org.odftoolkit.odfdom.incubator.doc.style;
import java.util.logging.Logger;
import org.odftoolkit.odfdom.dom.OdfContentDom;
import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;
import org.odftoolkit.odfdom.dom.OdfSchemaDocument;
import org.odftoolkit.odfdom.dom.OdfStylesDom;
import org.odftoolkit.odfdom.dom.element.OdfStylableElement;
import org.odftoolkit.odfdom.dom.element.OdfStyleBase;
import org.odftoolkit.odfdom.dom.element.OdfStylePropertiesBase;
import org.odftoolkit.odfdom.dom.element.style.StyleStyleElement;
import org.odftoolkit.odfdom.dom.element.style.StyleTextPropertiesElement;
import org.odftoolkit.odfdom.dom.style.OdfStyleFamily;
import org.odftoolkit.odfdom.incubator.doc.office.OdfOfficeAutomaticStyles;
import org.odftoolkit.odfdom.incubator.doc.office.OdfOfficeStyles;
import org.odftoolkit.odfdom.pkg.OdfElement;
import org.odftoolkit.odfdom.pkg.OdfFileDom;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/** Convenient functionality for the parent ODF OpenDocument element */
public class OdfStyle extends StyleStyleElement {
private static final Logger LOG = Logger.getLogger(OdfStyle.class.getName());
/** */
private static final long serialVersionUID = 1114614579014922410L;
public OdfStyle(OdfFileDom ownerDoc) {
super(ownerDoc);
}
@Override
public OdfStyleBase getParentStyle() {
String parent = this.getStyleParentStyleNameAttribute();
OdfStyleBase parentStyle = null;
if ((parent != null) && (parent.length() != 0)) {
parentStyle =
((OdfSchemaDocument) mPackageDocument).getDocumentStyles().getStyle(parent, getFamily());
} else {
OdfOfficeStyles documentStyles = ((OdfSchemaDocument) mPackageDocument).getDocumentStyles();
if (documentStyles != null) {
parentStyle = documentStyles.getDefaultStyle(getFamily());
}
}
return parentStyle;
}
@Override
public OdfStyleFamily getFamily() {
String family = getStyleFamilyAttribute();
if (family != null) {
return OdfStyleFamily.valueOf(family);
} else {
return null;
}
}
/**
* @return true if there is a property other than the style:family, style:name and
* style:parent-style-name with a value other than "null", or a similar attribute on the
* properties element
*/
public boolean hasPropertyAttribute() {
boolean hasPropertyAttributes = false;
NodeList children = this.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if ((child != null) && OdfStylePropertiesBase.class.isInstance(child)) {
if (hasPropertyAttributes((OdfElement) child)) {
hasPropertyAttributes = true;
break;
}
}
}
return hasPropertyAttributes;
}
/**
* If a property element has more than the three default attributes style:parent-style-name,
* style:name and style:family, it is considered not empty!
*/
private static boolean hasPropertyAttributes(OdfElement propsElement) {
boolean hasPropertyAttributes = false;
NamedNodeMap attrs = propsElement.getAttributes();
int attrsCount = attrs.getLength();
for (int i = 0; i < attrsCount; i++) {
Attr a = (Attr) attrs.item(i);
if (a.getNamespaceURI().equals(OdfDocumentNamespace.STYLE.getUri())) {
// none of the below should be in here, as we are looking at teh properties..
if (!(a.getLocalName().equals("family")
|| a.getLocalName().equals("name")
|| a.getLocalName().equals("parent-style-name"))) {
if (!a.getValue().equals("null")) {
hasPropertyAttributes = true;
break;
}
}
} else {
if (!a.getValue().equals("null")) {
hasPropertyAttributes = true;
break;
}
}
}
return hasPropertyAttributes;
}
/**
* Two spans having the same range will be provided, only one will remain, styles are being
* merged.
*
* <p>Merges the automatic styles of the two given spans, uses one template style. The styles from
* the inner span have the higher priority. The outer span will be deleted afterwards.
*
* @return the remaining stylableElement of the two
*/
// ToDo: Refactoring - the style changs are only dependent on the state of the style-dominant span
// (refactor with method below)
public static OdfStylableElement mergeSelectionWithSameRange(
OdfStylableElement higherPrioSpan, OdfStylableElement lowerPrioSpan) {
// only do the merging of both spans, if they really exist
if (higherPrioSpan != null && lowerPrioSpan != null) {
boolean innerHasAutomatic = higherPrioSpan.hasAutomaticStyle();
boolean innerHasTemplate = higherPrioSpan.hasDocumentStyle();
boolean outerHasAutomatic = lowerPrioSpan.hasAutomaticStyle();
boolean outerHasTemplate = lowerPrioSpan.hasDocumentStyle();
OdfOfficeAutomaticStyles autoStyles = lowerPrioSpan.getOrCreateAutomaticStyles();
if (autoStyles == null) {
OdfFileDom fileDom = ((OdfFileDom) lowerPrioSpan.getOwnerDocument());
if (fileDom instanceof OdfContentDom) {
autoStyles = ((OdfContentDom) fileDom).getOrCreateAutomaticStyles();
} else {
autoStyles = ((OdfStylesDom) fileDom).getOrCreateAutomaticStyles();
}
}
OdfStyle innerAutoStyle = higherPrioSpan.getAutomaticStyle();
OdfStyle outerAutoStyle = lowerPrioSpan.getAutomaticStyle();
StyleTextPropertiesElement innerPropsElement = null;
StyleTextPropertiesElement outerPropsElement;
// in case there is an automatic style, make sure there is a property element
if (innerAutoStyle != null) {
innerPropsElement =
(StyleTextPropertiesElement)
innerAutoStyle.getChildElement(
StyleTextPropertiesElement.ELEMENT_NAME.getUri(),
StyleTextPropertiesElement.ELEMENT_NAME.getLocalName(),
0);
if (innerPropsElement != null) {
NodeList textPropsChildren = innerPropsElement.getChildNodes();
if (textPropsChildren == null
|| textPropsChildren.getLength() == 0 && !hasPropertyAttributes(innerPropsElement)) {
innerHasAutomatic = false;
}
} else {
innerAutoStyle.appendChild(
new StyleTextPropertiesElement((OdfFileDom) higherPrioSpan.getOwnerDocument()));
innerHasAutomatic = false;
}
} else {
innerHasAutomatic = false;
}
if (outerAutoStyle != null) {
outerPropsElement =
(StyleTextPropertiesElement)
outerAutoStyle.getChildElement(
StyleTextPropertiesElement.ELEMENT_NAME.getUri(),
StyleTextPropertiesElement.ELEMENT_NAME.getLocalName(),
0);
if (outerPropsElement != null) {
NodeList textPropsChildren = outerPropsElement.getChildNodes();
if (textPropsChildren == null
|| textPropsChildren.getLength() == 0 && !hasPropertyAttributes(outerPropsElement)) {
outerHasAutomatic = false;
}
} else {
outerAutoStyle.appendChild(
new StyleTextPropertiesElement((OdfFileDom) lowerPrioSpan.getOwnerDocument()));
outerHasAutomatic = false;
}
} else {
outerHasAutomatic = false;
}
// if one of the styles has no style
if (!innerHasAutomatic && !innerHasTemplate || !outerHasAutomatic && !outerHasTemplate) {
// if the outer has no style, do nothing as it will be deleted
if (!outerHasAutomatic && !outerHasTemplate) {
// KEEP INNER - delete OUTER STYLE & SPAN
// Done as the deletion is after these conditions
} else { // the inner has no style, so just reference inner span to the outer style
// KEEP INNER SPAN (ALWAYS - due to the content)
// point to the new style with @style:name
higherPrioSpan.setStyleName(lowerPrioSpan.getStyleName());
}
} else if (innerHasAutomatic && !innerHasTemplate) { // *** INNER ONLY AUTO
if (outerHasAutomatic && !outerHasTemplate) { // *** INNER ONLY AUTO & OUTER ONLY AUTO
// KEEP OUTER STYLE:
// override OUTER properties
OdfStyle newAutoStyle = autoStyles.makeStyleUnique(outerAutoStyle);
StyleTextPropertiesElement newPropsElement =
(StyleTextPropertiesElement)
newAutoStyle.getChildElement(
StyleTextPropertiesElement.ELEMENT_NAME.getUri(),
StyleTextPropertiesElement.ELEMENT_NAME.getLocalName(),
0);
OdfElement.copyAttributes(innerPropsElement, newPropsElement);
// KEEP INNER SPAN (ALWAYS - due to the content)
// point to the new style with @style:name
higherPrioSpan.setStyleName(newAutoStyle.getStyleNameAttribute());
} else if (!outerHasAutomatic
&& outerHasTemplate) { // *** INNER ONLY AUTO & OUTER ONLY TEMPLATE
// KEEP INNER STYLE:
// overtake OUTER template style parent
OdfStyle newAutoStyle = autoStyles.makeStyleUnique(innerAutoStyle);
String parentStyle;
if (outerAutoStyle == null) {
// if there is no empty automatic style, take the template style name from the span
parentStyle = lowerPrioSpan.getStyleName();
} else {
// if there is an empty automatic style, take the template style name from the style
parentStyle = outerAutoStyle.getStyleParentStyleNameAttribute();
}
newAutoStyle.setStyleParentStyleNameAttribute(parentStyle);
higherPrioSpan.setStyleName(newAutoStyle.getStyleNameAttribute());
} else { // *** INNER ONLY AUTO & OUTER BOTH
// KEEP OUTER STYLE:
// override OUTER properties
OdfStyle newAutoStyle = autoStyles.makeStyleUnique(outerAutoStyle);
StyleTextPropertiesElement newPropsElement =
(StyleTextPropertiesElement)
newAutoStyle.getChildElement(
StyleTextPropertiesElement.ELEMENT_NAME.getUri(),
StyleTextPropertiesElement.ELEMENT_NAME.getLocalName(),
0);
OdfElement.copyAttributes(innerPropsElement, newPropsElement);
// overtake OUTER template style parent
newAutoStyle.setStyleParentStyleNameAttribute(
innerAutoStyle.getStyleParentStyleNameAttribute());
// overtake OUTER span @style:name
higherPrioSpan.setStyleName(newAutoStyle.getStyleNameAttribute());
}
} else if (!innerHasAutomatic && innerHasTemplate) { // *** INNER ONLY TEMPLATE
if (outerHasAutomatic && !outerHasTemplate) { // *** INNER ONLY TEMPLATE & OUTER ONLY AUTO
// KEEP INNER
// create INNER automatic styles
OdfStyle newAutoStyle = autoStyles.makeStyleUnique(outerAutoStyle);
newAutoStyle.setStyleParentStyleNameAttribute(higherPrioSpan.getStyleName());
higherPrioSpan.setStyleName(newAutoStyle.getStyleNameAttribute());
} else if (!outerHasAutomatic
&& outerHasTemplate) { // *** INNER ONLY TEMPLATE & OUTER ONLY TEMPLATE
// KEEP INNER - delete OUTER STYLE & SPAN
// Done as the deletion of span is after these conditions
} else { // *** INNER ONLY TEMPLATE & OUTER BOTH
// KEEP INNER
// overtake OUTER properties element, by cloning OUTER style
OdfStyle newAutoStyle = autoStyles.makeStyleUnique(outerAutoStyle);
// overtake INNER template style parent
newAutoStyle.setStyleParentStyleNameAttribute(higherPrioSpan.getStyleName());
higherPrioSpan.setStyleName(newAutoStyle.getStyleNameAttribute());
}
} else { // *** INNER BOTH
if (outerHasAutomatic && !outerHasTemplate) { // *** INNER BOTH & OUTER ONLY AUTO
// KEEP OUTER:
// overtake OUTER properties element, by cloning OUTER style
OdfStyle newAutoStyle = autoStyles.makeStyleUnique(outerAutoStyle);
StyleTextPropertiesElement newPropsElement =
(StyleTextPropertiesElement)
newAutoStyle.getChildElement(
StyleTextPropertiesElement.ELEMENT_NAME.getUri(),
StyleTextPropertiesElement.ELEMENT_NAME.getLocalName(),
0);
OdfElement.copyAttributes(innerPropsElement, newPropsElement);
// overtake INNER template style parent
newAutoStyle.setStyleParentStyleNameAttribute(
innerAutoStyle.getStyleParentStyleNameAttribute());
higherPrioSpan.setStyleName(newAutoStyle.getStyleNameAttribute());
} else if (!outerHasAutomatic && outerHasTemplate) { // *** INNER BOTH & OUTER ONLY TEMPLATE
// KEEP INNER - delete OUTER STYLE & SPAN
// Done as the deletion is after these conditions
} else { // *** INNER BOTH & OUTER BOTH
// KEEP OUTER
// overtake OUTER properties element, by cloning OUTER style
OdfStyle newAutoStyle = autoStyles.makeStyleUnique(outerAutoStyle);
StyleTextPropertiesElement newPropsElement =
(StyleTextPropertiesElement)
newAutoStyle.getChildElement(
StyleTextPropertiesElement.ELEMENT_NAME.getUri(),
StyleTextPropertiesElement.ELEMENT_NAME.getLocalName(),
0);
OdfElement.copyAttributes(innerPropsElement, newPropsElement);
// overtake INNER template style parent
newAutoStyle.setStyleParentStyleNameAttribute(
innerAutoStyle.getStyleParentStyleNameAttribute());
higherPrioSpan.setStyleName(newAutoStyle.getStyleNameAttribute());
}
}
OdfElement.removeSingleElement(lowerPrioSpan);
}
return higherPrioSpan;
}
@Override
public String toString() {
return "[name: "
+ this.getStyleNameAttribute()
+ " family: "
+ this.getStyleFamilyAttribute()
+ "]";
}
}