Initialize
[sdk/ide/product.git] / org.eclipse.jst.pagedesigner / src / org / eclipse / jst / pagedesigner / dom / CaretMoveIterator.java
1 /*******************************************************************************\r
2  * Copyright (c) 2006 Sybase, Inc. and others.\r
3  *\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     Sybase, Inc. - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.eclipse.jst.pagedesigner.dom;\r
13 \r
14 import org.eclipse.jst.jsf.common.ui.internal.logging.Logger;\r
15 import org.eclipse.jst.pagedesigner.IHTMLConstants;\r
16 import org.eclipse.jst.pagedesigner.PDPlugin;\r
17 import org.eclipse.jst.pagedesigner.utils.HTMLUtil;\r
18 import org.eclipse.jst.pagedesigner.validation.caret.IMovementMediator;\r
19 import org.eclipse.jst.pagedesigner.validation.caret.Target;\r
20 import org.w3c.dom.Node;\r
21 import org.w3c.dom.Text;\r
22 import org.w3c.dom.traversal.NodeIterator;\r
23 \r
24 /**\r
25  * @author mengbo\r
26  */\r
27 public class CaretMoveIterator {\r
28         private final static Logger _log = PDPlugin\r
29                         .getLogger(CaretMoveIterator.class);\r
30 \r
31         private final boolean INNER_DEBUG = false;\r
32 \r
33         private NodeIterator _nodeIterator;\r
34 \r
35         private IMovementMediator _validator;\r
36 \r
37         private IDOMPosition _currentPosition;\r
38 \r
39         private boolean _forward;\r
40 \r
41         /**\r
42          * @param nodeIterator \r
43          * @param validator \r
44          * @param position \r
45          * @param forward \r
46          */\r
47         public CaretMoveIterator(NodeIterator nodeIterator,\r
48                         IMovementMediator validator, IDOMPosition position, boolean forward) {\r
49                 super();\r
50                 _nodeIterator = nodeIterator;\r
51                 _validator = validator;\r
52                 _currentPosition = position;\r
53                 _forward = forward;\r
54         }\r
55 \r
56         /**\r
57          * @return the node iterator\r
58          */\r
59         public NodeIterator getNodeIterator() {\r
60                 return _nodeIterator;\r
61         }\r
62 \r
63         /**\r
64          * @return Returns the _currentPosition.\r
65          */\r
66         public IDOMPosition getCurrentPosition() {\r
67                 return _currentPosition;\r
68         }\r
69 \r
70         /**\r
71          * @param position\r
72          *            The _currentPosition to set.\r
73          */\r
74         public void setCurrentPosition(IDOMPosition position) {\r
75                 _currentPosition = position;\r
76         }\r
77 \r
78         // assume the currentPosition is invalid\r
79         private IDOMPosition moveOut(Node container) {\r
80                 IDOMPosition result = new DOMRefPosition(container, _forward);\r
81                 String name = container.getNodeName();\r
82                 if (name != null\r
83                                 && EditModelQuery.HTML_STYLE_NODES.contains(name.toLowerCase())) {\r
84                         result = moveToNextPosition(result, _validator);\r
85                 }\r
86                 return result;\r
87         }\r
88 \r
89         /**\r
90          * @param node\r
91          * @return the dom position\r
92          */\r
93         public IDOMPosition moveIn(Node node) {\r
94                 IDOMPosition result = null;\r
95                 if (INNER_DEBUG) {\r
96                         _log.info("- Move into: " + node.getLocalName()); //$NON-NLS-1$\r
97                 }\r
98                 if (_validator.isEditable(new Target(node))) {\r
99                         int index;\r
100                         // Transparent text is not editable, so this is not transparent.\r
101                         if (EditModelQuery.isText(node)) {\r
102                                 index = (_forward) ? 0 : ((Text) node).getData().length();\r
103                                 result = new DOMPosition(node, index);\r
104                                 // move ahead one pos.\r
105                                 IDOMPosition pos = getNextTextPosition(result);\r
106                                 if (pos != null) {\r
107                                         result = pos;\r
108                                 }\r
109                         } else {\r
110                                 if (node.hasChildNodes()) {\r
111                                         index = _forward ? 0 : node.getChildNodes().getLength();\r
112                                         result = new DOMPosition(node, index); // DOMRefPosition(next,\r
113                                         // !_forward);\r
114                                 } else {\r
115                                         result = new DOMPosition(node, 0);\r
116                                 }\r
117                         }\r
118                 } else {\r
119                         if (node.hasChildNodes()) {\r
120                                 Node child = _forward ? node.getFirstChild() : node\r
121                                                 .getLastChild();\r
122                                 result = new DOMRefPosition(child, _forward);\r
123                                 while (child != null) {\r
124                                         if (_validator.allowsMoveIn(new Target(child))) {\r
125                                                 result = moveIn(child);\r
126                                                 break;\r
127                                         }\r
128                                         child = _forward ? child.getNextSibling() : child\r
129                                                         .getPreviousSibling();\r
130                                 }\r
131                         } else {\r
132                                 // Should be impposible to reach here.\r
133                                 result = new DOMPosition(node, 0);\r
134                         }\r
135                 }\r
136                 return result;\r
137         }\r
138 \r
139         private IDOMPosition getNextTextPosition(IDOMPosition position) {\r
140                 IDOMPosition result = null;\r
141                 String value = position.getContainerNode().getNodeValue();\r
142                 int i = position.getOffset();\r
143                 if (_forward) {\r
144                         if (i < value.length()) {\r
145                                 if (HTMLUtil.isHTMLWhitespace(value.charAt(i))) {\r
146                                         while (i < value.length()\r
147                                                         && HTMLUtil.isHTMLWhitespace(value.charAt(i))) {\r
148                                                 i++;\r
149                                         }\r
150                                         result = new DOMPosition(position.getContainerNode(), i);\r
151                                 } else if (i < value.length()) {\r
152                                         result = new DOMPosition(position.getContainerNode(), i + 1);\r
153                                 }\r
154                         }\r
155                 } else {\r
156                         if (i > 0) {\r
157                                 if (HTMLUtil.isHTMLWhitespace(value.charAt(i - 1))) {\r
158                                         while (i > 0\r
159                                                         && HTMLUtil.isHTMLWhitespace(value.charAt(i - 1))) {\r
160                                                 i--;\r
161                                         }\r
162                                         result = new DOMPosition(position.getContainerNode(), i);\r
163                                 } else if (i > 0) {\r
164                                         result = new DOMPosition(position.getContainerNode(), i - 1);\r
165                                 }\r
166                         }\r
167                 }\r
168                 return result;\r
169         }\r
170 \r
171         /**\r
172          * Assume the original position are valid.\r
173          * \r
174          * @param position\r
175          * @param validator\r
176          * @param _forward\r
177          * @param referenceImediatly\r
178          * @return\r
179          */\r
180         private IDOMPosition moveToNextPosition(IDOMPosition position,\r
181                         IMovementMediator validator) {\r
182                 IDOMPosition currentPosition = null;\r
183                 if (validator.isValidPosition(position) && position.isText()) {\r
184                         currentPosition = getNextTextPosition(position);\r
185                 }\r
186                 if (currentPosition == null) {\r
187                         Node nextNode = EditModelQuery.getInstance().getSibling(position,\r
188                                         _forward);\r
189                         while (EditModelQuery.isText(nextNode)\r
190                                         && ((Text) nextNode).getData().length() == 0) {\r
191                                 nextNode = EditModelQuery.getInstance().getSibling(nextNode,\r
192                                                 _forward);\r
193                         }\r
194                         if (nextNode != null) {\r
195                                 // move in?\r
196                                 if (validator.allowsMoveIn(new Target(nextNode))) {\r
197                                         currentPosition = moveIn(nextNode);\r
198                                         // Stop when it is in table. For others we continue search\r
199                                         // for text.\r
200                                         if (!canStopHere(nextNode) && //\r
201                                                         EditModelQuery.getInstance().getSibling(\r
202                                                                         currentPosition, _forward) != null) {\r
203                                                 currentPosition = moveToNextPosition(currentPosition,\r
204                                                                 validator);\r
205                                         }\r
206                                 }\r
207                                 // not allowed to move in. e.g. it's empty string.\r
208                                 else {\r
209                                         currentPosition = new DOMRefPosition(nextNode, _forward);// skip(position);\r
210                                 }\r
211                         } else {\r
212                                 if (validator.allowsMoveOut(new Target(\r
213                                                 getNaviContainer(position)))) {\r
214                                         currentPosition = moveOut(getNaviContainer(position));\r
215                                 }\r
216                         }\r
217                 }\r
218                 currentPosition = EditHelper.ensureDOMPosition(currentPosition);\r
219                 if (currentPosition != null\r
220                                 && !validator.isValidPosition(currentPosition)) {\r
221                         currentPosition = moveToNextPosition(currentPosition, validator);\r
222                 }\r
223                 return currentPosition;\r
224         }\r
225 \r
226         /**\r
227          * When the tag starts from new line, or in table, then caret can be put at\r
228          * 0 offset.\r
229          * \r
230          * @param node\r
231          * @return\r
232          */\r
233         private boolean canStopHere(Node node) {\r
234                 boolean result = false;\r
235                 if (EditModelQuery.isText(node)) {\r
236                         result = true;\r
237                 } else if (node != null && node.getNodeName() != null) {\r
238                         result |= node.getNodeName().equals(IHTMLConstants.TAG_TD);\r
239                         result |= EditModelQuery.isBlockNode(node);\r
240                 }\r
241                 return result;\r
242         }\r
243 \r
244         /**\r
245          * Move operation position to next edit position. We may need rule to valid\r
246          * it based on operation ID and direction. We need to pack transparent\r
247          * string.\r
248          * \r
249          * @param currentPosition\r
250          * @param forward\r
251          * @param validator\r
252          * @return the dom position\r
253          */\r
254         public IDOMPosition moveToNextEditPosition(IDOMPosition currentPosition,\r
255                         boolean forward, IMovementMediator validator) {\r
256                 IDOMPosition result = null;\r
257                 if ((currentPosition = moveToNextPosition(currentPosition, validator)) != null) {\r
258                         result = currentPosition;\r
259                 } else {\r
260                         result = _currentPosition;\r
261                 }\r
262                 return result;\r
263         }\r
264 \r
265         private Node getNaviContainer(IDOMPosition position) {\r
266                 if (position.isText()) {\r
267                         return position.getContainerNode().getParentNode();\r
268                 }\r
269         return position.getContainerNode();\r
270         }\r
271 }\r