blob: 615d1ed43870e73efa4bef5a399ed6f06cc667d9 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2006 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
* Hajime Katayama - bug 245216 - correction to get the best node when the ending
* and starting offsets are equal, and they both
* contain the offset range.
*
*******************************************************************************/
package org.eclipse.wst.dtd.core.internal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.graphics.Image;
import org.eclipse.wst.dtd.core.internal.document.DTDModelImpl;
import org.eclipse.wst.dtd.core.internal.event.IDTDFileListener;
import org.eclipse.wst.dtd.core.internal.event.NodesEvent;
import org.eclipse.wst.dtd.core.internal.parser.DTDRegionTypes;
import org.eclipse.wst.dtd.core.internal.util.DTDExternalReferenceRemover;
import org.eclipse.wst.dtd.core.internal.util.DTDModelUpdater;
import org.eclipse.wst.dtd.core.internal.util.DTDNotationReferenceRemover;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.sse.core.internal.provisional.events.NewDocumentEvent;
import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent;
import org.eclipse.wst.sse.core.internal.provisional.events.RegionsReplacedEvent;
import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentRegionsReplacedEvent;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
public class DTDFile implements IndexedRegion {
private NodeList attlistList = new NodeList(this, DTDRegionTypes.ATTLIST_TAG);
private NodeList commentList = new NodeList(this, DTDRegionTypes.COMMENT_START);
boolean creatingNewModel = false;
private DTDModelImpl fDTDModel;
private NodeList elementList = new NodeList(this, DTDRegionTypes.ELEMENT_TAG);
private NodeList entityList = new NodeList(this, DTDRegionTypes.ENTITY_TAG);
private List folderList = null;
private boolean isMovingNode = false;
private List modelListeners = new ArrayList();
private List nodeList = new ArrayList();
private NodeList notationList = new NodeList(this, DTDRegionTypes.NOTATION_TAG);
private NodeList unrecognizedList = new NodeList(this, DTDRegionTypes.UNKNOWN_CONTENT);
public DTDFile(DTDModelImpl dtdModel) {
this.fDTDModel = dtdModel;
}
public void addDTDFileListener(IDTDFileListener listener) {
modelListeners.add(listener);
}
protected void addNode(DTDNode node) {
addNode(nodeList.size(), node);
}
protected void addNode(int index, DTDNode node) {
nodeList.add(index, node);
/*
* if (index < nodeList.size()) { insertBefore(node, (DTDNode)
* nodeList.get(index)); } else { appendChild(node); }
*/
}
public DTDNode buildNode(IStructuredDocumentRegion flatNode) {
// ITextRegionList regions = flatNode.getRegions();
DTDNode node = null;
if (isElement(flatNode)) {
// then this is an element
node = new Element(this, flatNode);
}
else if (isEntity(flatNode)) {
node = new Entity(this, flatNode);
}
else if (isNotation(flatNode)) {
node = new Notation(this, flatNode);
}
else if (isAttributeList(flatNode)) {
node = new AttributeList(this, flatNode);
}
else if (isComment(flatNode)) {
node = new Comment(this, flatNode);
}
else if (isParameterEntityReference(flatNode)) {
node = new ParameterEntityReference(this, flatNode);
}
else if (!flatNode.getText().trim().equals("")) { //$NON-NLS-1$
node = new Unrecognized(this, flatNode);
}
if (node != null) {
insertNode(node);
node.resolveRegions();
}
return node;
}
public void buildNodes(IStructuredDocumentRegionList list) {
NodesEvent addedDTDNodes = new NodesEvent();
TopLevelNode previousNode = null;
for (int i = 0; i < list.getLength(); i++) {
IStructuredDocumentRegion flatNode = list.item(i);
TopLevelNode node = (TopLevelNode) buildNode(flatNode);
// if we don't create a node, then we assume that the flat
// node was whitespace. Tack it on to a previous toplevel
// node
if (node != null) {
previousNode = node;
addedDTDNodes.add(node);
}
else {
if (previousNode != null) {
previousNode.addWhitespaceStructuredDocumentRegion(flatNode);
}
}
}
if (addedDTDNodes.getNodes().size() > 0)// &&
// creatingNewModel == false)
{
// now tell people about the additions
notifyNodesAdded(addedDTDNodes);
}
}
// Implements IndexedRegion
public boolean contains(int testPosition) {
return getStartOffset() <= testPosition && testPosition <= getEndOffset();
}
public void createAttributeList(DTDNode node, String name, boolean isAfter) {
getDTDModel().beginRecording(this, DTDCoreMessages._UI_LABEL_DTD_FILE_ADD_ATTR_LIST); //$NON-NLS-1$
String newStream = "<!ATTLIST " + name + ">\n"; //$NON-NLS-1$ //$NON-NLS-2$
int offset = getInsertOffset(node, isAfter);
getStructuredDocument().replaceText(this, offset, 0, newStream);
getDTDModel().endRecording(this);
}
public void createComment(DTDNode node, String name, boolean isAfter) {
getDTDModel().beginRecording(this, DTDCoreMessages._UI_LABEL_DTD_FILE_ADD_COMMENT); //$NON-NLS-1$
String newStream = "<!-- " + name + " -->\n"; //$NON-NLS-1$ //$NON-NLS-2$
int offset = getInsertOffset(node, isAfter);
getStructuredDocument().replaceText(this, offset, 0, newStream);
getDTDModel().endRecording(this);
}
public void createElement(DTDNode node, String name, boolean isAfter) {
getDTDModel().beginRecording(this, DTDCoreMessages._UI_LABEL_DTD_FILE_ADD_ELEMENT); //$NON-NLS-1$
String newStream = "<!ELEMENT " + name + " EMPTY>\n"; //$NON-NLS-1$ //$NON-NLS-2$
int offset = getInsertOffset(node, isAfter);
getStructuredDocument().replaceText(this, offset, 0, newStream);
getDTDModel().endRecording(this);
}
public void createEntity(DTDNode node, String name, boolean isAfter) {
getDTDModel().beginRecording(this, DTDCoreMessages._UI_LABEL_DTD_FILE_ADD_ENTITY); //$NON-NLS-1$
String newStream = "<!ENTITY " + name + " \"\">\n"; //$NON-NLS-1$//$NON-NLS-2$
int offset = getInsertOffset(node, isAfter);
getStructuredDocument().replaceText(this, offset, 0, newStream);
getDTDModel().endRecording(this);
}
public void createNotation(DTDNode node, String name, boolean isAfter) {
getDTDModel().beginRecording(this, DTDCoreMessages._UI_LABEL_DTD_FILE_ADD_NOTATION); //$NON-NLS-1$
String newStream = "<!NOTATION " + name + " SYSTEM \"\">\n"; //$NON-NLS-1$ //$NON-NLS-2$
int offset = getInsertOffset(node, isAfter);
getStructuredDocument().replaceText(this, offset, 0, newStream);
getDTDModel().endRecording(this);
}
public void createParameterEntityReference(DTDNode node, String name, boolean isAfter) {
getDTDModel().beginRecording(this, DTDCoreMessages._UI_LABEL_DTD_FILE_ADD_PARM_ENTITY_REF); //$NON-NLS-1$
String newStream = name + "\n"; //$NON-NLS-1$
int offset = getInsertOffset(node, isAfter);
getStructuredDocument().replaceText(this, offset, 0, newStream);
getDTDModel().endRecording(this);
}
public void deleteNode(DTDNode node) {
getDTDModel().beginRecording(this, DTDCoreMessages._UI_LABEL_DTD_FILE_DELETE); //$NON-NLS-1$
deleteNode(this, node);
getDTDModel().endRecording(this);
}
public void deleteNode(Object requestor, DTDNode node) {
DTDNode parent = (DTDNode) node.getParentNode();
if (parent != null) {
parent.delete(requestor, node);
}
else {
if (!isMovingNode) {
DTDModelUpdater updater = new DTDModelUpdater();
updater.objectAboutToBeDeleted(requestor, node);
if (node instanceof ParameterEntityReference) {
Entity referencedEntity = ((ParameterEntityReference) node).getEntityObject();
if (referencedEntity != null) {
// remove references to all elements and parm entities
// contained in our current model
DTDExternalReferenceRemover remover = new DTDExternalReferenceRemover();
remover.externalReferenceAboutToChange(requestor, referencedEntity);
}
}
else if (node instanceof Notation) {
Notation notation = ((Notation) node);
DTDNotationReferenceRemover remover = new DTDNotationReferenceRemover();
remover.notationAboutToBeDeleted(requestor, notation);
}
}
// no parent? then delete up until the start of the next node
// if it is a top level node
int startOffset = node.getStartOffset();
int endOffset = node.getWhitespaceEndOffset();
if (node instanceof TopLevelNode) {
endOffset = getInsertOffset(node, true);
}
getStructuredDocument().replaceText(requestor, startOffset, endOffset - startOffset, ""); //$NON-NLS-1$
}
}
public NodeList getComments() {
return commentList;
}
public DTDModelImpl getDTDModel() {
return fDTDModel;
}
public NodeList getElementsAndParameterEntityReferences() {
return elementList;
}
public int getEndOffset() {
int result = -1;
IStructuredDocumentRegion region = getStructuredDocument().getLastStructuredDocumentRegion();
if (region != null) {
result = region.getEndOffset();
}
return result;
}
public int getLength() {
int result = -1;
int start = getStartOffset();
if (start >= 0) {
int end = getEndOffset();
if (end >= 0) {
result = end - start;
if (result < -1) {
result = -1;
}
}
}
return result;
}
public NodeList getEntities() {
return entityList;
}
public Image getImage() {
return DTDCorePlugin.getInstance().getImage(DTDResource.DTDFILEICON);
}
public int getInsertOffset(DTDNode node, boolean isAfter) {
int offset = 0;
if (node != null) {
if (isAfter) {
// then get the next node and use it's start offset
int index = getNodes().indexOf(getNode(node.getStructuredDTDDocumentRegion()));
DTDNode afterNode = null;
if (index + 1 < getNodes().size()) {
afterNode = (DTDNode) getNodes().get(index + 1);
}
if (afterNode != null) {
offset = afterNode.getStructuredDTDDocumentRegion().getStartOffset();
}
else {
// add to end
if (getStructuredDocument().getLastStructuredDocumentRegion() != null) {
offset = getStructuredDocument().getLastStructuredDocumentRegion().getEndOffset();
}
}
}
else {
offset = node.getStructuredDTDDocumentRegion().getStartOffset();
}
}
else {
// add to end
if (getStructuredDocument().getLastStructuredDocumentRegion() != null) {
offset = getStructuredDocument().getLastStructuredDocumentRegion().getEndOffset();
}
}
return offset;
}
public String getName() {
org.eclipse.core.runtime.Path path = new org.eclipse.core.runtime.Path(getDTDModel().getId().toString());
return path.lastSegment();
}
public DTDNode getNode(IStructuredDocumentRegion flatNode) {
for (int i = 0; i < nodeList.size(); i++) {
DTDNode node = (DTDNode) nodeList.get(i);
if (node.getStructuredDTDDocumentRegion() == flatNode) {
return node;
}
}
return null;
}
public IndexedRegion getNodeAt(int offset) {
DTDNode node = getTopLevelNodeAt(offset);
if (node != null) {
return node.getDeepestNode(offset);
}
return null;
}
public IndexedRegion getNodeAt(int startOffset, int endOffset) {
DTDNode node = getTopLevelNodeAt(startOffset);
if (node != null) {
return node.getDeepestNode(startOffset, endOffset);
}
return null;
}
public List getNodeLists() {
if (folderList == null) {
folderList = new ArrayList();
folderList.add(notationList);
folderList.add(entityList);
folderList.add(elementList);
folderList.add(attlistList);
folderList.add(commentList);
folderList.add(unrecognizedList);
}
return folderList;
}
public List getNodes() {
return nodeList;
}
public NodeList getNotations() {
return notationList;
}
public int getStartOffset() {
int result = -1;
IStructuredDocumentRegion region = getStructuredDocument().getFirstStructuredDocumentRegion();
if (region != null) {
result = region.getStartOffset();
}
return result;
}
public IStructuredDocument getStructuredDocument() {
return fDTDModel.getStructuredDocument();
}
public DTDNode getTopLevelNodeAt(int offset) {
DTDNode bestNode = null;
for (int i = 0; i < nodeList.size(); i++) {
DTDNode node = (DTDNode) nodeList.get(i);
if (node.contains(offset)) {
if(bestNode == null) {
bestNode = node;
} else {
if(node.getStartOffset() > bestNode.getStartOffset()) {
bestNode = node;
}
}
}
}
return bestNode;
}
public NodeList getUnrecognized() {
return unrecognizedList;
}
public void insertIntoModel(Object requestor, DTDNode reference, DTDNode node, boolean isAfter) {
String nodeText = ""; //$NON-NLS-1$
if (node instanceof TopLevelNode) {
nodeText = ((TopLevelNode) node).getFullText();
}
else {
nodeText = node.getNodeText();
}
int offset = getInsertOffset(reference, isAfter);
getStructuredDocument().replaceText(requestor, offset, 0, nodeText);
}
public void insertNode(DTDNode node) {
int startOffset = node.getStartOffset();
int insertIndex = -1;
// System.out.println("startoffset = " + startOffset);
for (int i = 0; i < getNodes().size(); i++) {
DTDNode currentNode = (DTDNode) getNodes().get(i);
// System.out.println("currentNode endOffset = "
// +currentNode.getEndOffset());
if (currentNode.getEndOffset() > startOffset) {
// System.out.println("endoffset " +
// currentNode.getEndOffset() + " > " + startOffset);
insertIndex = i;
break;
}
}
if (insertIndex == -1) {
insertIndex = getNodes().size();
}
// System.out.println("insert index = " + insertIndex);
addNode(insertIndex, node);
}
// it is assumed that flatnode contains at least 3 regions
public boolean isAttributeList(IStructuredDocumentRegion flatNode) {
if (flatNode.getRegions().size() >= 3) {
ITextRegion second = flatNode.getRegions().get(1);
ITextRegion third = flatNode.getRegions().get(2);
if (second.getType().equals(DTDRegionTypes.EXCLAMATION) && third.getType().equals(DTDRegionTypes.ATTLIST_TAG)) {
return true;
}
}
return false;
}
// it is assumed that flatnode contains at least 3 regions
public boolean isComment(IStructuredDocumentRegion flatNode) {
if (flatNode.getRegions().size() >= 2) {
ITextRegion region = flatNode.getRegions().get(1);
if (region.getType().equals(DTDRegionTypes.COMMENT_START)) {
return true;
}
}
return false;
}
// it is assumed that flatnode contains at least 3 regions
public boolean isElement(IStructuredDocumentRegion flatNode) {
if (flatNode.getRegions().size() >= 3) {
ITextRegion second = flatNode.getRegions().get(1);
ITextRegion third = flatNode.getRegions().get(2);
if (second.getType().equals(DTDRegionTypes.EXCLAMATION) && third.getType().equals(DTDRegionTypes.ELEMENT_TAG)) {
return true;
}
}
return false;
}
// it is assumed that flatnode contains at least 3 regions
public boolean isEntity(IStructuredDocumentRegion flatNode) {
if (flatNode.getRegions().size() >= 3) {
ITextRegion second = flatNode.getRegions().get(1);
ITextRegion third = flatNode.getRegions().get(2);
if (second.getType().equals(DTDRegionTypes.EXCLAMATION) && third.getType().equals(DTDRegionTypes.ENTITY_TAG)) {
return true;
}
}
return false;
}
// it is assumed that flatnode contains at least 3 regions
public boolean isNotation(IStructuredDocumentRegion flatNode) {
if (flatNode.getRegions().size() >= 3) {
ITextRegion second = flatNode.getRegions().get(1);
ITextRegion third = flatNode.getRegions().get(2);
if (second.getType().equals(DTDRegionTypes.EXCLAMATION) && third.getType().equals(DTDRegionTypes.NOTATION_TAG)) {
return true;
}
}
return false;
}
// it is assumed that flatnode contains at least 3 regions
public boolean isParameterEntityReference(IStructuredDocumentRegion flatNode) {
if (flatNode.getRegions().size() == 1) {
ITextRegion region = flatNode.getRegions().get(0);
if (region.getType().equals(DTDRegionTypes.ENTITY_PARM)) {
return true;
}
}
return false;
}
boolean isSameTopLevelType(DTDNode affectedNode) {
IStructuredDocumentRegion flatNode = affectedNode.getStructuredDTDDocumentRegion();
// return true if the flatnode still matches what the affectedNode
// is representing
if (affectedNode instanceof Element && isElement(flatNode)) {
return true;
}
if (affectedNode instanceof Entity && isEntity(flatNode)) {
return true;
}
if (affectedNode instanceof Comment && isComment(flatNode)) {
return true;
}
if (affectedNode instanceof AttributeList && isAttributeList(flatNode)) {
return true;
}
if (affectedNode instanceof Notation && isNotation(flatNode)) {
return true;
}
if (affectedNode instanceof Unrecognized && isUnrecognized(flatNode)) {
return true;
}
return false;
}
public boolean isUnrecognized(IStructuredDocumentRegion flatNode) {
return !isElement(flatNode) && !isEntity(flatNode) && !isNotation(flatNode) && !isParameterEntityReference(flatNode) && !isAttributeList(flatNode) && !isComment(flatNode);
}
public void moveNode(Object requestor, DTDNode referenceNode, DTDNode nodeToMove, boolean isAfter) {
isMovingNode = true;
deleteNode(requestor, nodeToMove);
insertIntoModel(requestor, referenceNode, nodeToMove, isAfter);
isMovingNode = false;
}
public void newModel(NewDocumentEvent event) {
creatingNewModel = true;
nodeList.clear();
NodesEvent removeEvent = new NodesEvent();
removeEvent.getNodes().addAll(nodeList);
notifyNodesRemoved(removeEvent);
/* removeChildNodes(); */
if (event.getStructuredDocument() != null && event.getStructuredDocument().getRegionList() != null) {
buildNodes(event.getStructuredDocument().getRegionList());
}
creatingNewModel = false;
}
public void nodesReplaced(StructuredDocumentRegionsReplacedEvent event) {
IStructuredDocumentRegionList oldNodes = event.getOldStructuredDocumentRegions();
NodesEvent removedDTDNodes = new NodesEvent();
for (int i = 0; i < oldNodes.getLength(); i++) {
IStructuredDocumentRegion flatNode = oldNodes.item(i);
for (Iterator iter = getNodes().iterator(); iter.hasNext();) {
DTDNode node = (DTDNode) iter.next();
if (node.getStructuredDTDDocumentRegion() == flatNode) {
removedDTDNodes.add(node);
}
}
}
buildNodes(event.getNewStructuredDocumentRegions());
if (removedDTDNodes.getNodes().size() > 0) {
notifyNodesRemoved(removedDTDNodes);
removeNodes(removedDTDNodes.getNodes());
}
}
public void notifyNodeChanged(DTDNode node) {
Iterator iter = modelListeners.iterator();
while (iter.hasNext()) {
IDTDFileListener listener = (IDTDFileListener) iter.next();
listener.nodeChanged(node);
}
}
public void notifyNodesAdded(NodesEvent addedNodes) {
Iterator iter = modelListeners.iterator();
while (iter.hasNext()) {
IDTDFileListener listener = (IDTDFileListener) iter.next();
listener.nodesAdded(addedNodes);
}
}
protected void notifyNodesRemoved(NodesEvent event) {
Iterator iter = modelListeners.iterator();
while (iter.hasNext()) {
IDTDFileListener listener = (IDTDFileListener) iter.next();
listener.nodesRemoved(event);
}
}
public void rebuildNodes(List nodes) {
// remove the old nodes
removeNodes(nodes);
// now rebuild them
NodesEvent addedDTDNodes = new NodesEvent();
Iterator dtdNodes = nodes.iterator();
while (dtdNodes.hasNext()) {
DTDNode dtdNode = (DTDNode) dtdNodes.next();
// System.out.println("rebuilding " +
// dtdNode.getStructuredDocumentRegion().getText());
DTDNode node = buildNode(dtdNode.getStructuredDTDDocumentRegion());
if (node != null) {
addedDTDNodes.add(node);
}
}
if (addedDTDNodes.getNodes().size() > 0) {
// now tell people about the additions
notifyNodesAdded(addedDTDNodes);
}
}
public void regionChanged(RegionChangedEvent event) {
ITextRegion changedRegion = event.getRegion();
IStructuredDocumentRegion flatNode = event.getStructuredDocumentRegion();
DTDNode affectedNode = (DTDNode) getNodeAt(flatNode.getStartOffset(changedRegion), flatNode.getEndOffset(changedRegion));
if (affectedNode != null) {
// no need to resolve regions as it is just a change
// affectedNode.resolveRegions();
notifyNodeChanged(affectedNode);
}
}
public void regionsReplaced(RegionsReplacedEvent event) {
List nodesToRebuild = new ArrayList();
IStructuredDocumentRegion flatNode = event.getStructuredDocumentRegion();
DTDNode affectedNode = getNode(flatNode);
if (!isSameTopLevelType(affectedNode)) {
// Bug 111100 - Fire off a node removal event
// to remove the node from the tree viewer
NodesEvent removedDTDNodes = new NodesEvent();
removedDTDNodes.add(affectedNode);
notifyNodesRemoved(removedDTDNodes);
nodesToRebuild.add(affectedNode);
rebuildNodes(nodesToRebuild);
}
else {
affectedNode.resolveRegions();
notifyNodeChanged(affectedNode);
// now try and determine which ones were added
NodesEvent addedDTDNodes = new NodesEvent();
ITextRegionList newRegions = event.getNewRegions();
int size = newRegions.size();
for (int i = 0; i < size; i++) {
ITextRegion region = newRegions.get(i);
DTDNode deepestNode = affectedNode.getDeepestNode(flatNode.getStartOffset(region), flatNode.getEndOffset(region));
// Bug 111100 - We do not need to fire a node added event
// for the affectedNode because that is already handled by
// the node changed event above
if (deepestNode != affectedNode && !addedDTDNodes.getNodes().contains(deepestNode)) {
addedDTDNodes.add(deepestNode);
}
}
if (addedDTDNodes.getNodes().size() > 0) {
notifyNodesAdded(addedDTDNodes);
}
}
}
public void removeDTDFileListener(IDTDFileListener listener) {
modelListeners.remove(listener);
}
protected void removeNodes(List nodes) {
getNodes().removeAll(nodes);
/*
* for (int i = 0; i < nodes.size(); i++) {
* removeChild((DTDNode)nodes.get(i)); } // end of for ()
*/
}
}