blob: d4e8d631fca92e142d61d896f473c2620b5377e6 [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.dom;
import org.eclipse.jst.jsf.common.ui.internal.logging.Logger;
import org.eclipse.jst.pagedesigner.IHTMLConstants;
import org.eclipse.jst.pagedesigner.PDPlugin;
import org.eclipse.jst.pagedesigner.utils.HTMLUtil;
import org.eclipse.jst.pagedesigner.validation.caret.IMovementMediator;
import org.eclipse.jst.pagedesigner.validation.caret.Target;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import org.w3c.dom.traversal.NodeIterator;
/**
* @author mengbo
*/
public class CaretMoveIterator {
private final static Logger _log = PDPlugin
.getLogger(CaretMoveIterator.class);
private final boolean INNER_DEBUG = false;
private NodeIterator _nodeIterator;
private IMovementMediator _validator;
private IDOMPosition _currentPosition;
private boolean _forward;
/**
* @param nodeIterator
* @param validator
* @param position
* @param forward
*/
public CaretMoveIterator(NodeIterator nodeIterator,
IMovementMediator validator, IDOMPosition position, boolean forward) {
super();
_nodeIterator = nodeIterator;
_validator = validator;
_currentPosition = position;
_forward = forward;
}
/**
* @return the node iterator
*/
public NodeIterator getNodeIterator() {
return _nodeIterator;
}
/**
* @return Returns the _currentPosition.
*/
public IDOMPosition getCurrentPosition() {
return _currentPosition;
}
/**
* @param position
* The _currentPosition to set.
*/
public void setCurrentPosition(IDOMPosition position) {
_currentPosition = position;
}
// assume the currentPosition is invalid
private IDOMPosition moveOut(Node container) {
IDOMPosition result = new DOMRefPosition(container, _forward);
String name = container.getNodeName();
if (name != null
&& EditModelQuery.HTML_STYLE_NODES.contains(name.toLowerCase())) {
result = moveToNextPosition(result, _validator);
}
return result;
}
/**
* @param node
* @return the dom position
*/
public IDOMPosition moveIn(Node node) {
IDOMPosition result = null;
if (INNER_DEBUG) {
_log.info("- Move into: " + node.getLocalName());
}
if (_validator.isEditable(new Target(node))) {
int index;
// Transparent text is not editable, so this is not transparent.
if (EditModelQuery.isText(node)) {
index = (_forward) ? 0 : ((Text) node).getData().length();
result = new DOMPosition(node, index);
// move ahead one pos.
IDOMPosition pos = getNextTextPosition(result);
if (pos != null) {
result = pos;
}
} else {
if (node.hasChildNodes()) {
index = _forward ? 0 : node.getChildNodes().getLength();
result = new DOMPosition(node, index); // DOMRefPosition(next,
// !_forward);
} else {
result = new DOMPosition(node, 0);
}
}
} else {
if (node.hasChildNodes()) {
Node child = _forward ? node.getFirstChild() : node
.getLastChild();
result = new DOMRefPosition(child, _forward);
while (child != null) {
if (_validator.allowsMoveIn(new Target(child))) {
result = moveIn(child);
break;
}
child = _forward ? child.getNextSibling() : child
.getPreviousSibling();
}
} else {
// Should be impposible to reach here.
result = new DOMPosition(node, 0);
}
}
return result;
}
private IDOMPosition getNextTextPosition(IDOMPosition position) {
IDOMPosition result = null;
String value = position.getContainerNode().getNodeValue();
int i = position.getOffset();
if (_forward) {
if (i < value.length()) {
if (HTMLUtil.isHTMLWhitespace(value.charAt(i))) {
while (i < value.length()
&& HTMLUtil.isHTMLWhitespace(value.charAt(i))) {
i++;
}
result = new DOMPosition(position.getContainerNode(), i);
} else if (i < value.length()) {
result = new DOMPosition(position.getContainerNode(), i + 1);
}
}
} else {
if (i > 0) {
if (HTMLUtil.isHTMLWhitespace(value.charAt(i - 1))) {
while (i > 0
&& HTMLUtil.isHTMLWhitespace(value.charAt(i - 1))) {
i--;
}
result = new DOMPosition(position.getContainerNode(), i);
} else if (i > 0) {
result = new DOMPosition(position.getContainerNode(), i - 1);
}
}
}
return result;
}
/**
* Assume the original position are valid.
*
* @param position
* @param validator
* @param _forward
* @param referenceImediatly
* @return
*/
private IDOMPosition moveToNextPosition(IDOMPosition position,
IMovementMediator validator) {
IDOMPosition currentPosition = null;
if (validator.isValidPosition(position) && position.isText()) {
currentPosition = getNextTextPosition(position);
}
if (currentPosition == null) {
Node nextNode = EditModelQuery.getInstance().getSibling(position,
_forward);
while (EditModelQuery.isText(nextNode)
&& ((Text) nextNode).getData().length() == 0) {
nextNode = EditModelQuery.getInstance().getSibling(nextNode,
_forward);
}
if (nextNode != null) {
// move in?
if (validator.allowsMoveIn(new Target(nextNode))) {
currentPosition = moveIn(nextNode);
// Stop when it is in table. For others we continue search
// for text.
if (!canStopHere(nextNode) && //
EditModelQuery.getInstance().getSibling(
currentPosition, _forward) != null) {
currentPosition = moveToNextPosition(currentPosition,
validator);
}
}
// not allowed to move in. e.g. it's empty string.
else {
currentPosition = new DOMRefPosition(nextNode, _forward);// skip(position);
}
} else {
if (validator.allowsMoveOut(new Target(
getNaviContainer(position)))) {
currentPosition = moveOut(getNaviContainer(position));
}
}
}
currentPosition = EditHelper.ensureDOMPosition(currentPosition);
if (currentPosition != null
&& !validator.isValidPosition(currentPosition)) {
currentPosition = moveToNextPosition(currentPosition, validator);
}
return currentPosition;
}
/**
* When the tag starts from new line, or in table, then caret can be put at
* 0 offset.
*
* @param node
* @return
*/
private boolean canStopHere(Node node) {
boolean result = false;
if (EditModelQuery.isText(node)) {
result = true;
} else if (node != null && node.getNodeName() != null) {
result |= node.getNodeName().equals(IHTMLConstants.TAG_TD);
result |= EditModelQuery.isBlockNode(node);
}
return result;
}
/**
* Move operation position to next edit position. We may need rule to valid
* it based on operation ID and direction. We need to pack transparent
* string.
*
* @param currentPosition
* @param forward
* @param validator
* @return the dom position
*/
public IDOMPosition moveToNextEditPosition(IDOMPosition currentPosition,
boolean forward, IMovementMediator validator) {
IDOMPosition result = null;
if ((currentPosition = moveToNextPosition(currentPosition, validator)) != null) {
result = currentPosition;
} else {
result = _currentPosition;
}
return result;
}
private Node getNaviContainer(IDOMPosition position) {
if (position.isText()) {
return position.getContainerNode().getParentNode();
}
return position.getContainerNode();
}
}