/*******************************************************************************
* Copyright 2012-present Pixate, Inc.
*
* 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 com.pixate.freestyle.styling.adapters;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.pixate.freestyle.annotations.PXDocElement;
import com.pixate.freestyle.styling.PXDeclaration;
import com.pixate.freestyle.styling.PXRuleSet;
import com.pixate.freestyle.styling.stylers.PXStyler;
import com.pixate.freestyle.styling.stylers.PXStylerContext;
@PXDocElement(hide=true)
public class PXDOMStyleAdapter extends PXStyleAdapter {
private static PXDOMStyleAdapter sInstance;
private PXDOMStyleAdapter() {
}
@Override
protected List<PXStyler> createStylers() {
return Collections.emptyList();
}
@Override
public boolean updateStyle(List<PXRuleSet> ruleSets, List<PXStylerContext> contexts) {
for (int i = 0; i < ruleSets.size(); i++) {
PXStylerContext context = contexts.get(i);
PXRuleSet ruleSet = ruleSets.get(i);
Node node = (Node) context.getStyleable();
Document ownerDocument = node.getOwnerDocument();
NamedNodeMap attributes = node.getAttributes();
for (PXDeclaration declaration : ruleSet.getDeclarations()) {
String name = declaration.getName();
String value = declaration.getStringValue();
// Set the node's attribute
Node attNode = ownerDocument.createAttribute(name);
attNode.setNodeValue(value);
attributes.setNamedItem(attNode);
}
}
return true;
}
@Override
public String getElementName(Object object) {
return ((Node) object).getNodeName();
}
@Override
public String getStyleId(Object object) {
return getAttributeValue(object, "id");
}
@Override
public String getStyleClass(Object object) {
return getAttributeValue(object, "class");
}
@Override
public int getIndexInParent(Object styleable) {
Node node = (Node) styleable;
Node parentNode = node.getParentNode();
if (parentNode != null) {
NodeList siblings = parentNode.getChildNodes();
// find the node's index
for (int i = 0; i < siblings.getLength(); i++) {
Node sibling = siblings.item(i);
if (sibling == node) {
return i;
}
}
}
return -1;
}
@Override
public String getElementNamespace(Object styleable) {
return ((Node) styleable).getNamespaceURI();
}
@Override
public String getAttributeValue(Object styleable, String attribute) {
Node node = (Node) styleable;
NamedNodeMap attributes = node.getAttributes();
String result = null;
if (attributes != null) {
Node idAttr = attributes.getNamedItem(attribute);
if (idAttr != null) {
result = idAttr.getNodeValue();
}
}
return result;
}
@Override
public String getAttributeValue(Object styleable, String attributeName, String namespaceURI) {
Node node = (Node) styleable;
if (node.hasAttributes()) {
NamedNodeMap attributes = node.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Node attr = attributes.item(i);
if (attr.getLocalName().equals(attributeName)) {
// check for any namespace prefix (TODO - we check for the
// attribute prefix and for the node's namespace URI here.
// There is a chance that only one way is needed, but for
// now we keep both)
if (namespaceURI == null) {
return attr.getNamespaceURI() == null ? attr.getNodeValue() : null;
}
if ("*".equals(namespaceURI) || namespaceURI.equals(attr.getNamespaceURI())) {
return attr.getNodeValue();
}
}
}
}
return null;
}
@Override
public Object getSiblingAt(Object styleable, int offset) {
return getSiblingAt((Node) styleable, offset, true);
}
/**
* Returns the element children of the given node (Note: only the
* ELEMENT_NODEs will be returned)
*
* @param node
* @return
*/
@Override
public List<Object> getElementChildren(Object styleable) {
return getElementChildren((Node) styleable, true);
}
@Override
public int getChildCount(Object styleable) {
List<Object> children = getElementChildren(styleable);
return children == null ? 0 : children.size();
}
@Override
public Object getParent(Object styleable) {
return ((Node) styleable).getParentNode();
}
@Override
public boolean isSupportingStylers() {
return false;
}
// Private
private Object getSiblingAt(Node node, int offset, boolean skipNonElement) {
Object result = null;
int indexInParent = getIndexInParent(node);
if (indexInParent != -1) {
int siblingIndex = indexInParent + offset;
NodeList siblings = node.getParentNode().getChildNodes();
// Get the sibling at the offset. In case it's out of
// bounds, return null.
while (result == null) {
if (siblingIndex >= 0 && siblingIndex < siblings.getLength()) {
Node sibling = siblings.item(siblingIndex);
if (skipNonElement && sibling.getNodeType() != Document.ELEMENT_NODE) {
// skip the non-element node
siblingIndex += (offset < 0) ? -1 : 1;
} else {
result = sibling;
break;
}
} else {
// out of bounds
break;
}
}
}
return result;
}
/**
* Returns the children of the given node. In case 'onlyElements' is passed,
* only the ELEMENT_NODEs will be returned.
*
* @param node
* @param onlyElements
* @return
*/
private List<Object> getElementChildren(Node node, boolean onlyElements) {
NodeList childNodes = node.getChildNodes();
if (childNodes == null) {
return Collections.emptyList();
}
List<Object> children = new ArrayList<Object>(childNodes.getLength());
for (int i = 0; i < childNodes.getLength(); i++) {
short nodeType = childNodes.item(i).getNodeType();
if (!onlyElements
|| (onlyElements && nodeType != Document.COMMENT_NODE && nodeType != Document.PROCESSING_INSTRUCTION_NODE)) {
children.add(childNodes.item(i));
}
}
return children;
}
// Statics
public static PXDOMStyleAdapter getInstance() {
synchronized (PXDOMStyleAdapter.class) {
if (sInstance == null) {
sInstance = new PXDOMStyleAdapter();
}
}
return sInstance;
}
}