blob: 2f087a6b79c4f4d18b837be26dddb6b2b932cc48 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 Sybase, Inc. and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Sybase, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.jst.pagedesigner.commands.range;
import org.eclipse.jst.pagedesigner.IHTMLConstants;
import org.eclipse.jst.pagedesigner.commands.CommandResources;
import org.eclipse.jst.pagedesigner.dom.DOMRange;
import org.eclipse.jst.pagedesigner.dom.DOMRefPosition;
import org.eclipse.jst.pagedesigner.dom.DOMUtil;
import org.eclipse.jst.pagedesigner.dom.EditModelQuery;
import org.eclipse.jst.pagedesigner.dom.IDOMPosition;
import org.eclipse.jst.pagedesigner.viewer.IHTMLGraphicalViewer;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
/**
* @author mengbo
*/
public class ApplyStyleCommand extends RangeModeCommand {
private String _tag;
private String _cssProperty;
private String _cssPropertyValue;
private final Element _applyingNode;
/**
* @param viewer
* @param tag
* @param property
* @param value
*/
public ApplyStyleCommand(IHTMLGraphicalViewer viewer, String tag,
String property, String value) {
super(
CommandResources
.getString("ApplyStyleCommand.Label.ApplyStyle"), viewer); //$NON-NLS-1$
this._tag = tag;
this._cssProperty = property;
this._cssPropertyValue = value;
this._applyingNode = null;
}
/**
* @param viewer
* @param node
* @param property
* @param value
*/
public ApplyStyleCommand(IHTMLGraphicalViewer viewer, Element node,
String property, String value) {
super(
CommandResources
.getString("ApplyStyleCommand.Label.ApplyStyle"), viewer); //$NON-NLS-1$
this._applyingNode = node;
}
/**
* @return the applying node (may be null)
*/
protected final Element getApplyingNode() {
return _applyingNode;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jst.pagedesigner.commands.DesignerCommand#doExecute()
*/
protected DOMRange doRangeExecute(DOMRange range) {
if (range == null || range.isEmpty()) {
return null;
}
boolean ordered = range.isOrdered();
IDOMPosition start = ordered ? range.getStartPosition() : range
.getEndPosition();
IDOMPosition end = ordered ? range.getEndPosition() : range
.getStartPosition();
Node startContainer = start.getContainerNode();
Node endContainer = end.getContainerNode();
Node common = DOMUtil.findCommonAncester(start.getContainerNode(), end
.getContainerNode());
if (common == null) {
// should not happen.
return null;
}
if (common instanceof Text) {
// under the same Text scope
range = doTextNodeStyleApply((Text) common, start.getOffset(), end
.getOffset());
return range;
}
if (startContainer instanceof Text) {
// if the start offset is 0,then skip split the Text
if (start.getOffset() > 0) {
startContainer = ((Text) startContainer).splitText(start
.getOffset());
start = new DOMRefPosition(startContainer, false);
}
} else {
startContainer = start.getNextSiblingNode();
}
if (endContainer instanceof Text) {
if (end.getOffset() > 0) {
endContainer = ((Text) endContainer).splitText(end
.getOffset());
endContainer = endContainer.getPreviousSibling();
} else {
endContainer = endContainer.getPreviousSibling();
}
} else {
endContainer = end.getPreviousSiblingNode();
}
for (Node node = startContainer; node != endContainer; node = EditModelQuery
.getInstance().getNextLeafNeighbor(node)) {
if (EditModelQuery.hasAncestor(node, getTag(), true)) {
continue;
}
Element newnode = createStyleElement();
node.getParentNode().insertBefore(newnode, node);
newnode.appendChild(node);
}
if (!EditModelQuery.hasAncestor(endContainer, getTag(), true)) {
Element newnode = createStyleElement();
endContainer.getParentNode()
.insertBefore(newnode, endContainer);
newnode.appendChild(endContainer);
}
// merge the style tags
for (Node node = startContainer; node != endContainer; node = EditModelQuery
.getInstance().getNextLeafNeighbor(node)) {
Node stylenode = node;
while (stylenode != null
&& !stylenode.getNodeName().equalsIgnoreCase(getTag())) {
stylenode = stylenode.getParentNode();
}
if (stylenode == null) {
continue;
}
if (stylenode.getNextSibling() != null
&& stylenode.getNextSibling().getNodeName()
.equalsIgnoreCase(getTag())) {
Node sibling = stylenode.getNextSibling();
while (sibling.getFirstChild() != null) {
stylenode.appendChild(sibling.getFirstChild());
}
stylenode.getParentNode().removeChild(sibling);
node = startContainer;
}
}
return new DOMRange(start, end);
/*
* boolean ordered = range.isOrdered(); IDOMPosition start = ordered ?
* range.getStartPosition() : range.getEndPosition(); IDOMPosition end =
* ordered ? range.getEndPosition() : range.getStartPosition();
*
* Node common = DOMUtil.findCommonAncester(start.getContainerNode(),
* end.getContainerNode()); if (common == null) { // should not happen.
* return null; }
*
* DOMRange result = null; if (common instanceof Text) { result =
* doTextNodeStyleApply((Text) common, start.getOffset(),
* end.getOffset()); } else { IDOMPosition startPosition = start;
* IDOMPosition endPosition = end; Node ancester = common; DOMRange[]
* leftRange = new DOMRange[1]; DOMRange[] rightRange = new DOMRange[1];
*
* startPosition = partialApply(startPosition, ancester, true,
* leftRange); endPosition = partialApply(endPosition, ancester, false,
* rightRange); DOMRange middle = middleApply(ancester, startPosition,
* endPosition);
*
* IDOMPosition startref = null; if (leftRange[0] != null &&
* leftRange[0].getStartPosition() != null) { startref =
* leftRange[0].getStartPosition(); } else if (middle != null &&
* middle.getStartPosition() != null) { startref =
* middle.getStartPosition(); } else if (rightRange[0] != null &&
* rightRange[0].getStartPosition() != null) { startref =
* rightRange[0].getStartPosition(); }
*
* IDOMPosition endref = null; if (rightRange[0] != null &&
* rightRange[0].getEndPosition() != null) { endref =
* rightRange[0].getEndPosition(); } else if (middle != null &&
* middle.getEndPosition() != null) { endref = middle.getEndPosition(); }
* else if (leftRange[0] != null && leftRange[0].getEndPosition() !=
* null) { endref = leftRange[0].getEndPosition(); }
*
* if (startref == null) { result = null; } else { startref = new
* DOMPosition(EditModelQuery.getInstance().getNextLeafNeighbor(startref.getContainerNode()),
* 0); System.out.println(startref.toString()); endref = new
* DOMPosition(endref.getContainerNode(), 0); result = new
* DOMRange(startref, endref); } }
*
* if (result == null) { return null; }
*
* if (ordered) { return result; } else { return new
* DOMRange(result.getEndPosition(), result.getStartPosition()); }
*/
}
// TODO: unused code. Dead?
// private DOMRange middleApply(Node ancester, IDOMPosition startPosition,
// IDOMPosition endPosition) {
// startPosition = skip(startPosition, true);
// if (startPosition.getNextSiblingNode() == null
// || startPosition.getOffset() >= endPosition.getOffset()) {
// return null;
// } else {
// List needMove = new ArrayList();
// Node startNext = startPosition.getNextSiblingNode();
// Node endNext = endPosition.getNextSiblingNode();
// while (startNext != null && startNext != endNext) {
// needMove.add(startNext);
// startNext = startNext.getNextSibling();
// }
// Element newEle = createStyleElement();
// ancester.insertBefore(newEle, startPosition.getNextSiblingNode());
// for (int i = 0, n = needMove.size(); i < n; i++) {
// newEle.appendChild((Node) needMove.get(i));
// }
// return new DOMRange(new DOMRefPosition(newEle, false),
// new DOMRefPosition(newEle, true));
// }
// }
// TODO: unused code. Dead?
// private IDOMPosition partialApply(IDOMPosition position, Node ancester,
// boolean forward, DOMRange[] result) {
// IDOMPosition startRef = null, endRef = null;
//
// while (position != null && position.getContainerNode() != ancester) {
// Node container = position.getContainerNode();
// if (container instanceof Text) {
// // splitText will move the position up one level
// position = splitText(position);
// } else {
// // skip those nodes that can't have the style applied.
// position = skip(position, forward);
// Node sibling = position.getSibling(forward);
// if (sibling != null) {
// List needMove = new ArrayList();
// while (sibling != null) {
// needMove.add(sibling);
// sibling = forward ? sibling.getNextSibling() : sibling
// .getPreviousSibling();
// }
//
// // ok, there is nodes that need the style
// Element newEle = createStyleElement();
// container.insertBefore(newEle, position
// .getNextSiblingNode());
// for (int i = 0, size = needMove.size(); i < size; i++) {
// newEle.appendChild((Node) needMove.get(i));
// }
// if (startRef == null) {
// startRef = new DOMRefPosition(newEle, !forward);
// }
// endRef = new DOMRefPosition(newEle, forward);
// }
// // move the position up one level
// position = new DOMRefPosition(container, forward);
// }
// }
// if (startRef == null) {
// result[0] = null;
// } else {
// result[0] = forward ? new DOMRange(startRef, endRef)
// : new DOMRange(endRef, startRef);
// }
// return position;
// }
/**
* @param position
* @return
*/
// TODO: dead?
// private IDOMPosition splitText(IDOMPosition position) {
// Text text = (Text) position.getContainerNode();
// int offset = position.getOffset();
// if (offset <= 0) {
// return new DOMRefPosition(text, false);
// } else if (offset >= text.getData().length()) {
// return new DOMRefPosition(text, true);
// } else {
// text.splitText(offset);
// return new DOMRefPosition(text, true);
// }
// }
/**
* @param start
* @param end
* @param common
*/
private DOMRange doTextNodeStyleApply(Text textNode, int startOffset,
int endOffset) {
String data = textNode.getData();
String before = data.substring(0, startOffset);
String middle = data.substring(startOffset, endOffset);
String tail = data.substring(endOffset);
Text middleText = getModel().getDocument().createTextNode(middle);
// case 1: normal one
if (!isEmptyString(before) && !isEmptyString(tail)) {
Node parent = textNode.getParentNode();
parent.insertBefore(
getModel().getDocument().createTextNode(before), textNode);
Element bnode = createStyleElement();
bnode.appendChild(middleText);
parent.insertBefore(bnode, textNode);
textNode.setNodeValue(tail);
}
if (isEmptyString(before) && !isEmptyString(tail)) {
Node sibling = textNode.getPreviousSibling();
if (sibling != null
&& sibling.getNodeName().equalsIgnoreCase(getTag())) {
sibling.appendChild(middleText);
} else {
Node parent = textNode.getParentNode();
parent.insertBefore(getModel().getDocument().createTextNode(
before), textNode);
Element bnode = createStyleElement();
bnode.appendChild(middleText);
parent.insertBefore(bnode, textNode);
}
textNode.setNodeValue(tail);
}
if (!isEmptyString(before) && isEmptyString(tail)) {
Node sibling = textNode.getNextSibling();
textNode.setNodeValue(before);
if (sibling != null
&& sibling.getNodeName().equalsIgnoreCase(getTag())) {
sibling.insertBefore(middleText, sibling.getFirstChild());
} else {
Element bnode = createStyleElement();
bnode.appendChild(middleText);
textNode.getParentNode().insertBefore(bnode, sibling);
}
}
if (isEmptyString(before) && isEmptyString(tail)) {
Node previousSibling = textNode.getPreviousSibling();
Node nextSibling = textNode.getNextSibling();
//
if (getTag().equalsIgnoreCase(IHTMLConstants.TAG_P)) {
Element bnode = createStyleElement();
bnode.appendChild(middleText);
textNode.getParentNode().insertBefore(bnode, textNode);
textNode.getParentNode().removeChild(textNode);
}
//
else {
if (previousSibling != null
&& previousSibling.getNodeName().equalsIgnoreCase(
getTag()) && nextSibling != null
&& nextSibling.getNodeName().equalsIgnoreCase(getTag())) {
previousSibling.appendChild(middleText);
while (nextSibling.getFirstChild() != null) {
previousSibling
.appendChild(nextSibling.getFirstChild());
}
nextSibling.getParentNode().removeChild(nextSibling);
} else if (previousSibling != null
&& previousSibling.getNodeName().equalsIgnoreCase(
getTag())) {
previousSibling.appendChild(middleText);
} else if (nextSibling != null
&& nextSibling.getNodeName().equalsIgnoreCase(getTag())) {
nextSibling.insertBefore(middleText, nextSibling
.getFirstChild());
} else {
Element bnode = createStyleElement();
bnode.appendChild(middleText);
textNode.getParentNode().insertBefore(bnode, textNode);
}
textNode.getParentNode().removeChild(textNode);
}
}
return new DOMRange(new DOMRefPosition(middleText, false),
new DOMRefPosition(middleText, true));
}
private boolean isEmptyString(String str) {
if (str == null || str.length() == 0) {
return true;
}
return false;
}
/**
* @return a style element (cached on create)
*/
protected Element createStyleElement() {
if (_applyingNode != null) {
return _applyingNode;
}
Element element = getModel().getDocument().createElement(getTag());
if (_cssProperty != null && _cssPropertyValue != null) {
element.setAttribute(_cssProperty, _cssPropertyValue);
}
return element;
}
/**
* @param position
* @param b
* @return
*/
// TODO: dead?
// private IDOMPosition skip(IDOMPosition position, boolean forward) {
// Node node = position.getSibling(forward);
//
// if (node == null) {
// return position;
// }
// boolean canSkip = false;
// if (node instanceof Text) {
// canSkip = ((IDOMText) node).isElementContentWhitespace();
// } else if (node instanceof Element) {
// if (getTag().equalsIgnoreCase(((Element) node).getTagName())) {
// canSkip = true;
// } else {
// canSkip = false;
// }
// } else {
// canSkip = true;
// }
// if (canSkip) {
// return new DOMRefPosition(node, forward);
// } else {
// return position;
// }
// }
/**
* @return Returns the _cssProperty.
*/
public final String getCssProperty() {
return _cssProperty;
}
/**
* @param property
* The _cssProperty to set.
*/
public final void setCssProperty(String property) {
_cssProperty = property;
}
/**
* @return Returns the _cssPropertyValue.
*/
public final String getCssPropertyValue() {
return _cssPropertyValue;
}
/**
* @param propertyValue
* The _cssPropertyValue to set.
*/
public final void setCssPropertyValue(String propertyValue) {
_cssPropertyValue = propertyValue;
}
/**
* @return Returns the _tag.
*/
public final String getTag() {
if (_tag != null) {
return _tag;
}
return _applyingNode.getNodeName();
}
/**
* @param _tag
* The _tag to set.
*/
public final void setTag(String _tag) {
this._tag = _tag;
}
}