blob: 43cc37117f05c1e7d058c53c08ff2ba1f94b9e2c [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 java.util.ArrayList;
import java.util.List;
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.eclipse.wst.xml.core.internal.provisional.document.IDOMText;
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;
protected Element _applyingNode;
/**
* @param label
* @param viewer
*/
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;
}
public ApplyStyleCommand(IHTMLGraphicalViewer viewer, Element node,
String property, String value) {
super(
CommandResources
.getString("ApplyStyleCommand.Label.ApplyStyle"), viewer); //$NON-NLS-1$
this._applyingNode = node;
}
/*
* (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;
} else {
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()); }
*/
}
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));
}
}
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
*/
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
*/
protected Element createStyleElement() {
if (_applyingNode != null) {
return _applyingNode;
} else {
Element element = getModel().getDocument().createElement(getTag());
if (_cssProperty != null && _cssPropertyValue != null) {
element.setAttribute(_cssProperty, _cssPropertyValue);
}
return element;
}
}
/**
* @param position
* @param b
* @return
*/
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;
} else {
return _applyingNode.getNodeName();
}
}
/**
* @param _tag
* The _tag to set.
*/
public final void setTag(String _tag) {
this._tag = _tag;
}
private void updateStyleElement() {
}
}