blob: fb2fd64a27f5e3a304df22bec188594cc05de10c [file] [log] [blame]
/*
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.odftoolkit.simple.common.navigation;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;
import org.odftoolkit.odfdom.dom.element.OdfStylableElement;
import org.odftoolkit.odfdom.dom.style.OdfStyleFamily;
import org.odftoolkit.odfdom.dom.style.props.OdfStyleProperty;
import org.odftoolkit.odfdom.incubator.doc.style.OdfDefaultStyle;
import org.odftoolkit.odfdom.incubator.doc.style.OdfStyle;
import org.odftoolkit.odfdom.incubator.doc.text.OdfTextHeading;
import org.odftoolkit.odfdom.incubator.doc.text.OdfTextParagraph;
import org.odftoolkit.odfdom.pkg.OdfElement;
import org.odftoolkit.simple.TextDocument;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* A derived <code>Navigation</code> class used to navigate the text content,
* which can search the document and find matched style properties and return
* <code>TextSelection</code> instance(s).
*/
public class TextStyleNavigation extends Navigation {
private TextDocument mTextDocument;
private TextSelection mNextSelectedItem;
private TextSelection mTempSelectedItem;
private int mCurrentIndex;
private Map<OdfStyleProperty, String> mProps;
private String mText;
private Node mPhNode;
private int mIndex;
private Node mNode;
/**
* Construct <code>TextStyleNavigation</code> with style properties condition and
* navigation scope.
*
* @param props
* the matched style properties conditions
* @param doc
* the navigation search scope
*/
public TextStyleNavigation(Map<OdfStyleProperty, String> props, TextDocument doc) {
mTextDocument = doc;
mNextSelectedItem = null;
mTempSelectedItem = null;
this.mProps = props;
}
/**
* Check if has next <code>TextSelection</code> with satisfied style.
*
* @see org.odftoolkit.simple.common.navigation.Navigation#hasNext()
*/
@Override
public boolean hasNext() {
mTempSelectedItem = findNext(mNextSelectedItem);
return (mTempSelectedItem != null);
}
/**
* Get next <code>TextSelection</code>.
*
* @see org.odftoolkit.simple.common.navigation.Navigation#nextSelection()
*/
@Override
public Selection nextSelection() {
if(mTempSelectedItem !=null){
mNextSelectedItem = mTempSelectedItem;
mTempSelectedItem = null;
}else{
mNextSelectedItem = findNext(mNextSelectedItem);
}
if (mNextSelectedItem == null) {
return null;
} else {
Selection.SelectionManager.registerItem(mNextSelectedItem);
return mNextSelectedItem;
}
}
/**
* Check if the element has specified style properties, which are stated
* when the <code>TextStyleNavigation</code> created.
*
* @param element
* navigate this element
* @return true if this element has the specified style properties false if
* not match
*/
@Override
public boolean match(Node element) {
boolean match = false;
if (element.getNodeType() == Node.TEXT_NODE && !element.getNodeValue().trim().equals("")) {
if (element.getParentNode() instanceof OdfStylableElement) {
OdfStylableElement parStyleElement = (OdfStylableElement) element.getParentNode();
String parStyleName = getStyleName(parStyleElement);
if (getMatchStyleNames().contains(parStyleName)) {
match = true;
mText = element.getNodeValue();
NodeList nodes = getPHElement(element.getParentNode()).getChildNodes();
mIndex = 0;
getIndex(nodes, element);
}
}
}
return match;
}
/*
* Find next <code>TextSelection</code> which match specified style.
*/
private TextSelection findNext(TextSelection selected) {
OdfElement element = null;
try {
Node rootNode = mTextDocument.getContentRoot();
if (selected == null) {
mNode = getNextMatchElementInTree(rootNode, rootNode);
} else {
mNode = getNextMatchElementInTree(mNode, rootNode);
}
} catch (Exception ex) {
Logger.getLogger(TextStyleNavigation.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
}
if (mNode != null) {
element = (OdfElement) getPHElement(mNode);
TextSelection item = new TextSelection(this, mText, element,
mCurrentIndex);
return item;
}
return null;
}
private Node getPHElement(Node node) {
// get paragraph or heading element
if (node instanceof OdfTextParagraph) {
mPhNode = node;
} else if (node instanceof OdfTextHeading) {
mPhNode = node;
} else {
getPHElement(node.getParentNode());
}
return mPhNode;
}
private void getIndex(NodeList nodes, Node element) {
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
if (node == element) {
mCurrentIndex = mIndex;
break;
} else {
if (node.getNodeType() == Node.TEXT_NODE) {
mIndex = mIndex + node.getNodeValue().length();
} else if (node.getNodeType() == Node.ELEMENT_NODE) {
// mText:s
if (node.getLocalName().equals("s")) {
try {
mIndex = mIndex
+ Integer.parseInt(((Element) node).getAttributeNS(OdfDocumentNamespace.TEXT
.getUri(), "c"));
} catch (Exception e) {
mIndex++;
}
} else if (node.getLocalName().equals("line-break")) {
mIndex++;
} else if (node.getLocalName().equals("tab")) {
mIndex++;
} else {
getIndex(node.getChildNodes(), element);
}
}
}
}
}
private String getStyleName(OdfStylableElement element) {
String stylename = element.getStyleName();
if (stylename == null) {
if (element.getParentNode() instanceof OdfStylableElement) {
getStyleName((OdfStylableElement) element.getParentNode());
} else {
stylename = "defaultstyle";
}
}
return stylename;
}
private Set<String> getMatchStyleNames() {
Set<String> styleNames = new HashSet<String>();
String sname;
HashMap<String, OdfDefaultStyle> defaultStyles = new HashMap<String, OdfDefaultStyle>();
try {
NodeList defStyleList = mTextDocument.getDocumentStyles().getElementsByTagName("style:default-style");
for (int i = 0; i < defStyleList.getLength(); i++) {
OdfDefaultStyle defStyle = (OdfDefaultStyle) defStyleList.item(i);
defaultStyles.put(defStyle.getFamilyName(), defStyle);
}
NodeList styleList = mTextDocument.getDocumentStyles().getElementsByTagName("style:style");
for (int i = 0; i < styleList.getLength(); i++) {
OdfStyle sStyle = (OdfStyle) styleList.item(i);
// get default properties and style properties
Map<OdfStyleProperty, String> map = sStyle.getStylePropertiesDeep();
// check if properties include all search properties and value
// equal
Iterator<OdfStyleProperty> pIter = mProps.keySet().iterator();
boolean isStyle = false;
while (pIter.hasNext()) {
isStyle = false;
OdfStyleProperty p = pIter.next();
if (map.containsKey(p)) {
if (map.get(p).equals(mProps.get(p))) {
isStyle = true;
} else {
break;
}
} else {
break;
}
}
// put all match style names
if (isStyle) {
sname = sStyle.getStyleNameAttribute();
// if(sname.contains("default"))sname="defaultstyle";
styleNames.add(sname);
}
}
// get all automatic styles
Iterator<OdfStyle> cStyles = mTextDocument.getContentDom().getAutomaticStyles().getAllStyles().iterator();
while (cStyles.hasNext()) {
OdfStyle cStyle = cStyles.next();
// get default properties and style properties
Map<OdfStyleProperty, String> map = cStyle.getStylePropertiesDeep();
if (cStyle.getParentStyle() == null) {
if (cStyle.getFamilyName().equals("text")) {
if (defaultStyles.containsKey("text")) {
getTextDefaultProperties("text", defaultStyles, map);
} else {
getTextDefaultProperties("paragraph", defaultStyles, map);
}
}
}
// check if the search properties is in properties
Iterator<OdfStyleProperty> pIter = mProps.keySet().iterator();
boolean isStyle = false;
while (pIter.hasNext()) {
isStyle = false;
OdfStyleProperty p = pIter.next();
if (map.containsKey(p)) {
if (map.get(p).equals(mProps.get(p))) {
isStyle = true;
} else {
break;
}
} else {
break;
}
}
// put all match style names
if (isStyle) {
styleNames.add(cStyle.getStyleNameAttribute());
}
}
} catch (Exception e1) {
Logger.getLogger(TextStyleNavigation.class.getName()).log(Level.SEVERE, e1.getMessage(), e1);
}
return styleNames;
}
private void getTextDefaultProperties(String familyName, HashMap<String, OdfDefaultStyle> defaultStyles,
Map<OdfStyleProperty, String> map) {
OdfDefaultStyle defStyle = defaultStyles.get(familyName);
if (defStyle != null) {
OdfStyleFamily family = defStyle.getFamily();
if (family != null) {
for (OdfStyleProperty property : family.getProperties()) {
if (!map.containsKey(property) && defStyle.hasProperty(property)) {
map.put(property, defStyle.getProperty(property));
}
}
}
}
}
}