/******************************************************************************* * 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