blob: a5401da794b4d412ad8092b23bbb6929d8d05b27 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2011 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Jens Lukowski/Innoopract - initial renaming/restructuring
*
*******************************************************************************/
package org.eclipse.wst.dtd.core.internal;
import java.util.List;
import org.eclipse.wst.dtd.core.internal.parser.DTDRegionTypes;
import org.eclipse.wst.dtd.core.internal.text.RegionIterator;
import org.eclipse.wst.dtd.core.internal.util.DTDUniqueNameHelper;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
public class CMGroupNode extends CMRepeatableNode {
public static final char CHOICE = '|';
public static final char SEQUENCE = ',';
protected char connector = SEQUENCE;
// protected ArrayList children = new ArrayList();
public CMGroupNode(DTDFile file, IStructuredDocumentRegion flatNode) {
super(file, flatNode);
}
public void addChild() {
beginRecording(this, DTDCoreMessages._UI_LABEL_CM_GRP_NODE_ADD_CHILD); //$NON-NLS-1$
DTDNode lastNode = (DTDNode) getLastChild();
String elementName = DTDUniqueNameHelper.getUniqueName(getChildrenList(), "ChildNode"); //$NON-NLS-1$
if (lastNode != null) {
replaceText(this, lastNode.getEndOffset(), 0, String.valueOf(getConnector()) + elementName); //$NON-NLS-1$
}
else {
replaceText(this, getStartOffset() + 1, 0, elementName); //$NON-NLS-1$
}
endRecording(this);
}
public void addGroup() {
beginRecording(this, DTDCoreMessages._UI_LABEL_CM_GRP_NODE_ADD_GRP); //$NON-NLS-1$
DTDNode lastNode = (DTDNode) getLastChild();
if (lastNode != null) {
replaceText(this, lastNode.getEndOffset(), 0, String.valueOf(getConnector()) + " ()"); //$NON-NLS-1$
}
else {
replaceText(this, getStartOffset() + 1, 0, "()"); //$NON-NLS-1$
}
endRecording(this);
}
public void delete(Object requestor, DTDNode child) {
Object[] children = getChildren();
if (children.length == 1 && getFirstChild() == child) {
replaceText(requestor, child.getStartOffset(), child.getNodeLength(), null);
return;
}
for (int i = 0; i < children.length - 1; i++) {
DTDNode childA = (DTDNode) children[i];
DTDNode childB = (DTDNode) children[i + 1];
boolean childADeleted = childA == child;
boolean childBDeleted = childB == child;
if (childADeleted || childBDeleted) {
// we found the child
int startOffset = childADeleted ? childA.getStartOffset() : childA.getEndOffset();
int endOffset = childADeleted ? childB.getStartOffset() : childB.getEndOffset();
replaceText(requestor, startOffset, endOffset - startOffset, ""); //$NON-NLS-1$
removeChild(child);
break;
}
}
}
/**
* Get the value of connector.
*
* @return value of connector.
*/
public char getConnector() {
Object[] children = getChildren();
for (int i = 0; i < children.length - 1; i++) {
DTDNode childA = (DTDNode) children[i];
DTDNode childB = (DTDNode) children[i + 1];
// create a stream between the two siblings and walk it
// note that this stream includes the last region of the first
// sibling and the first region of the next sibling.
// both these should be ignored
RegionIterator iter = new RegionIterator(getStructuredDTDDocumentRegion(), childA.getEndOffset(), childB.getStartOffset());
// stream.setFirstRegion(childA.getLastRegion());
// stream.setLastRegion(childB.getFirstRegion());
// Iterator iter = stream.iterator();
// skip the first region which is the last region of childA
// do we need this now ?
// iter.next();
ITextRegion currentRegion = null;
while (iter.hasNext() && currentRegion != childB.getStartRegion()) {
currentRegion = iter.next();
if (currentRegion.getType() == DTDRegionTypes.CONNECTOR) {
connector = getStructuredDTDDocumentRegion().getText(currentRegion).charAt(0);
return connector;
}
}
}
return connector;
}
public String getImagePath() {
switch (getConnector()) {
case SEQUENCE :
return DTDResource.ONESEQUENCEICON;
/*
* switch (getOccurrence()) { case ONCE : return
* resourcePlugin.getImage(DTDResource.ONESEQUENCEICON); case
* OPTIONAL : return
* resourcePlugin.getImage(DTDResource.OPTIONALSEQUENCEICON); case
* ONE_OR_MORE : return
* resourcePlugin.getImage(DTDResource.ONEORMORESEQUENCEICON);
* case ZERO_OR_MORE : return
* resourcePlugin.getImage(DTDResource.ZEROORMORESEQUENCEICON); }
*/
case CHOICE :
return DTDResource.ONECHOICEICON;
/*
* switch (getOccurrence()) { case ONCE : return
* resourcePlugin.getImage(DTDResource.ONECHOICEICON); case OPTIONAL :
* return resourcePlugin.getImage(DTDResource.OPTIONALCHOICEICON);
* case ONE_OR_MORE : return
* resourcePlugin.getImage(DTDResource.ONEORMORECHOICEICON); case
* ZERO_OR_MORE : return
* resourcePlugin.getImage(DTDResource.ZEROORMORECHOICEICON); }
*/
}
return null;
}
public String getName() {
return ""; //$NON-NLS-1$
}
// returns the occurrenceregion, or the last region where the occurrence
// region should appear after
public ITextRegion getOccurrenceRegion() {
int nesting = 0;
// we skip past the first left paren we see since that is the
// beginning of our own node
RegionIterator iter = iterator();
// we assume the first region is the '('
iter.next();
ITextRegion currentRegion = null;
while (iter.hasNext() && nesting >= 0) {
currentRegion = iter.next();
if (currentRegion.getType() == DTDRegionTypes.LEFT_PAREN) {
nesting++;
}
if (currentRegion.getType() == DTDRegionTypes.RIGHT_PAREN) {
nesting--;
}
}
if (nesting < 0) {
// This means we have passed over the right paren that marks the
// end of our grouping.
// Look for an occurrence region
while (iter.hasNext()) {
currentRegion = iter.next();
if (currentRegion.getType() == DTDRegionTypes.OCCUR_TYPE) {
return currentRegion;
}
}
}
// if we're here, this means that there is no occur region. return the
// last region
return iter.previous();
}
public String getType() {
if (isRootElementContent()) {
if (getFirstChild() != null) {
CMNode node = (CMNode) getFirstChild();
if (node.getType().equals(PCDATA)) {
return MIXED;
}
else {
return CHILDREN;
}
}
}
return ""; //$NON-NLS-1$
}
public void insertChildNode(Object requestor, String nodeText, int position) {
Object[] children = getChildren();
int startOffset = 0;
String newText = ""; //$NON-NLS-1$
if (position < children.length) {
DTDNode reference = (DTDNode) children[position];
startOffset = reference.getStartOffset();
newText = nodeText + " " + String.valueOf(getConnector()) + " "; //$NON-NLS-1$ //$NON-NLS-2$
}
else if (position == children.length) {
// add to end
DTDNode reference = (DTDNode) children[position - 1];
startOffset = reference.getEndOffset();
newText = " " + String.valueOf(getConnector()) + " " + nodeText; //$NON-NLS-1$ //$NON-NLS-2$
}
replaceText(requestor, startOffset, 0, newText);
}
public void insertChildNode(String nodeText, int position) {
beginRecording(this, DTDCoreMessages._UI_LABEL_CM_GRP_NODE_INSERT_ELEMENT); //$NON-NLS-1$
insertChildNode(this, nodeText, position);
endRecording(this);
}
public void insertIntoModel(Object requestor, CMNode reference, CMNode node, boolean isAfter) {
String nodeText = node.getNodeText();
List children = getChildrenList();
int index = children.indexOf(reference);
if (index == -1) {
// no reference node, add it to the end??
index = children.size();
}
else {
// got an index. if we want to add after, increase by 1
index = isAfter ? index + 1 : index;
}
insertChildNode(requestor, nodeText, index);
}
public void resolveRegions() {
int nesting = 0;
// children.clear();
removeChildNodes();
DTDNode currentGroupNode = null;
CMBasicNode currentReferenceNode = null;
RegionIterator iter = iterator();
// we assume the first region is the '('
iter.next();
while (iter.hasNext() && nesting >= 0) {
ITextRegion currentRegion = iter.next();
if (nesting == 0) {
if (currentRegion.getType().equals(DTDRegionTypes.CONTENT_PCDATA)) {
currentGroupNode = currentReferenceNode = null;
DTDNode pcData = new CMBasicNode(getDTDFile(), getStructuredDTDDocumentRegion());
pcData.addRegion(currentRegion);
appendChild(pcData);
// children.add(pcData);
}
else if (currentRegion.getType().equals(DTDRegionTypes.NAME)) {
// we have hit a new reference node. Make sure we reset
// the groupnode var so it doesn't collect more regions
currentGroupNode = null;
currentReferenceNode = new CMBasicNode(getDTDFile(), getStructuredDTDDocumentRegion());
currentReferenceNode.addRegion(currentRegion);
appendChild(currentReferenceNode);
// children.add(currentReferenceNode);
}
else if (currentRegion.getType().equals(DTDRegionTypes.OCCUR_TYPE)) {
// we could potentially flag an error here if we hit an
// occurrence type and currentRefNode and currentGroupNode
// are null
if (currentReferenceNode != null) {
// currentReferenceNode.setOccurrence(currentRegion.getText().toCharArray()[0]);
currentReferenceNode.addRegion(currentRegion);
currentReferenceNode = null;
}
}
else if (currentRegion.getType().equals(DTDRegionTypes.CONNECTOR)) {
// note that if connector is already set and it is
// different from the current connector region, then we
// have an error!
// setConnector(currentRegion.getText().toCharArray()[0]);
}
else if (currentRegion.getType().equals(DTDRegionTypes.LEFT_PAREN)) {
if (currentGroupNode == null) {
// we have hit a new group. Make sure we reset the
// referencenode var so it doesn't collect any more
// regions
currentReferenceNode = null;
currentGroupNode = new CMGroupNode(getDTDFile(), getStructuredDTDDocumentRegion());
appendChild(currentGroupNode);
// children.add(currentGroupNode);
}
}
}
if (currentRegion.getType().equals(DTDRegionTypes.LEFT_PAREN)) {
nesting++;
}
if (currentRegion.getType().equals(DTDRegionTypes.RIGHT_PAREN)) {
nesting--;
if (nesting == 0 && currentGroupNode != null) {
currentGroupNode.addRegion(currentRegion);
// peek at next region to see if it is an occur region. if
// so, add it to the groupnode
if (iter.hasNext()) {
ITextRegion nextRegion = iter.next();
if (nextRegion.getType().equals(DTDRegionTypes.OCCUR_TYPE)) {
currentGroupNode.addRegion(nextRegion);
}
else {
// Otherwise, push it back as the next item to be
// retrieved by a future next() call
iter.previous();
}
}
currentGroupNode = null;
}
}
if (currentGroupNode != null) {
currentGroupNode.addRegion(currentRegion);
}
}
if (nesting < 0) {
// This means we have passed over the right paren that marks the
// end of our grouping.
// Look for an occurrence region
while (iter.hasNext()) {
ITextRegion currentRegion = iter.next();
if (currentRegion.getType().equals(DTDRegionTypes.OCCUR_TYPE)) {
// setOccurrence(currentRegion.getText().toCharArray()[0]);
}
} // end of while ()
}
// for (org.w3c.dom.Node child = getFirstChild(); child != null; child
// = child.getNextSibling())
// {
// System.out.println("child found = " + child);
// }
Object[] children = getChildren();
// System.out.println("children legnth = " + children.length);
for (int i = 0; i < children.length; i++) {
DTDNode currentNode = (DTDNode) children[i];
currentNode.resolveRegions();
} // end of while ()
}
/**
* Set the value of connector.
*
* @param v
* Value to assign to connector.
*/
public void setConnector(char v) {
if (connector != v) {
connector = v;
// walk through our kids and see if there is a connector between
// each sibling. if not, create one and set the connector. if
// there is
// then just change the text of the connector
Object[] children = getChildren();
if (children.length <= 1) {
// there won't be any connector existing between the children
// just notify a change in the node and return;
getDTDFile().notifyNodeChanged(this);
return;
}
beginRecording(this, DTDCoreMessages._UI_LABEL_CM_GRP_NODE_CONNECTOR); //$NON-NLS-1$
for (int i = 0; i < children.length - 1; i++) {
DTDNode childA = (DTDNode) children[i];
DTDNode childB = (DTDNode) children[i + 1];
// create a stream between the two siblings and walk it
// note that this stream includes the last region of the first
// sibling and the first region of the next sibling.
// both these should be ignored
RegionIterator iter = new RegionIterator(getStructuredDTDDocumentRegion(), childA.getEndOffset(), childB.getStartOffset());
// skip the first region which is the last region of childA
// do we still need this
// iter.next();
ITextRegion currentRegion = null;
boolean foundConnector = false;
while (iter.hasNext() && currentRegion != childB.getStartRegion()) {
currentRegion = iter.next();
if (currentRegion.getType() == DTDRegionTypes.CONNECTOR) {
foundConnector = true;
// Region oldRegion = currentRegion.createCopy();
// found a connector! on to the next sibling pair
// currentRegion.updateText(String.valueOf(v));
replaceText(this, getStructuredDTDDocumentRegion().getStartOffset(currentRegion), 1, String.valueOf(connector));
// changeStructuredDocument(oldRegion, currentRegion);
break;
}
}
if (!foundConnector) {
// if we're here, that means we need to insert a new
// connector region after childA
replaceText(this, childA.getEndOffset(), 0, String.valueOf(connector));
// DTDRegion connectorRegion = new
// DTDRegion(DTDRegionTypes.CONNECTOR,
// childA.getEndOffset(), 1);
// insertIntoStructuredDocument(connectorRegion);
}
}
endRecording(this);
}
}
// public Object[] getChildren()
// {
// return children.toArray();
// }
}// CMGroupNode