diff options
Diffstat (limited to 'bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal')
79 files changed, 25229 insertions, 0 deletions
diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/DebugAdapterFactory.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/DebugAdapterFactory.java new file mode 100644 index 0000000000..4275c5047d --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/DebugAdapterFactory.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal; + +import java.util.ArrayList; + +import org.eclipse.wst.sse.core.AbstractAdapterFactory; +import org.eclipse.wst.sse.core.AdapterFactory; +import org.eclipse.wst.sse.core.INodeAdapter; +import org.eclipse.wst.sse.core.INodeNotifier; +import org.eclipse.wst.sse.core.PropagatingAdapterFactory; + + +public class DebugAdapterFactory extends AbstractAdapterFactory implements PropagatingAdapterFactory { + + /** + * Constructor for PageDirectiveWatcherFactory. + */ + public DebugAdapterFactory() { + this(IDebugAdapter.class, true); + } + + /** + * Constructor for PageDirectiveWatcherFactory. + * + * @param adapterKey + * @param registerAdapters + */ + public DebugAdapterFactory(Object adapterKey, boolean registerAdapters) { + super(adapterKey, registerAdapters); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.PropagatingAdapterFactory#addContributedFactories(org.eclipse.wst.sse.core.AdapterFactory) + */ + public void addContributedFactories(AdapterFactory factory) { + //none expected + } + + public AdapterFactory copy() { + return new DebugAdapterFactory(this.adapterKey, this.shouldRegisterAdapter); + } + + protected INodeAdapter createAdapter(INodeNotifier target) { + EveryNodeDebugAdapter result = null; + result = EveryNodeDebugAdapter.getInstance(); + return result; + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.AdapterFactory#isFactoryForType(java.lang.Object) + */ + public boolean isFactoryForType(Object type) { + + return IDebugAdapter.class == type; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.PropagatingAdapterFactory#setContributedFactories(java.util.ArrayList) + */ + public void setContributedFactories(ArrayList list) { + // none expected + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/EveryNodeDebugAdapter.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/EveryNodeDebugAdapter.java new file mode 100644 index 0000000000..dc238974fc --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/EveryNodeDebugAdapter.java @@ -0,0 +1,274 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal; + +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.wst.sse.core.IModelStateListener; +import org.eclipse.wst.sse.core.INodeNotifier; +import org.eclipse.wst.sse.core.IStructuredModel; +import org.eclipse.wst.sse.core.events.AboutToBeChangeEvent; +import org.eclipse.wst.sse.core.events.IModelAboutToBeChangedListener; +import org.eclipse.wst.sse.core.events.IStructuredDocumentListener; +import org.eclipse.wst.sse.core.events.NewDocumentEvent; +import org.eclipse.wst.sse.core.events.NoChangeEvent; +import org.eclipse.wst.sse.core.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.events.RegionsReplacedEvent; +import org.eclipse.wst.sse.core.events.StructuredDocumentRegionsReplacedEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocument; +import org.eclipse.wst.sse.core.util.Debug; +import org.eclipse.wst.xml.core.document.XMLNode; + + +/** + * Purely for use in debugging + */ +public class EveryNodeDebugAdapter implements IDebugAdapter { + + static class InternalDocumentListener implements IDocumentListener { + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent) + */ + public void documentAboutToBeChanged(DocumentEvent event) { + Debug.println("IdocumentAboutToBeChanged: " + event); //$NON-NLS-1$ + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent) + */ + public void documentChanged(DocumentEvent event) { + Debug.println("IdocumentChanged: " + event); //$NON-NLS-1$ + + } + + } + + static class InternalModelStateListener implements IModelStateListener { + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.IModelStateListener#modelAboutToBeChanged(org.eclipse.wst.sse.core.IStructuredModel) + */ + public void modelAboutToBeChanged(IStructuredModel model) { + Debug.println("modelAboutToBeChanged: " + model); //$NON-NLS-1$ + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.IModelStateListener#modelChanged(org.eclipse.wst.sse.core.IStructuredModel) + */ + public void modelChanged(IStructuredModel model) { + Debug.println("modelChanged: " + model); //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.IModelStateListener#modelDirtyStateChanged(org.eclipse.wst.sse.core.IStructuredModel, + * boolean) + */ + public void modelDirtyStateChanged(IStructuredModel model, boolean isDirty) { + Debug.println("modelDirtyStateChanged: " + model); //$NON-NLS-1$ + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.IModelStateListener#modelResourceDeleted(org.eclipse.wst.sse.core.IStructuredModel) + */ + public void modelResourceDeleted(IStructuredModel model) { + Debug.println("modelResourceDeleted: " + model); //$NON-NLS-1$ + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.IModelStateListener#modelResourceMoved(org.eclipse.wst.sse.core.IStructuredModel, + * org.eclipse.wst.sse.core.IStructuredModel) + */ + public void modelResourceMoved(IStructuredModel oldModel, IStructuredModel newModel) { + Debug.println("modelResourceMoved: " + "oldModel: " + oldModel + "newModel: " + newModel); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + } + + static class InternalStructuredDocumentAboutToChange implements IModelAboutToBeChangedListener { + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IModelAboutToBeChangedListener#modelAboutToBeChanged(org.eclipse.wst.sse.core.events.AboutToBeChangeEvent) + */ + public void modelAboutToBeChanged(AboutToBeChangeEvent structuredDocumentEvent) { + Debug.println("structuredDocumentAboutToBeChanged: " + structuredDocumentEvent); //$NON-NLS-1$ + + } + + } + + static class InternalStructuredDocumentListener implements IStructuredDocumentListener { + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IStructuredDocumentListener#newModel(org.eclipse.wst.sse.core.events.NewDocumentContentEvent) + */ + public void newModel(NewDocumentEvent structuredDocumentEvent) { + Debug.println("structuredDocumentChanged - newModel: " + structuredDocumentEvent); //$NON-NLS-1$ + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IStructuredDocumentListener#noChange(org.eclipse.wst.sse.core.events.NoChangeEvent) + */ + public void noChange(NoChangeEvent structuredDocumentEvent) { + Debug.println("structuredDocumentChanged - noChange: " + structuredDocumentEvent); //$NON-NLS-1$ + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IStructuredDocumentListener#nodesReplaced(org.eclipse.wst.sse.core.events.StructuredDocumentRegionsReplacedEvent) + */ + public void nodesReplaced(StructuredDocumentRegionsReplacedEvent structuredDocumentEvent) { + Debug.println("structuredDocumentChanged - nodesReplaced: " + structuredDocumentEvent); //$NON-NLS-1$ + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IStructuredDocumentListener#regionChanged(org.eclipse.wst.sse.core.events.RegionChangedEvent) + */ + public void regionChanged(RegionChangedEvent structuredDocumentEvent) { + Debug.println("structuredDocumentChanged - regionChanged: " + structuredDocumentEvent); //$NON-NLS-1$ + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.events.IStructuredDocumentListener#regionsReplaced(org.eclipse.wst.sse.core.events.RegionsReplacedEvent) + */ + public void regionsReplaced(RegionsReplacedEvent structuredDocumentEvent) { + Debug.println("structuredDocumentChanged - regionsReplaced: " + structuredDocumentEvent); //$NON-NLS-1$ + + } + + } + + private static EveryNodeDebugAdapter singletonInstance; + + public static EveryNodeDebugAdapter getInstance() { + if (singletonInstance == null) { + singletonInstance = new EveryNodeDebugAdapter(); + } + return singletonInstance; + } + + InternalDocumentListener fInternalDocumentListener; + InternalModelStateListener fInternalModelStateListener; + InternalStructuredDocumentAboutToChange fInternalStructuredDocumentAboutToChange; + InternalStructuredDocumentListener fInternalStructuredDocumentListener; + IStructuredModel fModel; + + /** + * + */ + public EveryNodeDebugAdapter() { + super(); + fInternalDocumentListener = new InternalDocumentListener(); + fInternalStructuredDocumentAboutToChange = new InternalStructuredDocumentAboutToChange(); + fInternalStructuredDocumentListener = new InternalStructuredDocumentListener(); + fInternalModelStateListener = new InternalModelStateListener(); + } + + /** + * @param target + */ + public EveryNodeDebugAdapter(INodeNotifier target) { + this(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.INodeAdapter#isAdapterForType(java.lang.Object) + */ + public boolean isAdapterForType(Object type) { + return (type == IDebugAdapter.class); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.INodeAdapter#notifyChanged(org.eclipse.wst.sse.core.INodeNotifier, + * int, java.lang.Object, java.lang.Object, java.lang.Object, int) + */ + public void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) { + if (notifier instanceof XMLNode) { + setModel(((XMLNode) notifier).getModel()); + } + Debug.println("notifier: " + notifier + " " + INodeNotifier.EVENT_TYPE_STRINGS[eventType] + " changedFeature: " + changedFeature + " oldValue: " + oldValue + " newValue: " + newValue + " pos: " + pos); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.IDebugAdapter#setDocument(org.eclipse.wst.sse.core.text.IStructuredDocument) + */ + private void setModel(IStructuredModel structuredModel) { + if (fModel == structuredModel) + return; + + if (fModel != null) { + fModel.removeModelStateListener(fInternalModelStateListener); + // + IStructuredDocument structuredDocument = fModel.getStructuredDocument(); + if (structuredDocument != null) { + structuredDocument.removeDocumentListener(fInternalDocumentListener); + structuredDocument.removeDocumentAboutToChangeListener(fInternalStructuredDocumentAboutToChange); + structuredDocument.removeDocumentChangedListener(fInternalStructuredDocumentListener); + } + } + fModel = structuredModel; + if (fModel != null) { + + fModel.addModelStateListener(fInternalModelStateListener); + // + IStructuredDocument structuredDocument = fModel.getStructuredDocument(); + if (structuredDocument != null) { + structuredDocument.addDocumentListener(fInternalDocumentListener); + structuredDocument.addDocumentAboutToChangeListener(fInternalStructuredDocumentAboutToChange); + structuredDocument.addDocumentChangedListener(fInternalStructuredDocumentListener); + } + } + + + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/IDebugAdapter.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/IDebugAdapter.java new file mode 100644 index 0000000000..50b902514e --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/IDebugAdapter.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal; + +import org.eclipse.wst.sse.core.INodeAdapter; + + +public interface IDebugAdapter extends INodeAdapter { +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/ByteReader.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/ByteReader.java new file mode 100644 index 0000000000..e5f04fa496 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/ByteReader.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.contenttype; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +/** + * This is an "adapter" class, simply to get in input stream to act like a + * reader. We could not use InputStreamReader directly because its internal + * buffers are not controllable, and it sometimes pulls too much out of input + * stream (even when it wasn't needed for our purposes). + * + * The use of this class is highly specialized and by not means meant to be + * general purpose. Its use is restricted to those cases where the input + * stream can be regarded as ascii just long enough to determine what the real + * encoding should be. + */ + +public class ByteReader extends Reader { + + /** Default byte buffer size (2048). */ + public static final int DEFAULT_BUFFER_SIZE = 2048; + + protected byte[] fBuffer; + + protected InputStream fInputStream; + + protected ByteReader() { + super(); + } + + public ByteReader(InputStream inputStream) { + this(inputStream, DEFAULT_BUFFER_SIZE); + if (!inputStream.markSupported()) { + throw new IllegalArgumentException("ByteReader is required to have a resettable stream"); //$NON-NLS-1$ + } + } + + public ByteReader(InputStream inputStream, int size) { + fInputStream = inputStream; + if (!inputStream.markSupported()) { + throw new IllegalArgumentException("ByteReader is required to have a resettable stream"); //$NON-NLS-1$ + } + fBuffer = new byte[size]; + + } + + public void close() throws IOException { + fInputStream.close(); + } + + public void mark(int readAheadLimit) throws IOException { + fInputStream.mark(readAheadLimit); + } + + public boolean markSupported() { + return true; + } + + public int read() throws IOException { + int b0 = fInputStream.read(); + return (b0 & 0x00FF); + } + + public int read(char ch[], int offset, int length) throws IOException { + if (length > fBuffer.length) { + length = fBuffer.length; + } + + int count = fInputStream.read(fBuffer, 0, length); + + for (int i = 0; i < count; i++) { + int b0 = fBuffer[i]; + // the 0x00FF is to "lose" the negative bits filled in the byte to + // int conversion + // (and which would be there if cast directly from byte to char). + char c0 = (char) (b0 & 0x00FF); + ch[i] = c0; + } + return count; + } + + public boolean ready() throws IOException { + return fInputStream.available() > 0; + } + + public void reset() throws IOException { + fInputStream.reset(); + } + + public long skip(long n) throws IOException { + return fInputStream.skip(n); + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/EncodingParserConstants.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/EncodingParserConstants.java new file mode 100644 index 0000000000..7e12f93503 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/EncodingParserConstants.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.contenttype; + + +public interface EncodingParserConstants { + + final String EOF = "EOF"; //$NON-NLS-1$ + final String InvalidTerminatedStringValue = "InvalidTerminatedStringValue"; //$NON-NLS-1$ + final String InvalidTermintatedUnDelimitedStringValue = "InvalidTermintatedUnDelimitedStringValue"; //$NON-NLS-1$ + final String MAX_CHARS_REACHED = "MAX_CHARS_REACHED"; //$NON-NLS-1$ + final String StringValue = "strval"; //$NON-NLS-1$ + final String UnDelimitedStringValue = "UnDelimitedStringValue"; //$NON-NLS-1$ + String UTF16BE = "UTF16BE"; //$NON-NLS-1$ + String UTF16LE = "UTF16LE"; //$NON-NLS-1$ + + + String UTF83ByteBOM = "UTF83ByteBOM"; //$NON-NLS-1$ + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/HeadParserToken.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/HeadParserToken.java new file mode 100644 index 0000000000..0f14b8b570 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/HeadParserToken.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.contenttype; + +public class HeadParserToken { + private int fStart; + + private String fText; + private String fType; + + protected HeadParserToken() { + super(); + } + + public HeadParserToken(String type, int start, String text) { + this(); + fType = type; + fStart = start; + fText = text; + + } + + public String getText() { + return fText; + } + + public String getType() { + return fType; + } + + public String toString() { + return ("text: " + fText + " offset: " + fStart + " type: " + fType); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/IntStack.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/IntStack.java new file mode 100644 index 0000000000..1d1052d850 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/IntStack.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.contenttype; + +/* + * + * A non-resizable class implementing the behavior of java.util.Stack, but + * directly for the <code> integer </code> primitive. + */ +import java.util.EmptyStackException; + +public class IntStack { + private int[] list = null; + + private int size = 0; + + public IntStack() { + this(100); + } + + public IntStack(int maxdepth) { + super(); + list = new int[maxdepth]; + initialize(); + } + + public void clear() { + initialize(); + } + + public boolean empty() { + return size == 0; + } + + public int get(int slot) { + return list[slot]; + } + + private void initialize() { + for (int i = 0; i < list.length; i++) + list[i] = -1; + } + + /** + * Returns the int at the top of the stack without removing it + * + * @return int at the top of this stack. + * @exception EmptyStackException + * when empty. + */ + public int peek() { + if (size == 0) + throw new EmptyStackException(); + return list[size - 1]; + } + + /** + * Removes and returns the int at the top of the stack + * + * @return int at the top of this stack. + * @exception EmptyStackException + * when empty. + */ + public int pop() { + int value = peek(); + list[size - 1] = -1; + size--; + return value; + } + + /** + * Pushes an item onto the top of this stack. + * + * @param newValue - + * the int to be pushed onto this stack. + * @return the <code>newValue</code> argument. + */ + public int push(int newValue) { + if (size == list.length) { + throw new StackOverflowError(); + } + list[size++] = newValue; + return newValue; + } + + public int size() { + return list.length; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/XMLDeclDetector.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/XMLDeclDetector.java new file mode 100644 index 0000000000..5843f61c4f --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/XMLDeclDetector.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.contenttype; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; + +public class XMLDeclDetector { + private static final int MAX_BUF_SIZE = 1024 * 2; + private static final int MAX_MARK_SIZE = 1024 * 2; + protected boolean fHeaderParsed; + private boolean fIsXML; + protected Reader fReader; + //private boolean DEBUG = false; + private XMLHeadTokenizer fTokenizer; + + private boolean canHandleAsUnicodeStream(String tokenType) { + boolean canHandleAsUnicodeStream = false; + if (tokenType == EncodingParserConstants.UTF83ByteBOM) { + canHandleAsUnicodeStream = true; + //fUnicode = "UTF-8"; //$NON-NLS-1$ + } else if (tokenType == EncodingParserConstants.UTF16BE) { + canHandleAsUnicodeStream = true; + //fUnicode = "UTF-16BE"; //$NON-NLS-1$ + } else if (tokenType == EncodingParserConstants.UTF16LE) { + canHandleAsUnicodeStream = true; + //fUnicode = "UTF-16"; //$NON-NLS-1$ + } + return canHandleAsUnicodeStream; + } + + final private void ensureInputSet() { + if (fReader == null) { + throw new IllegalStateException("input must be set before use"); //$NON-NLS-1$ + } + } + + //private String fUnicode; + + /** + * @return Returns the tokenizer. + */ + private XMLHeadTokenizer getTokenizer() { + if (fTokenizer == null) { + fTokenizer = new XMLHeadTokenizer(); + } + return fTokenizer; + } + + /** + * @return Returns the isXML. + */ + public boolean isXML() throws IOException { + ensureInputSet(); + if (!fHeaderParsed) { + parseInput(); + } + return fIsXML; + } + + private void parseInput() throws IOException { + XMLHeadTokenizer tokenizer = getTokenizer(); + tokenizer.reset(fReader); + HeadParserToken token = null; + String tokenType = null; + do { + token = tokenizer.getNextToken(); + tokenType = token.getType(); + if (canHandleAsUnicodeStream(tokenType)) { + fReader.reset(); + // this is (obviously) not always true. + // TODO: need to fix so we "remember" original iFile or + // inputstream, and + // create appropriate InputStreamReader. + // I'm not sure what to do for the set(reader) case ... if its + // even relevent. + // plus, ensure against infinite loops! + fIsXML = true; + //fReader = new InputStreamReader(fReader, fUnicode); + // parseInput(); + } else { + if (tokenType == XMLHeadTokenizerConstants.XMLDelEncoding) { + fIsXML = true; + } + } + } while (tokenizer.hasMoreTokens()); + + } + + private void resetAll() { + fReader = null; + fHeaderParsed = false; + fIsXML = false; + //fUnicode = null; + + } + + public void set(IFile iFile) throws CoreException { + resetAll(); + InputStream inputStream = iFile.getContents(true); + InputStream resettableStream = new BufferedInputStream(inputStream, MAX_BUF_SIZE); + resettableStream.mark(MAX_MARK_SIZE); + set(resettableStream); + } + + public void set(InputStream inputStream) { + resetAll(); + fReader = new ByteReader(inputStream); + try { + fReader.mark(MAX_MARK_SIZE); + } catch (IOException e) { + // impossible, since we know ByteReader supports marking + throw new Error(e); + } + } + + /** + * Note: this is not part of interface to help avoid confusion ... it + * expected this Reader is a well formed character reader ... that is, its + * all ready been determined to not be a unicode marked input stream. And, + * its assumed to be in the correct position, at position zero, ready to + * read first character. + */ + public void set(Reader reader) { + resetAll(); + fReader = reader; + if (!fReader.markSupported()) { + fReader = new BufferedReader(fReader); + } + + try { + fReader.mark(MAX_MARK_SIZE); + } catch (IOException e) { + // impossble, since we just checked if markable + throw new Error(e); + } + + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/XMLHeadTokenizer.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/XMLHeadTokenizer.java new file mode 100644 index 0000000000..b4a9b6ebba --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/XMLHeadTokenizer.java @@ -0,0 +1,1222 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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 + * + *******************************************************************************/ +/* The following code was generated by JFlex 1.2.2 on 4/6/04 11:13 PM */ + +/*nlsXXX*/ +package org.eclipse.wst.xml.core.internal.contenttype; + +import java.io.IOException; +import java.io.Reader; + + + +/** + * This class is a scanner generated by <a + * href="http://www.informatik.tu-muenchen.de/~kleing/jflex/">JFlex </a> 1.2.2 + * on 4/6/04 11:13 PM from the specification file + * <tt>file:/D:/DevTimeSupport/HeadParsers/XMLHeadTokenizer/XMLHeadTokenizer.jflex</tt> + */ +public class XMLHeadTokenizer { + + /** this character denotes the end of file */ + final public static int YYEOF = -1; + + /** lexical states */ + final public static int YYINITIAL = 0; + final public static int UnDelimitedString = 10; + final public static int DQ_STRING = 6; + final public static int SQ_STRING = 8; + final public static int ST_XMLDecl = 2; + final public static int QuotedAttributeValue = 4; + + /** + * YY_LEXSTATE[l] is the state in the DFA for the lexical state l + * YY_LEXSTATE[l+1] is the state in the DFA for the lexical state l at the + * beginning of a line l is of the form l = 2*k, k a non negative integer + */ + private final static int YY_LEXSTATE[] = {0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6}; + + /** + * Translates characters to character classes + */ + final private static String yycmap_packed = "\11\0\1\6\1\7\2\0\1\11\22\0\1\6\1\0\1\27\2\0" + "\1\31\1\0\1\30\24\0\1\12\1\10\1\26\1\13\3\0\1\21" + "\1\23\1\17\1\0\1\25\1\0\1\24\2\0\1\16\1\15\1\20" + "\1\22\10\0\1\14\12\0\1\21\1\23\1\17\1\0\1\25\1\0" + "\1\24\2\0\1\16\1\15\1\20\1\22\10\0\1\14\102\0\1\4" + "\3\0\1\5\17\0\1\3\16\0\1\1\20\0\1\3\16\0\1\1" + "\1\2\170\0\1\2\ufe87\0"; + + /** + * Translates characters to character classes + */ + final private static char[] yycmap = yy_unpack_cmap(yycmap_packed); + + + /* error codes */ + final private static int YY_UNKNOWN_ERROR = 0; + final private static int YY_ILLEGAL_STATE = 1; + final private static int YY_NO_MATCH = 2; + final private static int YY_PUSHBACK_2BIG = 3; + + /* error messages for the codes above */ + final private static String YY_ERROR_MSG[] = {"Unkown internal scanner error", "Internal error: unknown state", "Error: could not match input", "Error: pushback value was too large"}; + + /** the input device */ + private java.io.Reader yy_reader; + + /** the current state of the DFA */ + private int yy_state; + + /** the current lexical state */ + private int yy_lexical_state = YYINITIAL; + + /** + * this buffer contains the current text to be matched and is the source + * of the yytext() string + */ + private char yy_buffer[] = new char[16384]; + + /** the textposition at the last accepting state */ + private int yy_markedPos; + + /** the textposition at the last state to be included in yytext */ + private int yy_pushbackPos; + + /** the current text position in the buffer */ + private int yy_currentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int yy_startRead; + + /** + * endRead marks the last character in the buffer, that has been read from + * input + */ + private int yy_endRead; + + /** number of newlines encountered up to the start of the matched text */ + int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + int yycolumn; + + /** + * yy_atBOL == true <=>the scanner is currently at the beginning of a line + */ + private boolean yy_atBOL; + + /** yy_atEOF == true <=>the scanner has returned a value for EOF */ + private boolean yy_atEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean yy_eof_done; + + /* user code: */ + + + private boolean hasMore = true; + private final static int MAX_TO_SCAN = 1000; + StringBuffer string = new StringBuffer(); + // state stack for easier state handling + private IntStack fStateStack = new IntStack(); + private String valueText = null; + + + public XMLHeadTokenizer() { + super(); + } + + public void reset(Reader in) { + /* the input device */ + yy_reader = in; + + /* the current state of the DFA */ + yy_state = 0; + + /* the current lexical state */ + yy_lexical_state = YYINITIAL; + + /* + * this buffer contains the current text to be matched and is the + * source of the yytext() string + */ + java.util.Arrays.fill(yy_buffer, (char) 0); + + /* the textposition at the last accepting state */ + yy_markedPos = 0; + + /* the textposition at the last state to be included in yytext */ + yy_pushbackPos = 0; + + /* the current text position in the buffer */ + yy_currentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + yy_startRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + yy_endRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /** + * the number of characters from the last newline up to the start of + * the matched text + */ + yycolumn = 0; + + /** + * yy_atBOL == true <=>the scanner is currently at the beginning of a + * line + */ + yy_atBOL = false; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + yy_atEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + yy_eof_done = false; + + + fStateStack.clear(); + + hasMore = true; + + // its a little wasteful to "throw away" first char array generated + // by class init (via auto generated code), but we really do want + // a small buffer for our head parsers. + if (yy_buffer.length != MAX_TO_SCAN) { + yy_buffer = new char[MAX_TO_SCAN]; + } + + + } + + + public final HeadParserToken getNextToken() throws IOException { + String context = null; + context = primGetNextToken(); + HeadParserToken result = null; + if (valueText != null) { + result = createToken(context, yychar, valueText); + valueText = null; + } else { + result = createToken(context, yychar, yytext()); + } + return result; + } + + public final boolean hasMoreTokens() { + return hasMore && yychar < MAX_TO_SCAN; + } + + private void pushCurrentState() { + fStateStack.push(yystate()); + + } + + private void popState() { + yybegin(fStateStack.pop()); + } + + private HeadParserToken createToken(String context, int start, String text) { + return new HeadParserToken(context, start, text); + } + + + + /** + * Creates a new scanner There is also a java.io.InputStream version of + * this constructor. + * + * @param in + * the java.io.Reader to read input from. + */ + public XMLHeadTokenizer(java.io.Reader in) { + this.yy_reader = in; + } + + /** + * Creates a new scanner. There is also java.io.Reader version of this + * constructor. + * + * @param in + * the java.io.Inputstream to read input from. + */ + public XMLHeadTokenizer(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed + * the packed character translation table + * @return the unpacked character translation table + */ + private static char[] yy_unpack_cmap(String packed) { + char[] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 128) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do + map[j++] = value; + while (--count > 0); + } + return map; + } + + + /** + * Gets the next input character. + * + * @return the next character of the input stream, EOF if the end of the + * stream is reached. + * @exception IOException + * if any I/O-Error occurs + */ + private int yy_advance() throws java.io.IOException { + + /* standard case */ + if (yy_currentPos < yy_endRead) + return yy_buffer[yy_currentPos++]; + + /* if the eof is reached, we don't need to work hard */ + if (yy_atEOF) + return YYEOF; + + /* otherwise: need to refill the buffer */ + + /* first: make room (if you can) */ + if (yy_startRead > 0) { + System.arraycopy(yy_buffer, yy_startRead, yy_buffer, 0, yy_endRead - yy_startRead); + + /* translate stored positions */ + yy_endRead -= yy_startRead; + yy_currentPos -= yy_startRead; + yy_markedPos -= yy_startRead; + yy_pushbackPos -= yy_startRead; + yy_startRead = 0; + } + + /* is the buffer big enough? */ + if (yy_currentPos >= yy_buffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[yy_currentPos * 2]; + System.arraycopy(yy_buffer, 0, newBuffer, 0, yy_buffer.length); + yy_buffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = yy_reader.read(yy_buffer, yy_endRead, yy_buffer.length - yy_endRead); + + if (numRead == -1) + return YYEOF; + + yy_endRead += numRead; + + return yy_buffer[yy_currentPos++]; + } + + + /** + * Closes the input stream. + */ + final public void yyclose() throws java.io.IOException { + yy_atEOF = true; /* indicate end of file */ + yy_endRead = yy_startRead; /* invalidate buffer */ + yy_reader.close(); + } + + + /** + * Returns the current lexical state. + */ + final public int yystate() { + return yy_lexical_state; + } + + /** + * Enters a new lexical state + * + * @param newState + * the new lexical state + */ + final public void yybegin(int newState) { + yy_lexical_state = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + final public String yytext() { + return new String(yy_buffer, yy_startRead, yy_markedPos - yy_startRead); + } + + /** + * Returns the length of the matched text region. + */ + final public int yylength() { + return yy_markedPos - yy_startRead; + } + + + /** + * Reports an error that occured while scanning. + * + * @param errorCode + * the code of the errormessage to display + */ + private void yy_ScanError(int errorCode) { + try { + System.out.println(YY_ERROR_MSG[errorCode]); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println(YY_ERROR_MSG[YY_UNKNOWN_ERROR]); + } + + // System.exit(1); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number + * the number of characters to be read again. This number must + * not be greater than yylength()! + */ + private void yypushback(int number) { + if (number > yylength()) + yy_ScanError(YY_PUSHBACK_2BIG); + + yy_markedPos -= number; + } + + + /** + * Contains user EOF-code, which will be executed exactly once, when the + * end of file is reached + */ + private void yy_do_eof() { + if (!yy_eof_done) { + yy_eof_done = true; + hasMore = false; + + } + } + + + /** + * Resumes scanning until the next regular expression is matched, the end + * of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception IOException + * if any I/O-Error occurs + */ + public String primGetNextToken() throws java.io.IOException { + int yy_input; + int yy_action; + + + while (true) { + + yychar += yylength(); + + yy_atBOL = yy_markedPos <= 0 || yy_buffer[yy_markedPos - 1] == '\n'; + if (!yy_atBOL && yy_buffer[yy_markedPos - 1] == '\r') { + yy_atBOL = yy_advance() != '\n'; + if (!yy_atEOF) + yy_currentPos--; + } + + yy_action = -1; + + yy_currentPos = yy_startRead = yy_markedPos; + + if (yy_atBOL) + yy_state = YY_LEXSTATE[yy_lexical_state + 1]; + else + yy_state = YY_LEXSTATE[yy_lexical_state]; + + + yy_forAction : { + while (true) { + + yy_input = yy_advance(); + + if (yy_input == YYEOF) + break yy_forAction; + + yy_input = yycmap[yy_input]; + + boolean yy_isFinal = false; + boolean yy_noLookAhead = false; + + yy_forNext : { + switch (yy_state) { + case 0 : + switch (yy_input) { + default : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 7; + break yy_forNext; + } + + case 1 : + switch (yy_input) { + case 1 : + yy_isFinal = true; + yy_state = 8; + break yy_forNext; + case 2 : + yy_isFinal = true; + yy_state = 9; + break yy_forNext; + case 3 : + yy_isFinal = true; + yy_state = 10; + break yy_forNext; + case 6 : + case 7 : + case 9 : + yy_isFinal = true; + yy_state = 11; + break yy_forNext; + case 10 : + yy_isFinal = true; + yy_state = 12; + break yy_forNext; + default : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 7; + break yy_forNext; + } + + case 2 : + switch (yy_input) { + case 11 : + yy_isFinal = true; + yy_state = 13; + break yy_forNext; + case 15 : + yy_isFinal = true; + yy_state = 14; + break yy_forNext; + default : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 7; + break yy_forNext; + } + + case 3 : + switch (yy_input) { + case 6 : + case 9 : + yy_isFinal = true; + yy_state = 16; + break yy_forNext; + case 7 : + yy_isFinal = true; + yy_state = 17; + break yy_forNext; + case 23 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 18; + break yy_forNext; + case 24 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 19; + break yy_forNext; + default : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 15; + break yy_forNext; + } + + case 4 : + switch (yy_input) { + case 7 : + case 9 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 21; + break yy_forNext; + case 11 : + yy_isFinal = true; + yy_state = 22; + break yy_forNext; + case 23 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 23; + break yy_forNext; + case 24 : + yy_isFinal = true; + yy_state = 24; + break yy_forNext; + default : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 20; + break yy_forNext; + } + + case 5 : + switch (yy_input) { + case 7 : + case 9 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 21; + break yy_forNext; + case 24 : + yy_isFinal = true; + yy_state = 25; + break yy_forNext; + case 25 : + yy_isFinal = true; + yy_state = 26; + break yy_forNext; + default : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 20; + break yy_forNext; + } + + case 6 : + switch (yy_input) { + case 11 : + yy_isFinal = true; + yy_state = 26; + break yy_forNext; + case 6 : + case 7 : + case 9 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 27; + break yy_forNext; + case 23 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 28; + break yy_forNext; + case 24 : + yy_isFinal = true; + yy_state = 29; + break yy_forNext; + default : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 20; + break yy_forNext; + } + + case 8 : + switch (yy_input) { + case 2 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 30; + break yy_forNext; + default : + break yy_forAction; + } + + case 9 : + switch (yy_input) { + case 1 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 31; + break yy_forNext; + default : + break yy_forAction; + } + + case 10 : + switch (yy_input) { + case 4 : + yy_state = 32; + break yy_forNext; + default : + break yy_forAction; + } + + case 11 : + switch (yy_input) { + case 6 : + case 7 : + case 9 : + yy_state = 33; + break yy_forNext; + case 10 : + yy_state = 34; + break yy_forNext; + default : + break yy_forAction; + } + + case 12 : + switch (yy_input) { + case 11 : + yy_state = 35; + break yy_forNext; + default : + break yy_forAction; + } + + case 13 : + switch (yy_input) { + case 22 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 36; + break yy_forNext; + default : + break yy_forAction; + } + + case 14 : + switch (yy_input) { + case 16 : + yy_state = 37; + break yy_forNext; + default : + break yy_forAction; + } + + case 16 : + switch (yy_input) { + case 6 : + case 9 : + yy_isFinal = true; + yy_state = 16; + break yy_forNext; + case 7 : + yy_state = 38; + break yy_forNext; + default : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 15; + break yy_forNext; + } + + case 17 : + switch (yy_input) { + case 6 : + case 9 : + yy_isFinal = true; + yy_state = 16; + break yy_forNext; + case 7 : + yy_state = 38; + break yy_forNext; + default : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 15; + break yy_forNext; + } + + case 22 : + switch (yy_input) { + case 22 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 39; + break yy_forNext; + default : + break yy_forAction; + } + + case 24 : + switch (yy_input) { + case 10 : + yy_state = 40; + break yy_forNext; + default : + break yy_forAction; + } + + case 25 : + switch (yy_input) { + case 10 : + yy_state = 40; + break yy_forNext; + default : + break yy_forAction; + } + + case 26 : + switch (yy_input) { + case 22 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 41; + break yy_forNext; + default : + break yy_forAction; + } + + case 29 : + switch (yy_input) { + case 10 : + yy_state = 40; + break yy_forNext; + default : + break yy_forAction; + } + + case 32 : + switch (yy_input) { + case 5 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 42; + break yy_forNext; + default : + break yy_forAction; + } + + case 33 : + switch (yy_input) { + case 6 : + case 7 : + case 9 : + yy_state = 33; + break yy_forNext; + case 10 : + yy_state = 34; + break yy_forNext; + default : + break yy_forAction; + } + + case 34 : + switch (yy_input) { + case 11 : + yy_state = 35; + break yy_forNext; + default : + break yy_forAction; + } + + case 35 : + switch (yy_input) { + case 12 : + yy_state = 43; + break yy_forNext; + default : + break yy_forAction; + } + + case 37 : + switch (yy_input) { + case 17 : + yy_state = 44; + break yy_forNext; + default : + break yy_forAction; + } + + case 38 : + switch (yy_input) { + case 6 : + case 9 : + yy_isFinal = true; + yy_state = 16; + break yy_forNext; + case 7 : + yy_state = 38; + break yy_forNext; + default : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 15; + break yy_forNext; + } + + case 40 : + switch (yy_input) { + case 24 : + yy_isFinal = true; + yy_noLookAhead = true; + yy_state = 21; + break yy_forNext; + default : + break yy_forAction; + } + + case 43 : + switch (yy_input) { + case 13 : + yy_state = 45; + break yy_forNext; + default : + break yy_forAction; + } + + case 44 : + switch (yy_input) { + case 18 : + yy_state = 46; + break yy_forNext; + default : + break yy_forAction; + } + + case 45 : + switch (yy_input) { + case 14 : + yy_state = 47; + break yy_forNext; + default : + break yy_forAction; + } + + case 46 : + switch (yy_input) { + case 19 : + yy_state = 48; + break yy_forNext; + default : + break yy_forAction; + } + + case 47 : + switch (yy_input) { + case 6 : + case 7 : + case 9 : + yy_isFinal = true; + yy_state = 49; + break yy_forNext; + default : + break yy_forAction; + } + + case 48 : + switch (yy_input) { + case 20 : + yy_state = 50; + break yy_forNext; + default : + break yy_forAction; + } + + case 49 : + switch (yy_input) { + case 6 : + case 7 : + case 9 : + yy_isFinal = true; + yy_state = 49; + break yy_forNext; + default : + break yy_forAction; + } + + case 50 : + switch (yy_input) { + case 16 : + yy_state = 51; + break yy_forNext; + default : + break yy_forAction; + } + + case 51 : + switch (yy_input) { + case 21 : + yy_state = 52; + break yy_forNext; + default : + break yy_forAction; + } + + case 52 : + switch (yy_input) { + case 6 : + case 7 : + case 9 : + yy_state = 52; + break yy_forNext; + case 8 : + yy_isFinal = true; + yy_state = 53; + break yy_forNext; + default : + break yy_forAction; + } + + case 53 : + switch (yy_input) { + case 6 : + case 7 : + case 9 : + yy_isFinal = true; + yy_state = 53; + break yy_forNext; + default : + break yy_forAction; + } + + default : + yy_ScanError(YY_ILLEGAL_STATE); + break; + } + } + + if (yy_isFinal) { + yy_action = yy_state; + yy_markedPos = yy_currentPos; + if (yy_noLookAhead) + break yy_forAction; + } + + } + } + + + switch (yy_action) { + + case 25 : { + popState(); + valueText = string.toString(); + return EncodingParserConstants.StringValue; + } + case 55 : + break; + case 21 : { + yypushback(1); + popState(); + valueText = string.toString(); + return EncodingParserConstants.InvalidTerminatedStringValue; + } + case 56 : + break; + case 15 : + case 16 : { + yypushback(1); + yybegin(UnDelimitedString); + string.setLength(0); + } + case 57 : + break; + case 28 : + case 29 : { + yypushback(1); + popState(); + valueText = string.toString(); + return EncodingParserConstants.InvalidTermintatedUnDelimitedStringValue; + } + case 58 : + break; + case 39 : { + yypushback(2); + popState(); + valueText = string.toString(); + return EncodingParserConstants.InvalidTerminatedStringValue; + } + case 59 : + break; + case 41 : { + yypushback(2); + popState(); + valueText = string.toString(); + return EncodingParserConstants.InvalidTerminatedStringValue; + } + case 60 : + break; + case 7 : + case 8 : + case 9 : + case 10 : + case 11 : + case 12 : + case 13 : + case 14 : + case 17 : { + if (yychar > MAX_TO_SCAN) { + hasMore = false; + return EncodingParserConstants.MAX_CHARS_REACHED; + } + } + case 61 : + break; + case 30 : { + if (yychar == 0) { + hasMore = false; + return EncodingParserConstants.UTF16BE; + } + } + case 62 : + break; + case 31 : { + if (yychar == 0) { + hasMore = false; + return EncodingParserConstants.UTF16LE; + } + } + case 63 : + break; + case 42 : { + if (yychar == 0) { + hasMore = false; + return EncodingParserConstants.UTF83ByteBOM; + } + } + case 64 : + break; + case 49 : { + if (yychar == 0) { + yybegin(ST_XMLDecl); + return XMLHeadTokenizerConstants.XMLDeclStart; + } + } + case 65 : + break; + case 36 : { + yybegin(YYINITIAL); + hasMore = false; + return XMLHeadTokenizerConstants.XMLDeclEnd; + } + case 66 : + break; + case 53 : { + pushCurrentState(); + yybegin(QuotedAttributeValue); + return XMLHeadTokenizerConstants.XMLDelEncoding; + } + case 67 : + break; + case 23 : { + popState(); + valueText = string.toString(); + return EncodingParserConstants.StringValue; + } + case 68 : + break; + case 20 : + case 22 : + case 24 : + case 26 : { + string.append(yytext()); + } + case 69 : + break; + case 19 : { + yybegin(SQ_STRING); + string.setLength(0); + } + case 70 : + break; + case 18 : { + yybegin(DQ_STRING); + string.setLength(0); + } + case 71 : + break; + case 27 : { + yypushback(1); + popState(); + valueText = string.toString(); + return EncodingParserConstants.UnDelimitedStringValue; + } + case 72 : + break; + default : + if (yy_input == YYEOF && yy_startRead == yy_currentPos) { + yy_atEOF = true; + yy_do_eof(); + { + hasMore = false; + return EncodingParserConstants.EOF; + } + } else { + yy_ScanError(YY_NO_MATCH); + } + } + } + } + + /** + * Runs the scanner on input files. + * + * This main method is the debugging routine for the scanner. It prints + * each returned token to System.out until the end of file is reached, or + * an error occured. + * + * @param argv + * the command line, contains the filenames to run the scanner + * on. + */ + public static void main(String argv[]) { + for (int i = 0; i < argv.length; i++) { + XMLHeadTokenizer scanner = null; + try { + scanner = new XMLHeadTokenizer(new java.io.FileReader(argv[i])); + } catch (java.io.FileNotFoundException e) { + System.out.println("File not found : \"" + argv[i] + "\""); + System.exit(1); + } + // catch (java.io.IOException e) { + // System.out.println("Error opening file \"" + argv[i] + "\""); + // System.exit(1); + // } + catch (ArrayIndexOutOfBoundsException e) { + System.out.println("Usage : java XMLHeadTokenizer <inputfile>"); + System.exit(1); + } + + try { + do { + System.out.println(scanner.primGetNextToken()); + } while (!scanner.yy_atEOF); + + } catch (java.io.IOException e) { + System.out.println("An I/O error occured while scanning :"); + System.out.println(e); + System.exit(1); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } + } + + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/XMLHeadTokenizerConstants.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/XMLHeadTokenizerConstants.java new file mode 100644 index 0000000000..3321348059 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/contenttype/XMLHeadTokenizerConstants.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.contenttype; + + +public interface XMLHeadTokenizerConstants extends EncodingParserConstants { + + final String XMLDeclEnd = "XMLDeclEnd"; //$NON-NLS-1$ + final String XMLDeclStart = "XMLDeclStart"; //$NON-NLS-1$ + final String XMLDelEncoding = "XMLDelEncoding"; //$NON-NLS-1$ + // final String XMLDeclVersion = "XMLDeclVersion"; +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/AttrImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/AttrImpl.java new file mode 100644 index 0000000000..959f512544 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/AttrImpl.java @@ -0,0 +1,756 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import java.util.Iterator; + +import org.eclipse.wst.common.contentmodel.CMAttributeDeclaration; +import org.eclipse.wst.common.contentmodel.CMElementDeclaration; +import org.eclipse.wst.common.contentmodel.CMNamedNodeMap; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionContainer; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.document.XMLAttr; +import org.eclipse.wst.xml.core.document.XMLCharEntity; +import org.eclipse.wst.xml.core.document.XMLNamespace; +import org.eclipse.wst.xml.core.jsp.model.parser.temp.XMLJSPRegionContexts; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + + +/** + * AttrImpl class + */ +public class AttrImpl extends NodeImpl implements XMLAttr { + private ITextRegion equalRegion = null; + + private String name = null; + private ITextRegion nameRegion = null; + private String namespaceURI = null; + private ElementImpl ownerElement = null; + private ITextRegion valueRegion = null; + private String valueSource = null; + + /** + * AttrImpl constructor + */ + protected AttrImpl() { + super(); + } + + /** + * AttrImpl constructor + * + * @param that + * AttrImpl + */ + protected AttrImpl(AttrImpl that) { + super(that); + + if (that != null) { + this.name = that.name; + this.valueSource = that.getValueSource(); + } + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + */ + public Node cloneNode(boolean deep) { + AttrImpl cloned = new AttrImpl(this); + return cloned; + } + + /** + */ + protected CMAttributeDeclaration getDeclaration() { + ElementImpl element = (ElementImpl) getOwnerElement(); + if (element == null) + return null; + CMElementDeclaration elementDecl = element.getDeclaration(); + if (elementDecl == null) + return null; + CMNamedNodeMap attributes = elementDecl.getAttributes(); + if (attributes == null) + return null; + return (CMAttributeDeclaration) attributes.getNamedItem(getName()); + } + + /** + * getEndOffset method + * + * @return int + */ + public int getEndOffset() { + if (this.ownerElement == null) + return 0; + int offset = this.ownerElement.getStartOffset(); + if (this.valueRegion != null) { + return (offset + this.valueRegion.getEnd()); + } + if (this.equalRegion != null) { + return (offset + this.equalRegion.getEnd()); + } + if (this.nameRegion != null) { + return (offset + this.nameRegion.getEnd()); + } + return 0; + } + + /** + * getEqualRegion method + * + * @return com.ibm.sed.structuredDocument.ITextRegion + */ + public ITextRegion getEqualRegion() { + return this.equalRegion; + } + + /** + */ + public String getLocalName() { + if (this.name == null) + return null; + int index = this.name.indexOf(':'); + if (index < 0) + return this.name; + return this.name.substring(index + 1); + } + + /** + * getName method + * + * @return java.lang.String + */ + public String getName() { + if (this.name == null) + return new String(); + return this.name; + } + + /** + * getNameRegion method + * + * @return com.ibm.sed.structuredDocument.ITextRegion + */ + public ITextRegion getNameRegion() { + return this.nameRegion; + } + + public int getNameRegionEndOffset() { + if (this.ownerElement == null) + return 0; + // assuming the firstStructuredDocumentRegion is the one that contains + // attributes + IStructuredDocumentRegion flatNode = this.ownerElement.getFirstStructuredDocumentRegion(); + if (flatNode == null) + return 0; + return flatNode.getEndOffset(this.nameRegion); + } + + public int getNameRegionStartOffset() { + if (this.ownerElement == null) + return 0; + // assuming the firstStructuredDocumentRegion is the one that contains + // attributes + IStructuredDocumentRegion flatNode = this.ownerElement.getFirstStructuredDocumentRegion(); + if (flatNode == null) + return 0; + return flatNode.getStartOffset(this.nameRegion); + } + + public String getNameRegionText() { + if (this.ownerElement == null) + return null; + // assuming the firstStructuredDocumentRegion is the one that contains + // attributes + IStructuredDocumentRegion flatNode = this.ownerElement.getFirstStructuredDocumentRegion(); + if (flatNode == null) + return null; + return flatNode.getText(this.nameRegion); + } + + public int getNameRegionTextEndOffset() { + if (this.ownerElement == null) + return 0; + // assuming the firstStructuredDocumentRegion is the one that contains + // attributes + IStructuredDocumentRegion flatNode = this.ownerElement.getFirstStructuredDocumentRegion(); + if (flatNode == null) + return 0; + return flatNode.getTextEndOffset(this.nameRegion); + } + + /** + */ + public String getNamespaceURI() { + String nsAttrName = null; + String prefix = getPrefix(); + if (prefix != null && prefix.length() > 0) { + if (prefix.equals(XMLNamespace.XMLNS)) { + // fixed URI + return XMLNamespace.XMLNS_URI; + } + nsAttrName = XMLNamespace.XMLNS_PREFIX + prefix; + } else { + String name = getName(); + if (name != null && name.equals(XMLNamespace.XMLNS)) { + // fixed URI + return XMLNamespace.XMLNS_URI; + } + // does not inherit namespace from owner element + // if (this.ownerElement != null) return + // this.ownerElement.getNamespaceURI(); + return this.namespaceURI; + } + + for (Node node = this.ownerElement; node != null; node = node.getParentNode()) { + if (node.getNodeType() != ELEMENT_NODE) + break; + Element element = (Element) node; + Attr attr = element.getAttributeNode(nsAttrName); + if (attr != null) + return attr.getValue(); + } + + return this.namespaceURI; + } + + /** + * getNodeName method + * + * @return java.lang.String + */ + public String getNodeName() { + return getName(); + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return ATTRIBUTE_NODE; + } + + /** + * getNodeValue method + * + * @return java.lang.String + */ + public String getNodeValue() { + return getValue(); + } + + /** + * getOwnerElement method + * + * @return org.w3c.dom.Element + */ + public Element getOwnerElement() { + return this.ownerElement; + } + + /** + */ + public String getPrefix() { + if (this.name == null) + return null; + int index = this.name.indexOf(':'); + if (index <= 0) + return null; + // exclude JSP tag in name + if (this.name.charAt(0) == '<') + return null; + return this.name.substring(0, index); + } + + /** + * getSpecified method + * + * @return boolean + */ + public boolean getSpecified() { + return true; + } + + /** + * getStartOffset method + * + * @return int + */ + public int getStartOffset() { + if (this.ownerElement == null) + return 0; + int offset = this.ownerElement.getStartOffset(); + if (this.nameRegion != null) { + return (offset + this.nameRegion.getStart()); + } + if (this.equalRegion != null) { + return (offset + this.equalRegion.getStart()); + } + if (this.valueRegion != null) { + return (offset + this.valueRegion.getStart()); + } + return 0; + } + + /** + * getValue method + * + * @return java.lang.String + */ + public String getValue() { + return getValue(getValueSource()); + } + + /** + * Returns value for the source + */ + private String getValue(String source) { + if (source == null) + return new String(); + if (source.length() == 0) + return source; + StringBuffer buffer = null; + int offset = 0; + int length = source.length(); + int ref = source.indexOf('&'); + while (ref >= 0) { + int end = source.indexOf(';', ref + 1); + if (end > ref + 1) { + String name = source.substring(ref + 1, end); + String value = getCharValue(name); + if (value != null) { + if (buffer == null) + buffer = new StringBuffer(length); + if (ref > offset) + buffer.append(source.substring(offset, ref)); + buffer.append(value); + offset = end + 1; + ref = end; + } + } + ref = source.indexOf('&', ref + 1); + } + if (buffer == null) + return source; + if (length > offset) + buffer.append(source.substring(offset)); + return buffer.toString(); + } + + /** + * getValueRegion method + * + * @return com.ibm.sed.structuredDocument.ITextRegion + */ + public ITextRegion getValueRegion() { + return this.valueRegion; + } + + public int getValueRegionStartOffset() { + if (this.ownerElement == null) + return 0; + // assuming the firstStructuredDocumentRegion is the one that contains + // attributes + IStructuredDocumentRegion flatNode = this.ownerElement.getFirstStructuredDocumentRegion(); + if (flatNode == null) + return 0; + return flatNode.getStartOffset(this.valueRegion); + } + + public String getValueRegionText() { + if (this.ownerElement == null) + return null; + // assuming the firstStructuredDocumentRegion is the one that contains + // attributes + IStructuredDocumentRegion flatNode = this.ownerElement.getFirstStructuredDocumentRegion(); + if (flatNode == null) + return null; + if (this.valueRegion == null) + return null; + return flatNode.getText(this.valueRegion); + } + + /** + */ + public String getValueSource() { + if (this.valueSource != null) + return this.valueSource; + // DW: 4/16/2003 due to change in structuredDocument ... we need a + // flatnode to + // get at region values. For now I'll assume this is always the first + // flatnode .. may need to make smarter later (e.g. to search for + // the flatnode that this.valueRegion belongs to. + // DW: 4/30/2003 For some reason, this method is getting called a lot + // Not sure if its a threading problem, or a fundamental error + // elsewhere. + // It needs more investigation, but in the use cases I've seen, + // doesn't + // seem to hurt to simply return null in those cases. I saw this null + // case, + // when tryint go format an XML file. + if (this.ownerElement == null) + return null; + IStructuredDocumentRegion ownerRegion = this.ownerElement.getFirstStructuredDocumentRegion(); + if (ownerRegion == null) + return null; + if (this.valueRegion != null) + return StructuredDocumentRegionUtil.getAttrValue(ownerRegion, this.valueRegion); + return new String(); + } + + private String getValueSource(ElementImpl ownerElement) { + if (this.valueSource != null) + return this.valueSource; + // DW: 4/16/2003 due to change in structuredDocument ... we need a + // flatnode to + // get at region values. For now I'll assume this is always the first + // flatnode .. may need to make smarter later (e.g. to search for + // the flatnode that this.valueRegion belongs to. + if (this.valueRegion != null) + return StructuredDocumentRegionUtil.getAttrValue(ownerElement.getStructuredDocumentRegion(), this.valueRegion); + return new String(); + } + + /** + */ + private String getValueSource(String value) { + if (value == null) + return null; + if (value.length() == 0) + return value; + StringBuffer buffer = null; + int offset = 0; + int length = value.length(); + int amp = value.indexOf('&'); + while (amp >= 0) { + if (buffer == null) + buffer = new StringBuffer(length + 4); + if (amp > offset) + buffer.append(value.substring(offset, amp)); + buffer.append(XMLCharEntity.AMP_REF); + offset = amp + 1; + amp = value.indexOf('&', offset); + } + if (buffer == null) + return value; + if (length > offset) + buffer.append(value.substring(offset)); + return buffer.toString(); + } + + /** + * Check if Attr has JSP in value + */ + public boolean hasJSPValue() { + if (this.valueRegion == null) + return false; + if (!(this.valueRegion instanceof ITextRegionContainer)) + return false; + ITextRegionList regions = ((ITextRegionContainer) this.valueRegion).getRegions(); + if (regions == null) + return false; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + if (region == null) + continue; + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_TAG_OPEN || regionType == XMLJSPRegionContexts.JSP_SCRIPTLET_OPEN || regionType == XMLJSPRegionContexts.JSP_EXPRESSION_OPEN || regionType == XMLJSPRegionContexts.JSP_DECLARATION_OPEN || regionType == XMLJSPRegionContexts.JSP_DIRECTIVE_OPEN) + return true; + } + return false; + } + + /** + * Check if Attr has only name but not equal sign nor value + */ + public boolean hasNameOnly() { + return (this.nameRegion != null && this.equalRegion == null && this.valueRegion == null); + } + + /** + */ + protected final boolean hasPrefix() { + if (this.name == null) + return false; + if (this.name.indexOf(':') <= 0) + return false; + // exclude JSP tag in name + if (this.name.charAt(0) == '<') + return false; + return true; + } + + /** + */ + protected final boolean ignoreCase() { + if (this.ownerElement != null) { + if (this.ownerElement.ignoreCase()) { + return !hasPrefix(); + } + } else { + DocumentImpl document = (DocumentImpl) getOwnerDocument(); + if (document != null && document.ignoreCase()) { + // even in case insensitive document, if having prefix, it's + // case sensitive + return !hasPrefix(); + } + } + return false; + } + + /** + */ + public boolean isGlobalAttr() { + if (hasPrefix()) + return false; + if (this.ownerElement == null) + return false; + return this.ownerElement.isGlobalTag(); + } + + /** + */ + public final boolean isXMLAttr() { + if (this.ownerElement != null) { + if (!this.ownerElement.isXMLTag()) { + return hasPrefix(); + } + } else { + DocumentImpl document = (DocumentImpl) getOwnerDocument(); + if (document != null && !document.isXMLType()) { + // even in non-XML document, if having prefix, it's XML tag + return hasPrefix(); + } + } + return true; + } + + /** + * matchName method + * + * @return boolean + * @param name + * java.lang.String + */ + protected boolean matchName(String name) { + if (name == null) + return (this.name == null); + if (this.name == null) + return false; + if (!ignoreCase()) + return this.name.equals(name); + return this.name.equalsIgnoreCase(name); + } + + /** + * notifyValueChanged method + */ + protected void notifyNameChanged() { + if (this.ownerElement == null) + return; + DocumentImpl document = (DocumentImpl) this.ownerElement.getContainerDocument(); + if (document == null) + return; + XMLModelImpl model = (XMLModelImpl) document.getModel(); + if (model == null) + return; + model.nameChanged(this); + } + + /** + * notifyValueChanged method + */ + protected void notifyValueChanged() { + if (this.ownerElement == null) + return; + DocumentImpl document = (DocumentImpl) this.ownerElement.getContainerDocument(); + if (document == null) + return; + XMLModelImpl model = (XMLModelImpl) document.getModel(); + if (model == null) + return; + model.valueChanged(this); + } + + /** + * removeRegions method + */ + void removeRegions() { + this.nameRegion = null; + this.valueRegion = null; + this.equalRegion = null; + } + + /** + */ + void resetRegions() { + this.valueSource = getValueSource(); + removeRegions(); + } + + /** + */ + void resetRegions(ElementImpl ownerElement) { + this.valueSource = getValueSource(ownerElement); + removeRegions(); + } + + /** + * setEqualRegion method + * + * @param equalRegion + * com.ibm.sed.structuredDocument.ITextRegion + */ + void setEqualRegion(ITextRegion equalRegion) { + this.equalRegion = equalRegion; + } + + /** + * setName method + * + * @param name + * java.lang.String + */ + protected void setName(String name) { + String value = null; + int startOffset = 0; + if (this.ownerElement != null) { + value = getValue(); + startOffset = this.ownerElement.getStartOffset(); + this.ownerElement.notify(CHANGE, this, value, null, startOffset); + } + this.name = name; + if (this.ownerElement != null) { + this.ownerElement.notify(CHANGE, this, null, value, startOffset); + } + } + + /** + * setNameRegion method + * + * @param nameRegion + * com.ibm.sed.structuredDocument.ITextRegion + */ + void setNameRegion(ITextRegion nameRegion) { + this.nameRegion = nameRegion; + } + + /** + */ + protected void setNamespaceURI(String namespaceURI) { + this.namespaceURI = namespaceURI; + } + + /** + * setNodeValue method + * + * @param nodeValue + * java.lang.String + */ + public void setNodeValue(String nodeValue) throws DOMException { + setValue(nodeValue); + } + + /** + * setOwnerElement method + * + * @param ownerElement + * org.w3c.dom.Element + */ + protected void setOwnerElement(Element ownerElement) { + this.ownerElement = (ElementImpl) ownerElement; + } + + /** + */ + public void setPrefix(String prefix) throws DOMException { + if (this.ownerElement != null && !this.ownerElement.isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + int prefixLength = (prefix != null ? prefix.length() : 0); + String localName = getLocalName(); + if (prefixLength == 0) { + setName(localName); + return; + } + if (localName == null) + localName = new String(); + int localLength = localName.length(); + StringBuffer buffer = new StringBuffer(prefixLength + 1 + localLength); + buffer.append(prefix); + buffer.append(':'); + buffer.append(localName); + setName(buffer.toString()); + + notifyNameChanged(); + } + + /** + * setValue method + * + * @param value + * java.lang.String + */ + public void setValue(String value) { + // Remember: as we account for "floaters" in + // future, remember that some are created + // in the natural process of implementing + // DOM apis. + // this "self notification" of about/changed, + // is added for this case, because it known to + // be called from properties pages. Should be a + // added to all DOM Modifiying APIs eventually. + try { + getModel().aboutToChangeModel(); + setValueSource(getValueSource(value)); + } finally { + getModel().changedModel(); + } + } + + /** + * setValueRegion method + * + * @param newValueRegion + * com.ibm.sed.structuredDocument.ITextRegion + */ + void setValueRegion(ITextRegion valueRegion) { + this.valueRegion = valueRegion; + if (valueRegion != null) + this.valueSource = null; + } + + /** + */ + public void setValueSource(String source) { + if (this.ownerElement != null && !this.ownerElement.isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + this.valueSource = source; + + notifyValueChanged(); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CDATASectionImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CDATASectionImpl.java new file mode 100644 index 0000000000..3954ca2d40 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CDATASectionImpl.java @@ -0,0 +1,140 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import java.util.Iterator; + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; +import org.w3c.dom.CDATASection; +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; + + +/** + * CDATASectionImpl class + */ +public class CDATASectionImpl extends TextImpl implements CDATASection { + + /** + * CDATASectionImpl constructor + */ + protected CDATASectionImpl() { + super(); + } + + /** + * CDATASectionImpl constructor + * + * @param that + * CDATASectionImpl + */ + protected CDATASectionImpl(CDATASectionImpl that) { + super(that); + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + * @param deep + * boolean + */ + public Node cloneNode(boolean deep) { + CDATASectionImpl cloned = new CDATASectionImpl(this); + return cloned; + } + + /** + * getData method + * + * @return java.lang.String + */ + public String getData() throws DOMException { + // instead of super(TextImpl).getData(), call getCharacterData() + String data = getCharacterData(); + if (data == null) { + data = getData(getStructuredDocumentRegion()); + if (data == null) + data = new String(); + } + return data; + } + + /** + */ + private String getData(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return null; + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return null; + + ITextRegion contentRegion = null; + StringBuffer buffer = null; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_CDATA_OPEN || regionType == XMLRegionContext.XML_CDATA_CLOSE) { + continue; + } + if (contentRegion == null) { // first content + contentRegion = region; + } else { // multiple contents + if (buffer == null) { + buffer = new StringBuffer(flatNode.getText(contentRegion)); + } + buffer.append(flatNode.getText(region)); + } + } + + if (buffer != null) + return buffer.toString(); + if (contentRegion != null) + return flatNode.getText(contentRegion); + return null; + } + + /** + * getNodeName method + * + * @return java.lang.String + */ + public String getNodeName() { + return "#cdata-section";//$NON-NLS-1$ + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return CDATA_SECTION_NODE; + } + + /** + */ + public boolean isClosed() { + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null) + return true; // will be generated + String regionType = StructuredDocumentRegionUtil.getLastRegionType(flatNode); + return (regionType == XMLRegionContext.XML_CDATA_CLOSE); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CMNodeUtil.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CMNodeUtil.java new file mode 100644 index 0000000000..44ecfabad5 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CMNodeUtil.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.common.contentmodel.CMAttributeDeclaration; +import org.eclipse.wst.common.contentmodel.CMElementDeclaration; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; + + +/** + */ +public class CMNodeUtil { + + /** + */ + public static CMAttributeDeclaration getAttributeDeclaration(Attr attr) { + if (attr == null) + return null; + return ((AttrImpl) attr).getDeclaration(); + } + + /** + */ + public static CMElementDeclaration getElementDeclaration(Element element) { + if (element == null) + return null; + return ((ElementImpl) element).getDeclaration(); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CharacterDataImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CharacterDataImpl.java new file mode 100644 index 0000000000..4c4a2dbc72 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CharacterDataImpl.java @@ -0,0 +1,353 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.xml.core.jsp.model.parser.temp.XMLJSPRegionContexts; +import org.w3c.dom.CharacterData; +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; + + +/** + * CharacterDataImpl class + */ +public abstract class CharacterDataImpl extends NodeImpl implements XMLJSPRegionContexts, CharacterData { + + private String data = null; + + /** + * CharacterDataImpl constructor + */ + protected CharacterDataImpl() { + super(); + } + + /** + * CharacterDataImpl constructor + * + * @param that + * CharacterDataImpl + */ + protected CharacterDataImpl(CharacterDataImpl that) { + super(that); + + if (that != null) { + this.data = that.getData(); + } + } + + /** + * appendData method + * + * @param arg + * java.lang.String + */ + public void appendData(String arg) throws DOMException { + if (arg == null) + return; + + String data = getData(); + if (data == null) + data = arg; + else + data += arg; + setData(data); + } + + /** + * deleteData method + * + * @param offset + * int + * @param count + * int + */ + public void deleteData(int offset, int count) throws DOMException { + if (count == 0) + return; + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + if (count < 0 || offset < 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + String data = getData(); + if (data == null) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + int length = data.length(); + if (offset > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + if (offset == 0) { + if (count > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + if (count == length) + data = new String(); + else + data = data.substring(count); + } else { + int end = offset + count; + if (end > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + if (end == length) + data = data.substring(0, offset); + else + data = data.substring(0, offset) + data.substring(end); + } + setData(data); + } + + /** + */ + protected final String getCharacterData() { + return this.data; + } + + /** + * getData method + * + * @return java.lang.String + */ + public String getData() throws DOMException { + return getCharacterData(); + } + + /** + * getLength method + * + * @return int + */ + public int getLength() { + String data = getData(); + if (data == null) + return 0; + return data.length(); + } + + /** + * getNodeValue method + * + * @return java.lang.String + */ + public String getNodeValue() { + return getData(); + } + + /** + * insertData method + * + * @param offset + * int + * @param arg + * java.lang.String + */ + public void insertData(int offset, String arg) throws DOMException { + if (arg == null) + return; + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + if (offset < 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + String data = getData(); + if (data == null) { + if (offset > 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + data = arg; + } else if (offset == 0) { + data = arg + data; + } else { + int length = data.length(); + if (offset > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + if (offset == length) + data += arg; + else + data = data.substring(0, offset) + arg + data.substring(offset); + } + setData(data); + } + + /** + * isJSPContent method + * + * @return boolean + */ + public boolean isJSPContent() { + Node parent = getParentNode(); + if (parent == null || parent.getNodeType() != Node.ELEMENT_NODE) + return false; + ElementImpl element = (ElementImpl) parent; + return element.isJSPContainer(); + } + + /** + * replaceData method + * + * @param offset + * int + * @param count + * int + * @param arg + * java.lang.String + */ + public void replaceData(int offset, int count, String arg) throws DOMException { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + if (arg == null) { + deleteData(offset, count); + return; + } + if (count == 0) { + insertData(offset, arg); + return; + } + if (offset < 0 || count < 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + String data = getData(); + if (data == null) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } else if (offset == 0) { + int length = data.length(); + if (count > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + if (count == length) + data = arg; + else + data = arg + data.substring(count); + } else { + int length = data.length(); + int end = offset + count; + if (end > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + if (end == length) + data = data.substring(0, offset) + arg; + else + data = data.substring(0, offset) + arg + data.substring(end); + } + setData(data); + } + + /** + */ + void resetStructuredDocumentRegions() { + this.data = getData(); + setStructuredDocumentRegion(null); + } + + /** + * setData method + * + * @param data + * java.lang.String + */ + public void setData(String data) throws DOMException { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.data = data; + + notifyValueChanged(); + } + + /** + * setNodeValue method + * + * @param nodeValue + * java.lang.String + */ + public void setNodeValue(String nodeValue) throws DOMException { + setData(nodeValue); + } + + /** + */ + void setStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + super.setStructuredDocumentRegion(flatNode); + if (flatNode != null) + this.data = null; + } + + /** + * substringData method + * + * @return java.lang.String + * @param offset + * int + * @param count + * int + */ + public String substringData(int offset, int count) throws DOMException { + if (count == 0) + return new String(); + if (offset < 0 || count < 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + String data = getData(); + if (data == null) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + int length = data.length(); + if (offset == 0 && count == length) + return data; + if (offset > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + int end = offset + count; + if (end > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + return data.substring(offset, end); + } + + /** + * toString method + * + * @return java.lang.String + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(getNodeName()); + buffer.append('('); + buffer.append(getData()); + buffer.append(')'); + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode != null) { + buffer.append('@'); + buffer.append(flatNode.toString()); + } + return buffer.toString(); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CommentImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CommentImpl.java new file mode 100644 index 0000000000..2d8d257072 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/CommentImpl.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import java.util.Iterator; + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.jsp.model.parser.temp.XMLJSPRegionContexts; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; +import org.w3c.dom.Comment; +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; + + +/** + * CommentImpl class + */ +public class CommentImpl extends CharacterDataImpl implements Comment { + + private boolean isJSPTag = false; + + /** + * CommentImpl constructor + */ + protected CommentImpl() { + super(); + } + + /** + * CommentImpl constructor + * + * @param that + * CommentImpl + */ + protected CommentImpl(CommentImpl that) { + super(that); + + if (that != null) { + this.isJSPTag = that.isJSPTag; + } + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + * @param deep + * boolean + */ + public Node cloneNode(boolean deep) { + CommentImpl cloned = new CommentImpl(this); + return cloned; + } + + /** + * getData method + * + * @return java.lang.String + */ + public String getData() throws DOMException { + String data = getCharacterData(); + if (data == null) { + data = getData(getStructuredDocumentRegion()); + if (data == null) + data = new String(); + } + return data; + } + + /** + */ + private String getData(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return null; + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return null; + + ITextRegion contentRegion = null; + StringBuffer buffer = null; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_COMMENT_OPEN || regionType == JSP_COMMENT_OPEN || regionType == XMLRegionContext.XML_COMMENT_CLOSE || regionType == JSP_COMMENT_CLOSE) { + continue; + } + if (contentRegion == null) { // first content + contentRegion = region; + } else { // multiple contents + if (buffer == null) { + buffer = new StringBuffer(flatNode.getText(contentRegion)); + } + buffer.append(flatNode.getText(region)); + } + } + + if (buffer != null) + return buffer.toString(); + if (contentRegion != null) + return flatNode.getText(contentRegion); + return null; + } + + /** + * getNodeName method + * + * @return java.lang.String + */ + public String getNodeName() { + return "#comment";//$NON-NLS-1$ + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return COMMENT_NODE; + } + + /** + */ + public boolean isClosed() { + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null) + return true; // will be generated + String regionType = StructuredDocumentRegionUtil.getLastRegionType(flatNode); + return (regionType == XMLRegionContext.XML_COMMENT_CLOSE || regionType == XMLJSPRegionContexts.JSP_COMMENT_CLOSE); + } + + /** + * isJSP method + * + * @return boolean + */ + public boolean isJSPTag() { + return this.isJSPTag; + } + + /** + * setJSPTag method + * + * @param isJSPTag + * boolean + */ + public void setJSPTag(boolean isJSPTag) { + if (isJSPTag == this.isJSPTag) + return; + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + DocumentImpl document = (DocumentImpl) getOwnerDocument(); + if (isJSPTag) { + if (document == null || !document.isJSPType()) + return; + } + + this.isJSPTag = isJSPTag; + + if (getContainerDocument() != null) { + // already in the tree, update IStructuredDocument + setData(getData()); // calls notifyValueChanged(); + } + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentFragmentImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentFragmentImpl.java new file mode 100644 index 0000000000..06058a549f --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentFragmentImpl.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.Node; + + +/** + * DocumentFragmentImpl class + */ +public class DocumentFragmentImpl extends NodeContainer implements DocumentFragment { + + /** + * DocumentFragmentImpl constructor + */ + protected DocumentFragmentImpl() { + super(); + } + + /** + * DocumentFragmentImpl constructor + * + * @param that + * DocumentFragmentImpl + */ + protected DocumentFragmentImpl(DocumentFragmentImpl that) { + super(that); + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + * @param deep + * boolean + */ + public Node cloneNode(boolean deep) { + DocumentFragmentImpl cloned = new DocumentFragmentImpl(this); + if (deep) + cloneChildNodes(cloned, deep); + return cloned; + } + + /** + * getNodeName method + * + * @return java.lang.String + */ + public String getNodeName() { + return "#document-fragment";//$NON-NLS-1$ + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return DOCUMENT_FRAGMENT_NODE; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentImpl.java new file mode 100644 index 0000000000..f4ca2cc236 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentImpl.java @@ -0,0 +1,1073 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +// for org.apache.xerces 3.2.1 +// import org.apache.xerces.utils.XMLCharacterProperties; +// DMW modified for XML4J 4.0.1 +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.wst.common.contentmodel.CMDocument; +import org.eclipse.wst.common.contentmodel.CMEntityDeclaration; +import org.eclipse.wst.common.contentmodel.CMNamedNodeMap; +import org.eclipse.wst.common.contentmodel.modelquery.ModelQuery; +import org.eclipse.wst.common.encoding.content.IContentTypeIdentifier; +import org.eclipse.wst.sse.core.modelhandler.IModelHandler; +import org.eclipse.wst.xml.core.NameValidator; +import org.eclipse.wst.xml.core.commentelement.impl.CommentElementRegistry; +import org.eclipse.wst.xml.core.document.DocumentTypeAdapter; +import org.eclipse.wst.xml.core.document.JSPTag; +import org.eclipse.wst.xml.core.document.XMLCharEntity; +import org.eclipse.wst.xml.core.document.XMLDocument; +import org.eclipse.wst.xml.core.document.XMLElement; +import org.eclipse.wst.xml.core.document.XMLModel; +import org.eclipse.wst.xml.core.modelquery.ModelQueryUtil; +import org.w3c.dom.Attr; +import org.w3c.dom.CDATASection; +import org.w3c.dom.Comment; +import org.w3c.dom.DOMException; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.w3c.dom.Entity; +import org.w3c.dom.EntityReference; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Notation; +import org.w3c.dom.ProcessingInstruction; +import org.w3c.dom.Text; +import org.w3c.dom.ranges.Range; +import org.w3c.dom.traversal.NodeFilter; +import org.w3c.dom.traversal.NodeIterator; +import org.w3c.dom.traversal.TreeWalker; + + +/** + * DocumentImpl class + */ +public class DocumentImpl extends NodeContainer implements XMLDocument { + + /** + * Internal-use only class. This class was added to better able to handle + * repetetive request for getElementsByTagName. The cache is cleared when + * ever the document changes at all, so still not real efficient, + */ + class TagNameCache { + + private boolean active = true; + + private Map cache; + + public TagNameCache() { + super(); + cache = new HashMap(); + } + + /** + * @param b + */ + public void activate(boolean b) { + active = b; + if (!b) + clear(); + } + + public void addItem(String tagname, NodeListImpl nodelist) { + if (tagname == null || nodelist == null) + return; + cache.put(tagname, nodelist); + } + + public void clear() { + cache.clear(); + } + + public NodeListImpl getItem(String tagName) { + NodeListImpl result = null; + if (active) { + result = (NodeListImpl) cache.get(tagName); + // if (result != null) { + // System.out.println("getElementsByTagname from cache: " + + // tagName); + // } + } + return result; + } + + } + + // this is a constant just to give compile-time control over + // whether or not to use the cache. If, in future, its found that + // there are no (or few) "duplicate requests" ... then this cache + // is not needed. + private static final boolean usetagnamecache = true; + private DocumentTypeAdapter documentTypeAdapter = null; + + private XMLModelImpl model = null; + private TagNameCache tagNameCache; + + /** + * DocumentImpl constructor + */ + protected DocumentImpl() { + super(); + if (usetagnamecache) { + tagNameCache = new TagNameCache(); + } + } + + /** + * DocumentImpl constructor + * + * @param that + * DocumentImpl + */ + protected DocumentImpl(DocumentImpl that) { + super(that); + if (usetagnamecache) { + tagNameCache = new TagNameCache(); + } + } + + /** + * @param b + */ + void activateTagNameCache(boolean b) { + tagNameCache.activate(b); + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * Changes the <code>ownerDocument</code> of a node, its children, as + * well as the attached attribute nodes if there are any. If the node has + * a parent it is first removed from its parent child list. This + * effectively allows moving a subtree from one document to another. The + * following list describes the specifics for each type of node. + * <dl> + * <dt>ATTRIBUTE_NODE</dt> + * <dd>The <code>ownerElement</code> attribute is set to + * <code>null</code> and the <code>specified</code> flag is set to + * <code>true</code> on the adopted <code>Attr</code>. The + * descendants of the source <code>Attr</code> are recursively adopted. + * </dd> + * <dt>DOCUMENT_FRAGMENT_NODE</dt> + * <dd>The descendants of the source node are recursively adopted.</dd> + * <dt>DOCUMENT_NODE</dt> + * <dd><code>Document</code> nodes cannot be adopted.</dd> + * <dt>DOCUMENT_TYPE_NODE</dt> + * <dd><code>DocumentType</code> nodes cannot be adopted.</dd> + * <dt>ELEMENT_NODE</dt> + * <dd>Specified attribute nodes of the source element are adopted, and + * the generated <code>Attr</code> nodes. Default attributes are + * discarded, though if the document being adopted into defines default + * attributes for this element name, those are assigned. The descendants + * of the source element are recursively adopted.</dd> + * <dt>ENTITY_NODE</dt> + * <dd><code>Entity</code> nodes cannot be adopted.</dd> + * <dt>ENTITY_REFERENCE_NODE</dt> + * <dd>Only the <code>EntityReference</code> node itself is adopted, + * the descendants are discarded, since the source and destination + * documents might have defined the entity differently. If the document + * being imported into provides a definition for this entity name, its + * value is assigned.</dd> + * <dt>NOTATION_NODE</dt> + * <dd><code>Notation</code> nodes cannot be adopted.</dd> + * <dt>PROCESSING_INSTRUCTION_NODE, TEXT_NODE, CDATA_SECTION_NODE, + * COMMENT_NODE</dt> + * <dd>These nodes can all be adopted. No specifics.</dd> + * Should this method simply return null when it fails? How "exceptional" + * is failure for this method?Stick with raising exceptions only in + * exceptional circumstances, return null on failure (F2F 19 Jun 2000).Can + * an entity node really be adopted?No, neither can Notation nodes (Telcon + * 13 Dec 2000).Does this affect keys and hashCode's of the adopted + * subtree nodes?If so, what about readonly-ness of key and hashCode?if + * not, would appendChild affect keys/hashCodes or would it generate + * exceptions if key's are duplicate? Update: Hashcodes have been dropped. + * Given that the key is only unique within a document an adopted node + * needs to be given a new key, but what does it mean for the application? + * + * @param source + * The node to move into this document. + * @return The adopted node, or <code>null</code> if this operation + * fails, such as when the source node comes from a different + * implementation. + * @exception DOMException + * NOT_SUPPORTED_ERR: Raised if the source node is of type + * <code>DOCUMENT</code>,<code>DOCUMENT_TYPE</code>. + * <br> + * NO_MODIFICATION_ALLOWED_ERR: Raised when the source node + * is readonly. + * @since DOM Level 3 + */ + public org.w3c.dom.Node adoptNode(org.w3c.dom.Node source) throws org.w3c.dom.DOMException { + return null; + } + + /** + * @param tagName + */ + protected void checkTagNameValidity(String tagName) { + if (!isValidName(tagName)) { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, createDOMExceptionMessage(DOMException.INVALID_CHARACTER_ERR, tagName)); + } + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + * @param deep + * boolean + */ + public Node cloneNode(boolean deep) { + DocumentImpl cloned = new DocumentImpl(this); + if (deep) + cloned.importChildNodes(this, true); + return cloned; + } + + /** + * createAttribute method + * + * @return org.w3c.dom.Attr + * @param name + * java.lang.String + */ + public Attr createAttribute(String name) throws DOMException { + AttrImpl attr = new AttrImpl(); + attr.setOwnerDocument(this); + attr.setName(name); + return attr; + } + + /** + */ + public Attr createAttributeNS(String uri, String name) throws DOMException { + AttrImpl attr = new AttrImpl(); + attr.setOwnerDocument(this); + attr.setName(name); + attr.setNamespaceURI(uri); + return attr; + } + + /** + * createCDATASection method + * + * @return org.w3c.dom.CDATASection + * @param data + * java.lang.String + */ + public CDATASection createCDATASection(String data) throws DOMException { + // allow CDATA section + // if (!isXMLType()) { + // throw new DOMException(DOMException.NOT_SUPPORTED_ERR, new + // String()); + // } + CDATASectionImpl cdata = new CDATASectionImpl(); + cdata.setOwnerDocument(this); + if (data != null) + cdata.setData(data); + return cdata; + } + + /** + * createComment method + * + * @return org.w3c.dom.Comment + * @param data + * java.lang.String + */ + public Comment createComment(String data) { + CommentImpl comment = new CommentImpl(); + comment.setOwnerDocument(this); + if (data != null) + comment.setData(data); + return comment; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.model.xml.XMLDocument#createCommentElement(java.lang.String, + * boolean) + */ + public Element createCommentElement(String tagName, boolean isJSPTag) throws DOMException { + if (!isJSPType() && isJSPTag) { + throw new DOMException(DOMException.INVALID_MODIFICATION_ERR, new String()); + } + ElementImpl element = (ElementImpl) createElement(tagName); + element.setJSPTag(isJSPTag); + CommentElementRegistry registry = CommentElementRegistry.getInstance(); + if (registry.setupCommentElement(element)) { + return element; + } else { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, new String()); + } + } + + /** + * createDoctype method + * + * @return org.w3c.dom.DocumentType + * @param name + * java.lang.String + */ + public DocumentType createDoctype(String name) { + DocumentTypeImpl docType = new DocumentTypeImpl(); + docType.setOwnerDocument(this); + docType.setName(name); + return docType; + } + + /** + * createDocumentFragment method + * + * @return org.w3c.dom.DocumentFragment + */ + public DocumentFragment createDocumentFragment() { + DocumentFragmentImpl fragment = new DocumentFragmentImpl(); + fragment.setOwnerDocument(this); + return fragment; + } + + /** + * createElement method + * + * @return org.w3c.dom.Element + * @param tagName + * java.lang.String + */ + public Element createElement(String tagName) throws DOMException { + checkTagNameValidity(tagName); + + ElementImpl element = new ElementImpl(); + element.setOwnerDocument(this); + element.setTagName(tagName); + return element; + } + + /** + */ + public Element createElementNS(String uri, String tagName) throws DOMException { + if (!isValidName(tagName)) { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, new String()); + } + + ElementImpl element = new ElementImpl(); + element.setOwnerDocument(this); + element.setTagName(tagName); + element.setNamespaceURI(uri); + return element; + } + + /** + * createEntity method + * + * @return org.w3c.dom.Entity + * @param name + * java.lang.String + */ + public Entity createEntity(String name) { + EntityImpl entity = new EntityImpl(); + entity.setOwnerDocument(this); + entity.setName(name); + return entity; + } + + /** + * createEntityReference method + * + * @return org.w3c.dom.EntityReference + * @param name + * java.lang.String + */ + public EntityReference createEntityReference(String name) throws DOMException { + if (!isXMLType()) { + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, new String()); + } + + EntityReferenceImpl ref = new EntityReferenceImpl(); + ref.setOwnerDocument(this); + ref.setName(name); + return ref; + } + + /** + */ + public NodeIterator createNodeIterator(Node root, int whatToShow, NodeFilter filter, boolean entityReferenceExpansion) { + if (root == null) + root = this; + return new NodeIteratorImpl(root, whatToShow, filter); + } + + /** + * createNotation method + * + * @return org.w3c.dom.Notation + * @param name + * java.lang.String + */ + public Notation createNotation(String name) { + NotationImpl notation = new NotationImpl(); + notation.setOwnerDocument(this); + notation.setName(name); + return notation; + } + + /** + * createProcessingInstruction method + * + * @return org.w3c.dom.ProcessingInstruction + * @param target + * java.lang.String + * @param data + * java.lang.String + */ + public ProcessingInstruction createProcessingInstruction(String target, String data) throws DOMException { + ProcessingInstructionImpl pi = new ProcessingInstructionImpl(); + pi.setOwnerDocument(this); + pi.setTarget(target); + if (data != null) + pi.setData(data); + return pi; + } + + /** + */ + public Range createRange() { + return new RangeImpl(); + } + + /** + * createTextNode method + * + * @return org.w3c.dom.Text + * @param data + * java.lang.String + */ + public Text createTextNode(String data) { + TextImpl text = new TextImpl(); + text.setOwnerDocument(this); + text.setData(data); + return text; + } + + /** + */ + public TreeWalker createTreeWalker(Node root, int whatToShow, NodeFilter filter, boolean entityReferenceExpansion) { + // not suppoerted + return null; + } + + private DocumentType findDoctype(Node node) { + for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child.getNodeType() == DOCUMENT_TYPE_NODE && child instanceof DocumentType) { + return (DocumentType) child; + } else if (child.getNodeType() == ELEMENT_NODE && ((XMLElement) child).isCommentTag()) { + // search DOCTYPE inside of generic comment element + DocumentType docType = findDoctype(child); + if (docType != null) { + return docType; + } + } + } + + return null; + } + + private Element findDocumentElement(String docName, Node node, Node[] firstFound) { + for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child.getNodeType() != ELEMENT_NODE) + continue; + ElementImpl element = (ElementImpl) child; + if (element.isCommentTag()) { + Element docElement = findDocumentElement(docName, element, firstFound); + if (docElement != null) { + return docElement; + } else { + // added 'else continue' to better handle cases where + // there is "more than one root" element + // especially complicated by CommentElements, which are + // sometimes treated as elements, but should + // be treated as comments in this context. + continue; + } + } + // note: the "name" won't match in the event of a jsp tag ... but + // incase + // the name is null, we do not want the jsp element returned as + // documentElement + if (element.isJSPTag()) + continue; + if (docName == null) + return element; + // use local name for namespace + String localName = element.getLocalName(); + if (localName == null) + continue; + if (isXMLType()) { + if (localName.equals(docName)) + return element; + } else { + if (localName.equalsIgnoreCase(docName)) + return element; + } + if (firstFound[0] == null) + firstFound[0] = element; + } + return null; + } + + /** + * getCharValue method + * + * @return java.lang.String + * @param name + * java.lang.String + */ + protected String getCharValue(String name) { + if (name == null) + return null; + int length = name.length(); + if (length == 0) + return null; + + if (name.charAt(0) == '#') { // character reference + if (length == 1) + return null; + int radix = 10; + String s = null; + // now allow hexadecimal also for non XML document + if (name.charAt(1) == 'x') { // hexadecimal + radix = 16; + s = name.substring(2); + } else { // decimal + s = name.substring(1); + } + if (s == null || s.length() == 0) + return null; + if (s.charAt(0) == '-') + return null; // no minus accepted + char c = 0; + try { + c = (char) Integer.parseInt(s, radix); + } catch (NumberFormatException ex) { + } + if (c == 0) + return null; + return String.valueOf(c); + } + + // implicit character entities for XML + if (name.equals(XMLCharEntity.LT_NAME)) + return XMLCharEntity.LT_VALUE; + if (name.equals(XMLCharEntity.GT_NAME)) + return XMLCharEntity.GT_VALUE; + if (name.equals(XMLCharEntity.AMP_NAME)) + return XMLCharEntity.AMP_VALUE; + if (name.equals(XMLCharEntity.QUOT_NAME)) + return XMLCharEntity.QUOT_VALUE; + if (isXMLType()) { + if (name.equals(XMLCharEntity.APOS_NAME)) + return XMLCharEntity.APOS_VALUE; + } + + CMDocument cm = getCMDocument(); + if (cm != null) { + CMNamedNodeMap map = cm.getEntities(); + if (map != null) { + CMEntityDeclaration decl = (CMEntityDeclaration) map.getNamedItem(name); + if (decl != null) { + String value = decl.getValue(); + if (value == null) + return null; + int valueLength = value.length(); + if (valueLength > 1 && value.charAt(0) == '&' && value.charAt(1) == '#' && value.charAt(valueLength - 1) == ';') { + // character reference + return getCharValue(value.substring(1, valueLength - 1)); + } + return value; + } + } + } + + return null; + } + + /** + */ + protected CMDocument getCMDocument() { + ModelQuery modelQuery = ModelQueryUtil.getModelQuery(this); + if (modelQuery == null) + return null; + return modelQuery.getCorrespondingCMDocument(this); + } + + /** + * getDoctype method + * + * @return org.w3c.dom.DocumentType + */ + public DocumentType getDoctype() { + return findDoctype(this); + } + + /** + * getDocumentElement + * + * @return org.w3c.dom.Element From DOM 2 Spec: documentElement of type + * Element [p.62] , readonly This is a convenience [p.119] + * attribute that allows direct access to the child node that is + * the root element of the document. For HTML documents, this is + * the element with the tagName "HTML". Note: we differ from this + * definition a little in that we don't necessarily take the first + * child but also look to match the name. In a well formed + * document, of course, the result is the same, but not + * necessarily the same in an ill-formed document. + */ + public Element getDocumentElement() { + String name = null; + DocumentType docType = getDocumentType(); + if (docType != null) { + name = docType.getName(); + } + + Element first[] = new Element[1]; + Element docElement = findDocumentElement(name, this, first); + if (docElement == null) { + docElement = first[0]; + } + + return docElement; + } + + /** + */ + protected DocumentType getDocumentType() { + DocumentTypeAdapter adapter = getDocumentTypeAdapter(); + if (adapter == null) + return getDoctype(); + return adapter.getDocumentType(); + } + + /** + */ + protected DocumentTypeAdapter getDocumentTypeAdapter() { + if (this.documentTypeAdapter == null) { + this.documentTypeAdapter = (DocumentTypeAdapter) getAdapterFor(DocumentTypeAdapter.class); + if (this.documentTypeAdapter == null) { + // add default adapter + this.documentTypeAdapter = new DocumentTypeAdapterImpl(this); + addAdapter(this.documentTypeAdapter); + } + } + return this.documentTypeAdapter; + } + + /** + */ + public String getDocumentTypeId() { + DocumentType docType = getDocumentType(); + if (docType == null) + return null; + String id = docType.getPublicId(); + if (id == null) + id = docType.getSystemId(); + return id; + } + + /** + */ + public Element getElementById(String id) { + if (id == null) + return null; + NodeIterator it = createNodeIterator(this, NodeFilter.SHOW_ALL, null, false); + if (it == null) + return null; + + for (Node node = it.nextNode(); node != null; node = it.nextNode()) { + if (node.getNodeType() != ELEMENT_NODE) + continue; + ElementImpl element = (ElementImpl) node; + String value = element.getAttribute("id");//$NON-NLS-1$ + if (value != null && value.equals(id)) + return element; + } + + return null; + } + + /** + * getElementsByTagName method + * + * @return org.w3c.dom.NodeList + * @param tagName + * java.lang.String + */ + public NodeList getElementsByTagName(String tagName) { + if (tagName == null) + return new NodeListImpl(); + + NodeListImpl elements = null; + + if (usetagnamecache) { + elements = tagNameCache.getItem(tagName); + } + + if (elements == null) { + elements = internalGetElementsByTagName(tagName); + + } + + return elements; + } + + /** + */ + public NodeList getElementsByTagNameNS(String uri, String tagName) { + if (tagName == null) + return new NodeListImpl(); + + NodeIterator it = createNodeIterator(this, NodeFilter.SHOW_ALL, null, false); + if (it == null) + return new NodeListImpl(); + NodeListImpl elements = new NodeListImpl(); + + if (uri != null && uri.length() == 1 && uri.charAt(0) == '*') { + uri = null; // do not care + } + if (tagName.length() == 1 && tagName.charAt(0) == '*') { + tagName = null; // do not care + } + + for (Node node = it.nextNode(); node != null; node = it.nextNode()) { + if (node.getNodeType() != ELEMENT_NODE) + continue; + ElementImpl element = (ElementImpl) node; + if (tagName != null) { + String localName = element.getLocalName(); + if (localName == null || !localName.equals(tagName)) + continue; + } + if (uri != null) { + String nsURI = element.getNamespaceURI(); + if (nsURI == null || !nsURI.equals(uri)) + continue; + } + elements.appendNode(element); + } + + return elements; + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying, as part of the XML declaration, the encoding + * of this document. This is <code>null</code> when unspecified. + * + * @since DOM Level 3 + */ + public java.lang.String getEncoding() { + return null; + } + + /** + */ + public DOMImplementation getImplementation() { + return model; + } + + /** + * other nodes will be referring to this one to get the owning model + */ + public XMLModel getModel() { + return model; + } + + /** + * getNodeName method + * + * @return java.lang.String + */ + public String getNodeName() { + return "#document";//$NON-NLS-1$ + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return DOCUMENT_NODE; + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying, as part of the XML declaration, whether this + * document is standalone. + * + * @since DOM Level 3 + */ + public boolean getStandalone() { + return false; + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying whether errors checking is enforced or not. + * When set to <code>false</code>, the implementation is free to not + * test every possible error case normally defined on DOM operations, and + * not raise any <code>DOMException</code>. In case of error, the + * behavior is undefined. This attribute is <code>true</code> by + * defaults. + * + * @since DOM Level 3 + */ + public boolean getStrictErrorChecking() { + return false; + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying, as part of the XML declaration, the version + * number of this document. This is <code>null</code> when unspecified. + * + * @since DOM Level 3 + */ + public String getVersion() { + return null; + } + + /** + */ + protected boolean ignoreCase() { + DocumentTypeAdapter adapter = getDocumentTypeAdapter(); + if (adapter == null) + return false; + return (adapter.getTagNameCase() != DocumentTypeAdapter.STRICT_CASE); + } + + /** + */ + protected void importChildNodes(Node parent, boolean deep) { + if (parent == null) + return; + + removeChildNodes(); + + for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { + Node imported = importNode(child, deep); + if (imported == null) + continue; + appendChild(imported); + } + } + + /** + */ + public Node importNode(Node node, boolean deep) throws DOMException { + if (node == null) + return null; + NodeImpl imported = (NodeImpl) node.cloneNode(deep); + if (imported == null) + return null; + imported.setOwnerDocument(this, deep); + return imported; + } + + private NodeListImpl internalGetElementsByTagName(String tagName) { + //System.out.println("getElementsByTagname: " + tagName); + NodeIterator it = createNodeIterator(this, NodeFilter.SHOW_ALL, null, false); + if (it == null) + return new NodeListImpl(); + NodeListImpl elements = new NodeListImpl(); + + if (tagName.length() == 1 && tagName.charAt(0) == '*') { + tagName = null; // do not care + } + + for (Node node = it.nextNode(); node != null; node = it.nextNode()) { + if (node.getNodeType() != ELEMENT_NODE) + continue; + if (tagName != null) { + ElementImpl element = (ElementImpl) node; + if (!element.matchTagName(tagName)) + continue; + } + elements.appendNode(node); + } + if (usetagnamecache) { + tagNameCache.addItem(tagName, elements); + } + return elements; + } + + /** + */ + public boolean isJSPDocument() { + Element element = getDocumentElement(); + if (element == null) + return false; + String tagName = element.getTagName(); + if (tagName == null) + return false; + return tagName.equals(JSPTag.JSP_ROOT); + } + + /** + */ + public boolean isJSPType() { + if (this.model == null) + return false; + IModelHandler handler = this.model.getModelHandler(); + if (handler == null) + return false; + String id = handler.getAssociatedContentTypeId(); + if (id == null) + return false; + // TODO: -- avoid this semi hardcoded string + return id.equals(IContentTypeIdentifier.ContentTypeID_JSP); + } + + /** + */ + protected boolean isValidName(String name) { + if (name == null || name.length() == 0) + return false; + // // DMW: modified for XML4J 4.0.1 + // if (XMLChar.isValidName(name)) return true; + if (NameValidator.isValid(name)) + return true; + // special for invalid declaration + if (name.length() == 1 && name.charAt(0) == '!') + return true; + // special for JSP tag in tag name + if (name.startsWith(JSPTag.TAG_OPEN)) + return true; + return false; + } + + /** + */ + public boolean isXMLType() { + DocumentTypeAdapter adapter = getDocumentTypeAdapter(); + if (adapter == null) + return true; + return adapter.isXMLType(); + } + + /** + */ + protected void releaseDocumentType() { + if (this.documentTypeAdapter == null) + return; + this.documentTypeAdapter.release(); + this.documentTypeAdapter = null; + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying, as part of the XML declaration, the encoding + * of this document. This is <code>null</code> when unspecified. + * + * @since DOM Level 3 + */ + public void setEncoding(java.lang.String encoding) { + } + + /** + * setModel method + * + * @param model + * XMLModel + */ + + protected void setModel(XMLModel model) { + this.model = (XMLModelImpl) model; + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying, as part of the XML declaration, whether this + * document is standalone. + * + * @since DOM Level 3 + */ + public void setStandalone(boolean standalone) { + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying whether errors checking is enforced or not. + * When set to <code>false</code>, the implementation is free to not + * test every possible error case normally defined on DOM operations, and + * not raise any <code>DOMException</code>. In case of error, the + * behavior is undefined. This attribute is <code>true</code> by + * defaults. + * + * @since DOM Level 3 + */ + public void setStrictErrorChecking(boolean strictErrorChecking) { + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying, as part of the XML declaration, the version + * number of this document. This is <code>null</code> when unspecified. + * + * @since DOM Level 3 + */ + public void setVersion(java.lang.String version) { + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentTypeAdapterImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentTypeAdapterImpl.java new file mode 100644 index 0000000000..8d63d99b79 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentTypeAdapterImpl.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.sse.core.INodeNotifier; +import org.eclipse.wst.xml.core.document.DocumentTypeAdapter; +import org.eclipse.wst.xml.core.document.XMLDocument; +import org.eclipse.wst.xml.core.document.XMLModel; +import org.w3c.dom.DocumentType; + + +/** + */ +public class DocumentTypeAdapterImpl implements DocumentTypeAdapter { + + private XMLDocument document = null; + private DocumentType documentType = null; + + /** + */ + protected DocumentTypeAdapterImpl() { + super(); + } + + /** + */ + protected DocumentTypeAdapterImpl(XMLDocument document) { + this.document = document; + if (document != null) { + this.documentType = document.getDoctype(); + } + } + + /** + */ + public int getAttrNameCase() { + return STRICT_CASE; + } + + /** + */ + protected XMLDocument getDocument() { + return this.document; + } + + /** + */ + public DocumentType getDocumentType() { + return this.documentType; + } + + /** + */ + public int getTagNameCase() { + return STRICT_CASE; + } + + /** + */ + public boolean hasFeature(String feature) { + return false; + } + + /** + */ + public boolean isAdapterForType(Object type) { + return (type == DocumentTypeAdapter.class); + } + + /** + */ + public boolean isXMLType() { + return true; + } + + /** + */ + public void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) { + if (eventType != INodeNotifier.STRUCTURE_CHANGED) + return; + if (notifier == null || !(notifier instanceof XMLDocument)) + return; + this.documentType = ((XMLDocument) notifier).getDoctype(); + } + + /** + */ + protected void notifyDocumentTypeChanged() { + if (this.document == null) + return; + XMLModel model = this.document.getModel(); + if (model == null) + return; + ((XMLModelImpl) model).documentTypeChanged(); + } + + /** + */ + public void release() { + // nothing to do + } + + /** + */ + protected void setDocumentType(DocumentType documentType) { + this.documentType = documentType; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentTypeImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentTypeImpl.java new file mode 100644 index 0000000000..c8137da34b --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/DocumentTypeImpl.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.xml.core.document.XMLDocumentType; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; +import org.w3c.dom.DOMException; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + + +/** + * DocumentType class + */ +public class DocumentTypeImpl extends NodeImpl implements XMLDocumentType { + private String internalSubset = null; + + private String name = null; + private String publicId = null; + private String systemId = null; + + /** + * DocumentTypeImpl constructor + */ + protected DocumentTypeImpl() { + super(); + } + + /** + * DocumentTypeImpl constructor + * + * @param that + * DocumentTypeImpl + */ + protected DocumentTypeImpl(DocumentTypeImpl that) { + super(that); + + if (that != null) { + this.name = that.name; + } + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + * @param deep + * boolean + */ + public Node cloneNode(boolean deep) { + DocumentTypeImpl cloned = new DocumentTypeImpl(this); + return cloned; + } + + /** + * getEntities method + * + * @return org.w3c.dom.NamedNodeMap + */ + public NamedNodeMap getEntities() { + return null; + } + + /** + */ + public String getInternalSubset() { + return this.internalSubset; + } + + /** + * getName method + * + * @return java.lang.String + */ + public String getName() { + if (this.name == null) + return new String(); + return this.name; + } + + /** + * getNodeName + * + * @return java.lang.String + */ + public String getNodeName() { + return getName(); + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return DOCUMENT_TYPE_NODE; + } + + /** + * getNotations method + * + * @return org.w3c.dom.NamedNodeMap + */ + public NamedNodeMap getNotations() { + return null; + } + + /** + * getPublicId method + * + * @return java.lang.String + */ + public String getPublicId() { + return this.publicId; + } + + /** + * getSystemId method + * + * @return java.lang.String + */ + public String getSystemId() { + return this.systemId; + } + + /** + */ + public boolean isClosed() { + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null) + return true; // will be generated + String regionType = StructuredDocumentRegionUtil.getLastRegionType(flatNode); + return (regionType == XMLRegionContext.XML_DOCTYPE_DECLARATION_CLOSE || regionType == XMLRegionContext.XML_DECLARATION_CLOSE); + } + + /** + */ + public void setInternalSubset(String internalSubset) { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.internalSubset = internalSubset; + } + + /** + * setName method + * + * @param name + * java.lang.String + */ + protected void setName(String name) { + this.name = name; + } + + /** + * setPublicId method + * + * @param publicId + * java.lang.String + */ + public void setPublicId(String publicId) { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.publicId = publicId; + + notifyValueChanged(); + } + + /** + * setSystemId method + * + * @param systemId + * java.lang.String + */ + public void setSystemId(String systemId) { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.systemId = systemId; + + notifyValueChanged(); + } + + /** + * toString method + * + * @return java.lang.String + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(getName()); + buffer.append('('); + buffer.append(getPublicId()); + buffer.append(')'); + buffer.append('('); + buffer.append(getSystemId()); + buffer.append(')'); + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode != null) { + buffer.append('@'); + buffer.append(flatNode.toString()); + } + return buffer.toString(); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ElementImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ElementImpl.java new file mode 100644 index 0000000000..fd8d27a8ce --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ElementImpl.java @@ -0,0 +1,1421 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import java.util.Iterator; + +import org.eclipse.wst.common.contentmodel.CMElementDeclaration; +import org.eclipse.wst.common.contentmodel.modelquery.ModelQuery; +import org.eclipse.wst.sse.core.parser.RegionParser; +import org.eclipse.wst.sse.core.text.IStructuredDocument; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.commentelement.CommentElementAdapter; +import org.eclipse.wst.xml.core.document.JSPTag; +import org.eclipse.wst.xml.core.document.XMLElement; +import org.eclipse.wst.xml.core.document.XMLModel; +import org.eclipse.wst.xml.core.document.XMLNamespace; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.eclipse.wst.xml.core.internal.parser.XMLSourceParser; +import org.eclipse.wst.xml.core.jsp.model.parser.temp.XMLJSPRegionContexts; +import org.eclipse.wst.xml.core.modelquery.ModelQueryUtil; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.traversal.NodeFilter; +import org.w3c.dom.traversal.NodeIterator; + + +/** + * ElementImpl class + */ +public class ElementImpl extends NodeContainer implements XMLElement { + + private class Attributes implements NamedNodeMap { + Attributes() { + super(); + } + + public int getLength() { + if (attrNodes == null) + return 0; + return attrNodes.getLength(); + } + + public Node getNamedItem(String name) { + return getAttributeNode(name); + } + + public Node getNamedItemNS(String uri, String name) { + return getAttributeNodeNS(uri, name); + } + + public Node item(int index) { + if (attrNodes == null) + return null; + return attrNodes.item(index); + } + + public Node removeNamedItem(String name) throws DOMException { + return removeAttributeNode(name); + } + + public Node removeNamedItemNS(String uri, String name) throws DOMException { + return removeAttributeNodeNS(uri, name); + } + + public Node setNamedItem(Node arg) throws DOMException { + return setAttributeNode((AttrImpl) arg); + } + + public Node setNamedItemNS(Node arg) throws DOMException { + return setAttributeNodeNS((AttrImpl) arg); + } + } + + NodeListImpl attrNodes = null; + private IStructuredDocumentRegion endStructuredDocumentRegion = null; + private boolean isCommentTag = false; + private boolean isEmptyTag = false; + private boolean isJSPTag = false; + private String namespaceURI = null; + + private String tagName = null; + + /** + * ElementImpl constructor + */ + protected ElementImpl() { + super(); + } + + /** + * ElementImpl constructor + * + * @param that + * ElementImpl + */ + protected ElementImpl(ElementImpl that) { + super(that); + + if (that != null) { + this.tagName = that.tagName; + this.isEmptyTag = that.isEmptyTag; + this.isJSPTag = that.isJSPTag; + this.isCommentTag = that.isCommentTag; + + // clone attributes + that.cloneAttributes(this); + } + } + + /** + * addEndTag method + * + * @param end + * org.w3c.dom.Element + */ + protected void addEndTag(Element endTag) { + if (endTag == null) + return; + if (hasEndTag()) + return; + ElementImpl end = (ElementImpl) endTag; + + // move the end flat node from the end tag + IStructuredDocumentRegion flatNode = end.getEndStructuredDocumentRegion(); + if (flatNode == null) + return; + end.setEndStructuredDocumentRegion(null); + setEndStructuredDocumentRegion(flatNode); + } + + /** + * appendAttibuteNode method + * + * @return org.w3c.dom.Attr + * @param newAttr + * org.w3c.dom.Attr + */ + public Attr appendAttributeNode(Attr newAttr) { + if (newAttr == null) + return null; + AttrImpl attr = (AttrImpl) newAttr; + if (attr.getOwnerElement() != null) + return null; + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + if (this.attrNodes == null) + this.attrNodes = new NodeListImpl(); + this.attrNodes.appendNode(attr); + attr.setOwnerElement(this); + + notifyAttrReplaced(attr, null); + return attr; + } + + /** + * cloneAttributes method + * + * @param newOwner + * org.w3c.dom.Element + */ + protected void cloneAttributes(Element newOwner) { + if (newOwner == null || newOwner == this) + return; + + ElementImpl element = (ElementImpl) newOwner; + element.removeAttributes(); + + if (this.attrNodes == null) + return; + + int length = this.attrNodes.getLength(); + for (int i = 0; i < length; i++) { + Node node = this.attrNodes.item(i); + if (node == null) + continue; + Attr cloned = (Attr) node.cloneNode(false); + if (cloned != null) + element.appendAttributeNode(cloned); + } + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + * @param deep + * boolean + */ + public Node cloneNode(boolean deep) { + ElementImpl cloned = new ElementImpl(this); + if (deep) + cloneChildNodes(cloned, deep); + return cloned; + } + + /** + * getAttribute method + * + * @return java.lang.String + * @param name + * java.lang.String + */ + public String getAttribute(String name) { + Attr attr = getAttributeNode(name); + if (attr == null) + return null; + return attr.getValue(); + } + + /** + * getAttributeNode method + * + * @return org.w3c.dom.Attr + * @param name + * java.lang.String + */ + public Attr getAttributeNode(String name) { + if (name == null) + return null; // invalid parameter + if (this.attrNodes == null) + return null; // no attribute + + int length = this.attrNodes.getLength(); + for (int i = 0; i < length; i++) { + AttrImpl attr = (AttrImpl) this.attrNodes.item(i); + if (attr == null) + continue; + if (attr.matchName(name)) + return attr; // found + } + + return null; // not found + } + + /** + */ + public Attr getAttributeNodeNS(String uri, String name) { + if (name == null) + return null; // invalid parameter + if (this.attrNodes == null) + return null; // no attribute + + int length = this.attrNodes.getLength(); + for (int i = 0; i < length; i++) { + AttrImpl attr = (AttrImpl) this.attrNodes.item(i); + if (attr == null) + continue; + String localName = attr.getLocalName(); + if (localName == null || !localName.equals(name)) + continue; + String nsURI = attr.getNamespaceURI(); + if (uri == null) { + if (nsURI != null) + continue; + } else { + if (nsURI == null || !nsURI.equals(uri)) + continue; + } + + // found + return attr; + } + + return null; // not found + } + + /** + */ + public String getAttributeNS(String uri, String name) { + Attr attr = getAttributeNodeNS(uri, name); + if (attr == null) + return null; + return attr.getValue(); + } + + /** + * getAttributes method + * + * @return org.w3c.dom.NamedNodeMap + */ + public NamedNodeMap getAttributes() { + return new Attributes(); + } + + /** + */ + protected CMElementDeclaration getDeclaration() { + Document document = getOwnerDocument(); + if (document == null) + return null; + ModelQuery modelQuery = ModelQueryUtil.getModelQuery(document); + if (modelQuery == null) + return null; + return modelQuery.getCMElementDeclaration(this); + } + + /** + * getElementsByTagName method + * + * @return org.w3c.dom.NodeList + * @param tagName + * java.lang.String + */ + public NodeList getElementsByTagName(String tagName) { + if (tagName == null) + return new NodeListImpl(); + + DocumentImpl document = (DocumentImpl) getOwnerDocument(); + if (document == null) + return new NodeListImpl(); + NodeIterator it = document.createNodeIterator(this, NodeFilter.SHOW_ALL, null, false); + if (it == null) + return new NodeListImpl(); + NodeListImpl elements = new NodeListImpl(); + + if (tagName.length() == 1 && tagName.charAt(0) == '*') { + tagName = null; // do not care + } + + for (Node node = it.nextNode(); node != null; node = it.nextNode()) { + if (node.getNodeType() != ELEMENT_NODE) + continue; + if (tagName != null) { + ElementImpl element = (ElementImpl) node; + if (!element.matchTagName(tagName)) + continue; + } + elements.appendNode(node); + } + + return elements; + } + + /** + */ + public NodeList getElementsByTagNameNS(String uri, String tagName) { + if (tagName == null) + return new NodeListImpl(); + + DocumentImpl document = (DocumentImpl) getOwnerDocument(); + if (document == null) + return new NodeListImpl(); + NodeIterator it = document.createNodeIterator(this, NodeFilter.SHOW_ALL, null, false); + if (it == null) + return new NodeListImpl(); + NodeListImpl elements = new NodeListImpl(); + + if (uri != null && uri.length() == 1 && uri.charAt(0) == '*') { + uri = null; // do not care + } + if (tagName.length() == 1 && tagName.charAt(0) == '*') { + tagName = null; // do not care + } + + for (Node node = it.nextNode(); node != null; node = it.nextNode()) { + if (node.getNodeType() != ELEMENT_NODE) + continue; + ElementImpl element = (ElementImpl) node; + if (tagName != null) { + String localName = element.getLocalName(); + if (localName == null || !localName.equals(tagName)) + continue; + } + if (uri != null) { + String nsURI = element.getNamespaceURI(); + if (nsURI == null || !nsURI.equals(uri)) + continue; + } + elements.appendNode(element); + } + + return elements; + } + + /** + * getEndOffset method + * + * @return int + */ + public int getEndOffset() { + if (this.endStructuredDocumentRegion != null) + return this.endStructuredDocumentRegion.getEnd(); + return super.getEndOffset(); + } + + /** + * getEndStartOffset method + * + * @return int + */ + public int getEndStartOffset() { + if (this.endStructuredDocumentRegion != null) + return this.endStructuredDocumentRegion.getStart(); + return super.getEndOffset(); + } + + /** + * getEndStructuredDocumentRegion method + * + * @return com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + public IStructuredDocumentRegion getEndStructuredDocumentRegion() { + return this.endStructuredDocumentRegion; + } + + /** + */ + public String getEndTagName() { + if (this.endStructuredDocumentRegion == null) + return null; + + ITextRegionList regions = this.endStructuredDocumentRegion.getRegions(); + if (regions == null) + return null; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_TAG_NAME || regionType == XMLJSPRegionContexts.JSP_ROOT_TAG_NAME || regionType == XMLJSPRegionContexts.JSP_DIRECTIVE_NAME) { + return this.endStructuredDocumentRegion.getText(region); + } + } + + return null; + } + + /** + * getFirstStructuredDocumentRegion method + * + * @return com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + public IStructuredDocumentRegion getFirstStructuredDocumentRegion() { + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode != null) + return StructuredDocumentRegionUtil.getStructuredDocumentRegion(flatNode); + return StructuredDocumentRegionUtil.getStructuredDocumentRegion(this.endStructuredDocumentRegion); + } + + /** + * getLastStructuredDocumentRegion method + * + * @return com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + public IStructuredDocumentRegion getLastStructuredDocumentRegion() { + if (this.endStructuredDocumentRegion != null) + return StructuredDocumentRegionUtil.getStructuredDocumentRegion(this.endStructuredDocumentRegion); + return StructuredDocumentRegionUtil.getStructuredDocumentRegion(getStructuredDocumentRegion()); + } + + /** + */ + public String getLocalName() { + if (this.tagName == null) + return null; + int index = this.tagName.indexOf(':'); + if (index < 0) + return this.tagName; + return this.tagName.substring(index + 1); + } + + /** + */ + public String getNamespaceURI() { + String nsAttrName = null; + String prefix = getPrefix(); + if (prefix != null && prefix.length() > 0) { + nsAttrName = XMLNamespace.XMLNS_PREFIX + prefix; + } else { + nsAttrName = XMLNamespace.XMLNS; + } + + for (Node node = this; node != null; node = node.getParentNode()) { + if (node.getNodeType() != ELEMENT_NODE) + break; + Element element = (Element) node; + Attr attr = element.getAttributeNode(nsAttrName); + if (attr != null) + return attr.getValue(); + } + + return this.namespaceURI; + } + + /** + * getNodeName method + * + * @return java.lang.String + */ + public String getNodeName() { + return getTagName(); + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return ELEMENT_NODE; + } + + /** + */ + public String getPrefix() { + if (this.tagName == null) + return null; + int index = this.tagName.indexOf(':'); + if (index <= 0) + return null; + // exclude JSP tag in tag name + if (this.tagName.charAt(0) == '<') + return null; + return this.tagName.substring(0, index); + } + + /** + * getStartEndOffset method + * + * @return int + */ + public int getStartEndOffset() { + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode != null) + return flatNode.getEnd(); + return super.getStartOffset(); + } + + /** + * getStartOffset method + * + * @return int + */ + public int getStartOffset() { + if (getStartStructuredDocumentRegion() == null && this.endStructuredDocumentRegion != null && !hasChildNodes()) { + return this.endStructuredDocumentRegion.getStart(); + } + return super.getStartOffset(); + } + + /** + * getStartStructuredDocumentRegion method + * + * @return com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + public IStructuredDocumentRegion getStartStructuredDocumentRegion() { + return getStructuredDocumentRegion(); + } + + /** + * getTagName method + * + * @return java.lang.String + */ + public String getTagName() { + if (this.tagName == null) + return new String(); + return this.tagName; + } + + /** + */ + public boolean hasAttribute(String name) { + return (getAttributeNode(name) != null); + } + + /** + */ + public boolean hasAttributeNS(String uri, String name) { + return (getAttributeNodeNS(uri, name) != null); + } + + /** + */ + public boolean hasAttributes() { + return (this.attrNodes != null && this.attrNodes.getLength() > 0); + } + + /** + * hasEndTag method + * + * @return boolean + */ + public boolean hasEndTag() { + return (this.endStructuredDocumentRegion != null); + } + + /** + */ + protected final boolean hasPrefix() { + if (this.tagName == null) + return false; + if (this.tagName.indexOf(':') <= 0) + return false; + // exclude JSP tag in tag name + if (this.tagName.charAt(0) == '<') + return false; + return true; + } + + /** + * hasStartTag method + * + * @return boolean + */ + public boolean hasStartTag() { + return (getStructuredDocumentRegion() != null); + } + + /** + */ + protected final boolean ignoreCase() { + DocumentImpl document = (DocumentImpl) getOwnerDocument(); + if (document != null && document.ignoreCase()) { + // even in case insensitive document, if having prefix, it's case + // sensitive tag + return !hasPrefix(); + } + return false; + } + + /** + */ + protected Attr insertAttributeNode(Attr newAttr, int index) { + if (newAttr == null) + return null; + AttrImpl attr = (AttrImpl) newAttr; + if (attr.getOwnerElement() != null) + return null; + + if (this.attrNodes == null) + this.attrNodes = new NodeListImpl(); + this.attrNodes.insertNode(attr, index); + attr.setOwnerElement(this); + + notifyAttrReplaced(attr, null); + return attr; + } + + /** + * insertBefore method + * + * @return org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + * @param refChild + * org.w3c.dom.Node + */ + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + // should throw DOMException instead of return null? + if (newChild == null) + return null; + if (!isContainer()) { // never be container + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, new String()); + } + if (newChild.getNodeType() != TEXT_NODE) { + if (isJSPContainer() || isCDATAContainer()) { // accepts only Text + // child + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, new String()); + } + } + return super.insertBefore(newChild, refChild); + } + + /** + */ + protected boolean isCDATAContainer() { + // use BlockMaker instead of CMElementDeclaration + // because <style> and <script> in XHTML is not CDATA content type + XMLModel model = getModel(); + if (model == null) + return false; // error + IStructuredDocument structuredDocument = model.getStructuredDocument(); + if (structuredDocument == null) + return false; // eror + RegionParser parser = structuredDocument.getParser(); + if (parser == null || !(parser instanceof XMLSourceParser)) + return false; + return (((XMLSourceParser) parser).getBlockMarker(this.tagName) != null); + /* + * CMElementDeclaration decl = getDeclaration(); if (decl == null) + * return false; if (decl instanceof CMNodeWrapper) { decl = + * (CMElementDeclaration)((CMNodeWrapper)decl).getOriginNode(); if + * (decl == null) return false; } if (decl instanceof + * TLDElementDeclaration) { String content = + * ((TLDElementDeclaration)decl).getBodycontent(); if (content == + * null) return false; return + * content.equals(JSP11TLDNames.CONTENT_TAGDEPENDENT); } if + * (!isGlobalTag()) return false; return (decl.getContentType() == + * CMElementDeclaration.CDATA); + */ + } + + /** + */ + public boolean isClosed() { + IStructuredDocumentRegion flatNode = null; + if (isEmptyTag() || !isContainer()) { + flatNode = getStructuredDocumentRegion(); + if (flatNode == null) + return true; // will be generated + } else { + flatNode = getEndStructuredDocumentRegion(); + if (flatNode == null) + return false; // must be generated + } + String regionType = StructuredDocumentRegionUtil.getLastRegionType(flatNode); + if (isCommentTag()) { + return (regionType == XMLJSPRegionContexts.JSP_COMMENT_CLOSE || regionType == XMLRegionContext.XML_COMMENT_CLOSE); + } + if (isJSPTag()) { + return (regionType == XMLJSPRegionContexts.JSP_CLOSE || regionType == XMLJSPRegionContexts.JSP_DIRECTIVE_CLOSE); + } + return (regionType == XMLRegionContext.XML_TAG_CLOSE || regionType == XMLRegionContext.XML_EMPTY_TAG_CLOSE || regionType == XMLRegionContext.XML_DECLARATION_CLOSE); + } + + /** + */ + public final boolean isCommentTag() { + return this.isCommentTag; + } + + /** + * isContainer method + * + * @return boolean + */ + public boolean isContainer() { + if (isCommentTag()) { + CommentElementAdapter adapter = (CommentElementAdapter) getAdapterFor(CommentElementAdapter.class); + if (adapter != null) { + return (adapter.isContainer()); + } + return (getDeclaration() == null); + } + if (isJSPTag()) { + // exclude JSP directive + return (matchTagName(JSPTag.JSP_SCRIPTLET) || matchTagName(JSPTag.JSP_DECLARATION) || matchTagName(JSPTag.JSP_EXPRESSION)); + } + if (!isXMLTag()) { // non-XML tag + CMElementDeclaration decl = getDeclaration(); + if (decl == null) + return false; // undefined tag + return (decl.getContentType() != CMElementDeclaration.EMPTY); + } + return true; + } + + /** + * isEmptyTag method + * + * @return boolean + */ + public boolean isEmptyTag() { + if (isJSPTag()) + return false; + if (isCommentTag()) + return false; + if (!isXMLTag()) + return false; + return this.isEmptyTag; + } + + /** + */ + public boolean isEndTag() { + return (hasEndTag() && !hasStartTag() && !hasChildNodes()); + } + + /** + */ + public boolean isGlobalTag() { + return !hasPrefix(); + } + + /** + */ + public boolean isImplicitTag() { + if (hasStartTag() || hasEndTag()) + return false; + // make sure this is in the document tree + // because if not in the document tree, no tags are generated yet + return (getContainerDocument() != null); + } + + /** + */ + public boolean isJSPContainer() { + return (isJSPTag() && !isCommentTag() && isContainer()); + } + + /** + * isJSPTag method + * + * @return boolean + */ + public final boolean isJSPTag() { + return this.isJSPTag; + } + + /** + */ + public boolean isStartTagClosed() { + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null) + return true; // will be generated + String regionType = StructuredDocumentRegionUtil.getLastRegionType(flatNode); + if (isCommentTag()) { + return (regionType == XMLJSPRegionContexts.JSP_COMMENT_CLOSE || regionType == XMLRegionContext.XML_COMMENT_CLOSE); + } + if (isJSPTag()) { + if (isContainer()) + return true; // start tag always has a single region + return (regionType == XMLJSPRegionContexts.JSP_DIRECTIVE_CLOSE); + } + return (regionType == XMLRegionContext.XML_TAG_CLOSE || regionType == XMLRegionContext.XML_EMPTY_TAG_CLOSE || regionType == XMLRegionContext.XML_DECLARATION_CLOSE); + } + + /** + */ + public final boolean isXMLTag() { + if (isJSPTag()) + return false; + if (isCommentTag()) + return false; + DocumentImpl document = (DocumentImpl) getOwnerDocument(); + if (document != null && !document.isXMLType()) { + // even in non-XML document, if having prefix, it's XML tag + return hasPrefix(); + } + return true; + } + + /** + */ + protected boolean matchEndTag(Element element) { + if (element == null) + return false; + ElementImpl impl = (ElementImpl) element; + if (isJSPTag() && !isCommentTag()) { + return (impl.isJSPTag() && !impl.isCommentTag()); + } + return matchTagName(element.getTagName()); + } + + /** + * matchTagName method + * + * @return boolean + * @param tagName + * java.lang.String + */ + public boolean matchTagName(String tagName) { + if (tagName == null) + return (this.tagName == null); + if (this.tagName == null) + return false; + if (!ignoreCase()) + return this.tagName.equals(tagName); + return this.tagName.equalsIgnoreCase(tagName); + } + + /** + * notifyAttrReplaced method + * + * @param newAttr + * org.w3c.dom.Attr + * @param oldAttr + * org.w3c.dom.Attr + */ + protected void notifyAttrReplaced(Attr newAttr, Attr oldAttr) { + DocumentImpl document = (DocumentImpl) getContainerDocument(); + if (document == null) + return; + XMLModelImpl model = (XMLModelImpl) document.getModel(); + if (model == null) + return; + model.attrReplaced(this, newAttr, oldAttr); + } + + /** + * notifyValueChanged method + */ + public void notifyEndTagChanged() { + DocumentImpl document = (DocumentImpl) getContainerDocument(); + if (document == null) + return; + XMLModelImpl model = (XMLModelImpl) document.getModel(); + if (model == null) + return; + model.endTagChanged(this); + } + + /** + */ + public void notifyStartTagChanged() { + DocumentImpl document = (DocumentImpl) getContainerDocument(); + if (document == null) + return; + XMLModelImpl model = (XMLModelImpl) document.getModel(); + if (model == null) + return; + model.startTagChanged(this); + } + + /** + */ + public boolean preferEmptyTag() { + if (hasChildNodes()) + return false; + if (isJSPTag()) + return false; + if (isCommentTag()) + return false; + if (!isXMLTag()) + return false; + CMElementDeclaration decl = getDeclaration(); + if (decl == null) + return false; + return (decl.getContentType() == CMElementDeclaration.EMPTY); + } + + /** + * removeAttribute method + * + * @param name + * java.lang.String + */ + public void removeAttribute(String name) throws DOMException { + removeAttributeNode(name); + } + + /** + * removeAttributeNode method + * + * @return org.w3c.dom.Attr + * @param oldAttr + * org.w3c.dom.Attr + */ + public Attr removeAttributeNode(Attr oldAttr) throws DOMException { + if (oldAttr == null) + return null; // invalid parameter + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + if (this.attrNodes == null) { // no attribute + throw new DOMException(DOMException.NOT_FOUND_ERR, new String()); + } + + int length = this.attrNodes.getLength(); + for (int i = 0; i < length; i++) { + AttrImpl attr = (AttrImpl) this.attrNodes.item(i); + if (attr != oldAttr) + continue; + + // found + this.attrNodes.removeNode(i); + attr.setOwnerElement(null); + + notifyAttrReplaced(null, attr); + return attr; + } + + // not found + throw new DOMException(DOMException.NOT_FOUND_ERR, new String()); + } + + /** + * removeAttributeNode method + * + * @return org.w3c.dom.Attr + * @param name + * java.lang.String + */ + public Attr removeAttributeNode(String name) { + if (name == null) + return null; // invalid parameter + if (this.attrNodes == null) + return null; // no attribute + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + int length = this.attrNodes.getLength(); + for (int i = 0; i < length; i++) { + AttrImpl attr = (AttrImpl) this.attrNodes.item(i); + if (attr == null) + continue; + if (!attr.matchName(name)) + continue; + + // found + this.attrNodes.removeNode(i); + attr.setOwnerElement(null); + + notifyAttrReplaced(null, attr); + return attr; + } + + return null; // not found + } + + /** + */ + public Attr removeAttributeNodeNS(String uri, String name) { + if (name == null) + return null; // invalid parameter + if (this.attrNodes == null) + return null; // no attribute + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + int length = this.attrNodes.getLength(); + for (int i = 0; i < length; i++) { + AttrImpl attr = (AttrImpl) this.attrNodes.item(i); + if (attr == null) + continue; + String localName = attr.getLocalName(); + if (localName == null || !localName.equals(name)) + continue; + String nsURI = attr.getNamespaceURI(); + if (uri == null) { + if (nsURI != null) + continue; + } else { + if (nsURI == null || !nsURI.equals(uri)) + continue; + } + + // found + this.attrNodes.removeNode(i); + attr.setOwnerElement(null); + + notifyAttrReplaced(null, attr); + return attr; + } + + return null; // not found + } + + /** + */ + public void removeAttributeNS(String uri, String name) throws DOMException { + removeAttributeNodeNS(uri, name); + } + + /** + * removeAttributes method + */ + public void removeAttributes() { + if (this.attrNodes == null) + return; + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + int length = this.attrNodes.getLength(); + for (int i = 0; i < length; i++) { + AttrImpl attr = (AttrImpl) this.attrNodes.item(i); + if (attr != null) { + attr.setOwnerElement(null); + notifyAttrReplaced(null, attr); + } + } + + this.attrNodes = null; + } + + /** + * removeEndTag method + * + * @return org.w3c.dom.Element + */ + protected Element removeEndTag() { + if (!hasEndTag()) + return null; + NodeListImpl attrNodes = this.attrNodes; + this.attrNodes = null; // not to copy attributes + ElementImpl end = (ElementImpl) cloneNode(false); + this.attrNodes = attrNodes; + if (end == null) + return null; + + // move the end flat node to the end tag + IStructuredDocumentRegion flatNode = getEndStructuredDocumentRegion(); + if (flatNode == null) + return null; + setEndStructuredDocumentRegion(null); + end.setEndStructuredDocumentRegion(flatNode); + return end; + } + + /** + */ + protected void removeStartTag() { + removeAttributes(); + } + + /** + * Resets attribute values from IStructuredDocumentRegion. + */ + void resetStructuredDocumentRegions() { + if (this.attrNodes != null) { + int length = this.attrNodes.getLength(); + for (int i = 0; i < length; i++) { + AttrImpl attr = (AttrImpl) this.attrNodes.item(i); + if (attr == null) + continue; + attr.resetRegions(); + } + } + + super.resetStructuredDocumentRegions(); // for children + + this.endStructuredDocumentRegion = null; + } + + /** + * setAttribute method + * + * @param name + * java.lang.String + * @param value + * java.lang.String + */ + public void setAttribute(String name, String value) throws DOMException { + if (name == null) + return; + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + Attr attr = getAttributeNode(name); + if (attr != null) { + attr.setValue(value); // change value + return; + } + + // new attribute + Document doc = getOwnerDocument(); + if (doc == null) + return; + attr = doc.createAttribute(name); + if (attr == null) + return; + attr.setValue(value); + appendAttributeNode(attr); + } + + /** + * setAttributeNode method + * + * @return org.w3c.dom.Attr + * @param newAttr + * org.w3c.dom.Attr + */ + public Attr setAttributeNode(Attr newAttr) throws DOMException { + if (newAttr == null) + return null; // nothing to do + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + AttrImpl attr = (AttrImpl) newAttr; + Element owner = attr.getOwnerElement(); + if (owner != null) { + if (owner == this) + return null; // nothing to do + throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, new String()); + } + + Attr oldAttr = removeAttributeNode(newAttr.getName()); + appendAttributeNode(attr); + return oldAttr; + } + + /** + */ + public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { + if (newAttr == null) + return null; // nothing to do + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + AttrImpl attr = (AttrImpl) newAttr; + Element owner = attr.getOwnerElement(); + if (owner != null) { + if (owner == this) + return null; // nothing to do + throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR, new String()); + } + + String name = newAttr.getLocalName(); + String uri = newAttr.getNamespaceURI(); + Attr oldAttr = removeAttributeNodeNS(uri, name); + appendAttributeNode(attr); + return oldAttr; + } + + /** + */ + public void setAttributeNS(String uri, String name, String value) throws DOMException { + if (name == null) + return; + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + Attr attr = getAttributeNodeNS(uri, name); + if (attr != null) { + attr.setValue(value); // change value + return; + } + + // new attribute + Document doc = getOwnerDocument(); + if (doc == null) + return; + attr = doc.createAttributeNS(uri, name); + if (attr == null) + return; + attr.setValue(value); + appendAttributeNode(attr); + } + + /** + */ + public void setCommentTag(boolean isCommentTag) { + XMLNode parent = (XMLNode) getParentNode(); + if (parent != null && !parent.isChildEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.isCommentTag = isCommentTag; + } + + /** + * setEmptyTag method + * + * @param isEmptyTag + * boolean + */ + public void setEmptyTag(boolean isEmptyTag) { + XMLNode parent = (XMLNode) getParentNode(); + if (parent != null && !parent.isChildEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.isEmptyTag = isEmptyTag; + } + + /** + * setEndStructuredDocumentRegion method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + void setEndStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + this.endStructuredDocumentRegion = flatNode; + + NodeContainer parent = (NodeContainer) getParentNode(); + if (parent != null) { + parent.syncChildEditableState(this); + } + } + + /** + * setJSPTag method + * + * @param isJSPTag + * boolean + */ + public void setJSPTag(boolean isJSPTag) { + XMLNode parent = (XMLNode) getParentNode(); + if (parent != null && !parent.isChildEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.isJSPTag = isJSPTag; + } + + /** + */ + protected void setNamespaceURI(String namespaceURI) { + this.namespaceURI = namespaceURI; + } + + /** + */ + protected void setOwnerDocument(Document ownerDocument, boolean deep) { + super.setOwnerDocument(ownerDocument, deep); + + if (this.attrNodes == null) + return; + + int length = this.attrNodes.getLength(); + for (int i = 0; i < length; i++) { + AttrImpl attr = (AttrImpl) this.attrNodes.item(i); + if (attr == null) + continue; + attr.setOwnerDocument(ownerDocument); + } + } + + /** + */ + public void setPrefix(String prefix) throws DOMException { + XMLNode parent = (XMLNode) getParentNode(); + if (parent != null && !parent.isChildEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + int prefixLength = (prefix != null ? prefix.length() : 0); + String localName = getLocalName(); + if (prefixLength == 0) { + if (localName == null || localName.length() == 0) { + // invalid local name + return; + } + setTagName(localName); + } else { + int localLength = (localName != null ? localName.length() : 0); + StringBuffer buffer = new StringBuffer(prefixLength + 1 + localLength); + buffer.append(prefix); + buffer.append(':'); + if (localName != null) + buffer.append(localName); + setTagName(buffer.toString()); + } + + boolean changeEndTag = hasEndTag(); + notifyStartTagChanged(); + if (changeEndTag) + notifyEndTagChanged(); + } + + /** + * setStartStructuredDocumentRegion method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + void setStartStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + setStructuredDocumentRegion(flatNode); + } + + /** + * setTagName method + * + * @param tagName + * java.lang.String + */ + protected void setTagName(String tagName) { + this.tagName = tagName; + } + + /** + * toString method + * + * @return java.lang.String + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + String tagName = getTagName(); + if (hasStartTag()) + buffer.append(tagName); + if (isEmptyTag()) + buffer.append('/'); + if (hasEndTag()) { + buffer.append('/'); + buffer.append(tagName); + } + if (buffer.length() == 0) + buffer.append(tagName); + + IStructuredDocumentRegion startStructuredDocumentRegion = getStartStructuredDocumentRegion(); + if (startStructuredDocumentRegion != null) { + buffer.append('@'); + buffer.append(startStructuredDocumentRegion.toString()); + } + IStructuredDocumentRegion endStructuredDocumentRegion = getEndStructuredDocumentRegion(); + if (endStructuredDocumentRegion != null) { + buffer.append('@'); + buffer.append(endStructuredDocumentRegion.toString()); + } + return buffer.toString(); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/EntityImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/EntityImpl.java new file mode 100644 index 0000000000..80e5d6fdbd --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/EntityImpl.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.w3c.dom.DOMException; +import org.w3c.dom.Entity; +import org.w3c.dom.Node; + +/** + * EntityImpl class + */ +public class EntityImpl extends NodeImpl implements Entity { + + private String name = null; + private String notationName = null; + private String publicId = null; + private String systemId = null; + + /** + * EntityImpl constructor + */ + protected EntityImpl() { + super(); + } + + /** + * EntityImpl constructor + * + * @param that + * EntityImpl + */ + protected EntityImpl(EntityImpl that) { + super(that); + + if (that != null) { + this.name = that.name; + this.publicId = that.publicId; + this.systemId = that.systemId; + this.notationName = that.notationName; + } + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + * @param deep + * boolean + */ + public Node cloneNode(boolean deep) { + EntityImpl cloned = new EntityImpl(this); + return cloned; + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying, as part of the text declaration, the encoding + * of this entity, when it is an external parsed entity. This is + * <code>null</code> otherwise. + * + * @since DOM Level 3 + */ + public java.lang.String getEncoding() { + return null; + } + + /** + * getNodeName method + * + * @return java.lang.String + */ + public String getNodeName() { + if (this.name == null) + return new String(); + return this.name; + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return ENTITY_NODE; + } + + /** + * getNotationName method + * + * @return java.lang.String + */ + public String getNotationName() { + return this.notationName; + } + + /** + * getPublicId method + * + * @return java.lang.String + */ + public String getPublicId() { + return this.publicId; + } + + /** + * getSystemId method + * + * @return java.lang.String + */ + public String getSystemId() { + return this.systemId; + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying, as part of the text declaration, the version + * number of this entity, when it is an external parsed entity. This is + * <code>null</code> otherwise. + * + * @since DOM Level 3 + */ + public java.lang.String getVersion() { + return null; + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying, as part of the text declaration, the encoding + * of this entity, when it is an external parsed entity. This is + * <code>null</code> otherwise. + * + * @since DOM Level 3 + */ + public void setEncoding(java.lang.String encoding) { + } + + /** + * setName method + * + * @param name + * java.lang.String + */ + protected void setName(String name) { + this.name = name; + } + + /** + * setNotationName method + * + * @param notationName + * java.lang.String + */ + public void setNotationName(String notationName) { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.notationName = notationName; + } + + /** + * setPublicId method + * + * @param publicId + * java.lang.String + */ + public void setPublicId(String publicId) { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.publicId = publicId; + } + + /** + * setSystemId method + * + * @param systemId + * java.lang.String + */ + public void setSystemId(String systemId) { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.systemId = systemId; + } + + /** + * <p> + * EXPERIMENTAL! Based on the <a + * href='http://www.w3.org/TR/2001/WD-DOM-Level-3-Core-20010605'>Document + * Object Model (DOM) Level 3 Core Working Draft of 5 June 2001. </a>. + * <p> + * An attribute specifying, as part of the text declaration, the version + * number of this entity, when it is an external parsed entity. This is + * <code>null</code> otherwise. + * + * @since DOM Level 3 + */ + public void setVersion(java.lang.String version) { + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/EntityReferenceImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/EntityReferenceImpl.java new file mode 100644 index 0000000000..366ecc0dbd --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/EntityReferenceImpl.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.w3c.dom.EntityReference; +import org.w3c.dom.Node; + +/** + * EntityReference class + */ +public class EntityReferenceImpl extends NodeImpl implements EntityReference { + + private String name = null; + + /** + * EntityReferenceImpl constructor + */ + protected EntityReferenceImpl() { + super(); + } + + /** + * EntityReferenceImpl constructor + * + * @param that + * EntityReferenceImpl + */ + protected EntityReferenceImpl(EntityReferenceImpl that) { + super(that); + + if (that != null) { + this.name = that.name; + } + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + * @param deep + * boolean + */ + public Node cloneNode(boolean deep) { + EntityReferenceImpl cloned = new EntityReferenceImpl(this); + return cloned; + } + + /** + * getNodeName method + * + * @return java.lang.String + */ + public String getNodeName() { + if (this.name == null) + return new String(); + return this.name; + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return ENTITY_REFERENCE_NODE; + } + + /** + * setName method + * + * @param name + * java.lang.String + */ + protected void setName(String name) { + this.name = name; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ModelParserAdapter.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ModelParserAdapter.java new file mode 100644 index 0000000000..011079bc31 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ModelParserAdapter.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.sse.core.INodeAdapter; +import org.eclipse.wst.xml.core.document.XMLElement; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + + +/** + */ +public interface ModelParserAdapter extends INodeAdapter { + + /** + */ + public boolean canBeImplicitTag(Element element); + + /** + */ + public boolean canBeImplicitTag(Element element, Node child); + + /** + */ + public boolean canContain(Element element, Node child); + + /** + */ + public Element createCommentElement(Document document, String data, boolean isJSPTag); + + /** + */ + public Element createImplicitElement(Document document, Node parent, Node child); + + /** + */ + public String getFindRootName(String tagName); + + /** + */ + public boolean isEndTag(XMLElement element); +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeContainer.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeContainer.java new file mode 100644 index 0000000000..121d57cb17 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeContainer.java @@ -0,0 +1,514 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +/** + * NodeContainer class + */ +public abstract class NodeContainer extends NodeImpl implements Node, NodeList { + + /** + */ + private class ChildNodesCache implements NodeList { + private Node curChild = null; + private int curIndex = -1; + private int length = 0; + + ChildNodesCache() { + initializeCache(); + } + + public int getLength() { + // atomic + return this.length; + } + + private void initializeCache() { + // note we use the outter objects lockobject + // (since we are using their "children". + synchronized (lockObject) { + for (Node child = firstChild; child != null; child = child.getNextSibling()) { + this.length++; + } + } + } + + public Node item(int index) { + synchronized (lockObject) { + if (this.length == 0) + return null; + if (index < 0) + return null; + if (index >= this.length) + return null; + + if (this.curIndex < 0) { // first time + if (index * 2 >= this.length) { // search from the last + this.curIndex = this.length - 1; + this.curChild = lastChild; + } else { // search from the first + this.curIndex = 0; + this.curChild = firstChild; + } + } + + if (index == this.curIndex) + return this.curChild; + + if (index > this.curIndex) { + while (index > this.curIndex) { + this.curIndex++; + this.curChild = this.curChild.getNextSibling(); + } + } else { // index < this.curIndex + while (index < this.curIndex) { + this.curIndex--; + this.curChild = this.curChild.getPreviousSibling(); + } + } + + return this.curChild; + } + } + } + + private NodeList childNodesCache = null; + + private boolean fChildEditable = true; + NodeImpl firstChild = null; + NodeImpl lastChild = null; + + Object lockObject = new byte[0]; + + /** + * NodeContainer constructor + */ + protected NodeContainer() { + super(); + } + + /** + * NodeContainer constructor + * + * @param that + * NodeContainer + */ + protected NodeContainer(NodeContainer that) { + super(that); + } + + /** + * appendChild method + * + * @return org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + */ + public Node appendChild(Node newChild) throws DOMException { + return insertBefore(newChild, null); + } + + /** + * cloneChildNodes method + * + * @param container + * org.w3c.dom.Node + * @param deep + * boolean + */ + protected void cloneChildNodes(Node newParent, boolean deep) { + if (newParent == null || newParent == this) + return; + if (!(newParent instanceof NodeContainer)) + return; + + NodeContainer container = (NodeContainer) newParent; + container.removeChildNodes(); + + for (Node child = getFirstChild(); child != null; child = child.getNextSibling()) { + Node cloned = child.cloneNode(deep); + if (cloned != null) + container.appendChild(cloned); + } + } + + /** + * getChildNodes method + * + * @return org.w3c.dom.NodeList + */ + public NodeList getChildNodes() { + return this; + } + + /** + * getFirstChild method + * + * @return org.w3c.dom.Node + */ + public Node getFirstChild() { + return this.firstChild; + } + + /** + * getLastChild method + * + * @return org.w3c.dom.Node + */ + public Node getLastChild() { + return this.lastChild; + } + + /** + * getLength method + * + * @return int + */ + public int getLength() { + if (this.firstChild == null) + return 0; + synchronized (lockObject) { + if (this.childNodesCache == null) + this.childNodesCache = new ChildNodesCache(); + return this.childNodesCache.getLength(); + } + } + + /** + */ + public String getSource() { + StringBuffer buffer = new StringBuffer(); + + IStructuredDocumentRegion startStructuredDocumentRegion = getStartStructuredDocumentRegion(); + if (startStructuredDocumentRegion != null) { + String source = startStructuredDocumentRegion.getText(); + if (source != null) + buffer.append(source); + } + + for (NodeImpl child = firstChild; child != null; child = (NodeImpl) child.getNextSibling()) { + String source = child.getSource(); + if (source != null) + buffer.append(source); + } + + IStructuredDocumentRegion endStructuredDocumentRegion = getEndStructuredDocumentRegion(); + if (endStructuredDocumentRegion != null) { + String source = endStructuredDocumentRegion.getText(); + if (source != null) + buffer.append(source); + } + + return buffer.toString(); + } + + /** + * hasChildNodes method + * + * @return boolean + */ + public boolean hasChildNodes() { + return (this.firstChild != null); + } + + /** + * insertBefore method + * + * @return org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + * @param refChild + * org.w3c.dom.Node + */ + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + if (newChild == null) + return null; // nothing to do + if (refChild != null && refChild.getParentNode() != this) { + throw new DOMException(DOMException.NOT_FOUND_ERR, new String()); + } + if (!isChildEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + if (newChild == refChild) + return newChild; // nothing to do + + if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) { + // insert child nodes instead + for (Node child = newChild.getFirstChild(); child != null; child = newChild.getFirstChild()) { + newChild.removeChild(child); + insertBefore(child, refChild); + } + return newChild; + } + // synchronized in case another thread is getting item, or length + synchronized (lockObject) { + this.childNodesCache = null; // invalidate child nodes cache + } + + NodeImpl child = (NodeImpl) newChild; + NodeImpl next = (NodeImpl) refChild; + NodeImpl prev = null; + Node oldParent = child.getParentNode(); + if (oldParent != null) + oldParent.removeChild(child); + if (next == null) { + prev = this.lastChild; + this.lastChild = child; + } else { + prev = (NodeImpl) next.getPreviousSibling(); + next.setPreviousSibling(child); + } + if (prev == null) + this.firstChild = child; + else + prev.setNextSibling(child); + child.setPreviousSibling(prev); + child.setNextSibling(next); + child.setParentNode(this); + // make sure having the same owner document + if (child.getOwnerDocument() == null) { + if (getNodeType() == DOCUMENT_NODE) { + child.setOwnerDocument((Document) this); + } else { + child.setOwnerDocument(getOwnerDocument()); + } + } + + notifyChildReplaced(child, null); + + return child; + } + + public boolean isChildEditable() { + if (!fChildEditable) { + XMLModelImpl model = (XMLModelImpl) getModel(); + if (model != null && model.isReparsing()) { + return true; + } + } + return fChildEditable; + } + + /** + * isContainer method + * + * @return boolean + */ + public boolean isContainer() { + return true; + } + + /** + * item method + * + * @return org.w3c.dom.Node + * @param index + * int + */ + public Node item(int index) { + if (this.firstChild == null) + return null; + synchronized (lockObject) { + if (this.childNodesCache == null) + this.childNodesCache = new ChildNodesCache(); + return this.childNodesCache.item(index); + } + } + + /** + * notifyChildReplaced method + * + * @param newChild + * org.w3c.dom.Node + * @param oldChild + * org.w3c.dom.Node + */ + protected void notifyChildReplaced(Node newChild, Node oldChild) { + DocumentImpl document = (DocumentImpl) getContainerDocument(); + if (document == null) + return; + + syncChildEditableState(newChild); + + XMLModelImpl model = (XMLModelImpl) document.getModel(); + if (model == null) + return; + model.childReplaced(this, newChild, oldChild); + } + + /** + * removeChild method + * + * @return org.w3c.dom.Node + * @param oldChild + * org.w3c.dom.Node + */ + public Node removeChild(Node oldChild) throws DOMException { + if (oldChild == null) + return null; + if (oldChild.getParentNode() != this) { + throw new DOMException(DOMException.NOT_FOUND_ERR, new String()); + } + + if (!isChildEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + // synchronized in case another thread is getting item, or length + synchronized (lockObject) { + this.childNodesCache = null; // invalidate child nodes cache + } + + NodeImpl child = (NodeImpl) oldChild; + NodeImpl prev = (NodeImpl) child.getPreviousSibling(); + NodeImpl next = (NodeImpl) child.getNextSibling(); + + child.setEditable(true, true); // clear ReadOnly flags + + if (prev == null) + this.firstChild = next; + else + prev.setNextSibling(next); + if (next == null) + this.lastChild = prev; + else + next.setPreviousSibling(prev); + child.setPreviousSibling(null); + child.setNextSibling(null); + child.setParentNode(null); + + notifyChildReplaced(null, child); + + return child; + } + + /** + * removeChildNodes method + */ + public void removeChildNodes() { + if (!isChildEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + Node nextChild = null; + for (Node child = getFirstChild(); child != null; child = nextChild) { + nextChild = child.getNextSibling(); + removeChild(child); + } + } + + /** + * removeChildNodes method + * + * @return org.w3c.dom.DocumentFragment + * @param firstChild + * org.w3c.dom.Node + * @param lastChild + * org.w3c.dom.Node + */ + public DocumentFragment removeChildNodes(Node firstChild, Node lastChild) { + if (!hasChildNodes()) + return null; + if (!isChildEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + Document document = null; + if (getNodeType() == DOCUMENT_NODE) + document = (Document) this; + else + document = getOwnerDocument(); + if (document == null) + return null; + DocumentFragment fragment = document.createDocumentFragment(); + if (fragment == null) + return null; + + if (firstChild == null) + firstChild = getFirstChild(); + if (lastChild == null) + lastChild = getLastChild(); + Node nextChild = null; + for (Node child = firstChild; child != null; child = nextChild) { + nextChild = child.getNextSibling(); + removeChild(child); + fragment.appendChild(child); + if (child == lastChild) + break; + } + + return fragment; + } + + /** + * replaceChild method + * + * @return org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + * @param oldChild + * org.w3c.dom.Node + */ + public Node replaceChild(Node newChild, Node oldChild) throws DOMException { + if (!isChildEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + if (oldChild == null) + return newChild; + if (newChild != null) + insertBefore(newChild, oldChild); + return removeChild(oldChild); + } + + public void setChildEditable(boolean editable) { + if (fChildEditable == editable) { + return; + } + + ReadOnlyController roc = ReadOnlyController.getInstance(); + Node node; + if (editable) { + for (node = getFirstChild(); node != null; node = node.getNextSibling()) { + roc.unlockNode((XMLNode) node); + } + } else { + for (node = getFirstChild(); node != null; node = node.getNextSibling()) { + roc.lockNode((XMLNode) node); + } + } + + fChildEditable = editable; + notifyEditableChanged(); + } + + protected void syncChildEditableState(Node child) { + ReadOnlyController roc = ReadOnlyController.getInstance(); + if (fChildEditable) { + roc.unlockNode((NodeImpl) child); + } else { + roc.lockNode((NodeImpl) child); + } + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeImpl.java new file mode 100644 index 0000000000..0ea5dde799 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeImpl.java @@ -0,0 +1,809 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.sse.core.AbstractNotifier; +import org.eclipse.wst.sse.core.IFactoryRegistry; +import org.eclipse.wst.sse.core.text.IStructuredDocument; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.xml.core.document.InvalidCharacterException; +import org.eclipse.wst.xml.core.document.XMLModel; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.w3c.dom.DOMException; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + + +/** + * NodeImpl class + */ +public abstract class NodeImpl extends AbstractNotifier implements XMLNode { + // define one empty nodelist, for repeated use + private final static NodeList EMPTY_NODE_LIST = new NodeListImpl(); + + private boolean fDataEditable = true; + private IStructuredDocumentRegion flatNode = null; + private NodeImpl nextSibling = null; + + private DocumentImpl ownerDocument = null; + private NodeImpl parentNode = null; + private NodeImpl previousSibling = null; + + /** + * NodeImpl constructor + */ + protected NodeImpl() { + super(); + } + + /** + * NodeImpl constructor + * + * @param that + * NodeImpl + */ + protected NodeImpl(NodeImpl that) { + if (that != null) { + this.ownerDocument = that.ownerDocument; + } + } + + /** + * appendChild method + * + * @return org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + */ + public Node appendChild(Node newChild) throws DOMException { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, new String()); + } + + /** + * contains method + * + * @return boolean + * @param offset + * int + */ + public boolean contains(int offset) { + return (offset >= getStartOffset() && offset < getEndOffset()); + } + + /** + * @param s + * @param tagName + * @return + */ + protected String createDOMExceptionMessage(short s, String tagName) { + String result = null; + // TODO: Should localize these messages, and provide /u escaped + // version of tagName + result = lookupMessage(s) + " " + tagName; //$NON-NLS-1$ + return result; + } + + /** + * getAttributes method + * + * @return org.w3c.dom.NamedNodeMap + */ + public NamedNodeMap getAttributes() { + return null; + } + + /** + */ + protected String getCharValue(String name) { + DocumentImpl document = (DocumentImpl) getOwnerDocument(); + if (document == null) + return null; + return document.getCharValue(name); + } + + /** + * getChildNodes method + * + * @return org.w3c.dom.NodeList + */ + public NodeList getChildNodes() { + // As per DOM spec, correct behavior for getChildNodes is to return a + // zero length NodeList, not null, when there are no children. + // We'll use a common instance of an empty node list, just to prevent + // creating a trival object many many times. + + return EMPTY_NODE_LIST; + } + + /** + * getCommonAncestor method + * + * @return org.w3c.dom.Node + * @param node + * org.w3c.dom.Node + */ + public Node getCommonAncestor(Node node) { + if (node == null) + return null; + + for (Node na = node; na != null; na = na.getParentNode()) { + for (Node ta = this; ta != null; ta = ta.getParentNode()) { + if (ta == na) + return ta; + } + } + + return null; // not found + } + + /** + * getContainerDocument method + * + * @return org.w3c.dom.Document + */ + public Document getContainerDocument() { + for (Node node = this; node != null; node = node.getParentNode()) { + if (node.getNodeType() == Node.DOCUMENT_NODE) { + return (Document) node; + } + } + return null; + } + + /** + * getEndOffset method + * + * @return int + */ + public int getEndOffset() { + Node node = this; + while (node != null) { + if (node.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) node; + IStructuredDocumentRegion endStructuredDocumentRegion = element.getEndStructuredDocumentRegion(); + if (endStructuredDocumentRegion != null) + return endStructuredDocumentRegion.getEnd(); + } + + Node last = node.getLastChild(); + if (last != null) { // dig into the last + node = last; + continue; + } + + IStructuredDocumentRegion lastStructuredDocumentRegion = ((NodeImpl) node).getStructuredDocumentRegion(); + if (lastStructuredDocumentRegion != null) + return lastStructuredDocumentRegion.getEnd(); + + Node prev = node.getPreviousSibling(); + if (prev != null) { // move to the previous + node = prev; + continue; + } + + Node parent = node.getParentNode(); + node = null; + while (parent != null) { + if (parent.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) parent; + IStructuredDocumentRegion startStructuredDocumentRegion = element.getStartStructuredDocumentRegion(); + if (startStructuredDocumentRegion != null) + return startStructuredDocumentRegion.getEnd(); + } + Node parentPrev = parent.getPreviousSibling(); + if (parentPrev != null) { // move to the previous + node = parentPrev; + break; + } + parent = parent.getParentNode(); + } + } + return 0; + } + + public IStructuredDocumentRegion getEndStructuredDocumentRegion() { + return null; + } + + /** + */ + public IFactoryRegistry getFactoryRegistry() { + XMLModel model = getModel(); + if (model != null) { + IFactoryRegistry reg = model.getFactoryRegistry(); + if (reg != null) + return reg; + } + return null; + } + + /** + * getFirstChild method + * + * @return org.w3c.dom.Node + */ + public Node getFirstChild() { + return null; + } + + /** + * getFirstStructuredDocumentRegion method + * + * @return com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + public IStructuredDocumentRegion getFirstStructuredDocumentRegion() { + return StructuredDocumentRegionUtil.getStructuredDocumentRegion(this.flatNode); + } + + /** + */ + public int getIndex() { + Node parent = getParentNode(); + if (parent == null) + return -1; // error + int index = 0; + for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { + if (child == this) + return index; + index++; + } + return -1; // error + } + + /** + * getLastChild method + * + * @return org.w3c.dom.Node + */ + public Node getLastChild() { + return null; + } + + /** + * getLastStructuredDocumentRegion method + * + * @return com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + public IStructuredDocumentRegion getLastStructuredDocumentRegion() { + return StructuredDocumentRegionUtil.getStructuredDocumentRegion(this.flatNode); + } + + /** + */ + public String getLocalName() { + return null; + } + + /** + * the default implementation can just refer to the owning document + */ + public XMLModel getModel() { + if (this.ownerDocument == null) + return null; + return this.ownerDocument.getModel(); + } + + /** + * all but attr return null + */ + public ITextRegion getNameRegion() { + return null; + } + + /** + */ + public String getNamespaceURI() { + return null; + } + + /** + * getNextSibling method + * + * @return org.w3c.dom.Node + */ + public Node getNextSibling() { + return this.nextSibling; + } + + /** + * getNodeAt method + * + * @return org.w3c.dom.Node + * @param offset + * int + */ + Node getNodeAt(int offset) { + XMLNode parent = this; + XMLNode child = (XMLNode) getFirstChild(); + while (child != null) { + if (child.getEndOffset() <= offset) { + child = (XMLNode) child.getNextSibling(); + continue; + } + if (child.getStartOffset() > offset) { + break; + } + + IStructuredDocumentRegion startStructuredDocumentRegion = child.getStartStructuredDocumentRegion(); + if (startStructuredDocumentRegion != null) { + if (startStructuredDocumentRegion.getEnd() > offset) + return child; + } + + // dig more + parent = child; + child = (XMLNode) parent.getFirstChild(); + } + + return parent; + } + + /** + * getNodeValue method + * + * @return java.lang.String + */ + public String getNodeValue() throws DOMException { + return null; + } + + /** + * getOwnerDocument method + * + * @return org.w3c.dom.Document + */ + public Document getOwnerDocument() { + return this.ownerDocument; + } + + /** + * getParentNode method + * + * @return org.w3c.dom.Node + */ + public Node getParentNode() { + return this.parentNode; + } + + /** + */ + public String getPrefix() { + return null; + } + + /** + * getPreviousSibling method + * + * @return org.w3c.dom.Node + */ + public Node getPreviousSibling() { + return this.previousSibling; + } + + /** + */ + public String getSource() { + if (this.flatNode == null) + return new String(); + return this.flatNode.getText(); + } + + /** + * getStartOffset method + * + * @return int + */ + public int getStartOffset() { + if (this.flatNode != null) + return this.flatNode.getStart(); + NodeImpl prev = (NodeImpl) getPreviousSibling(); + if (prev != null) + return prev.getEndOffset(); + Node parent = getParentNode(); + if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) parent; + if (element.hasStartTag()) + return element.getStartEndOffset(); + return element.getStartOffset(); + } + // final fallback to look into first child + NodeImpl child = (NodeImpl) getFirstChild(); + while (child != null) { + IStructuredDocumentRegion childStructuredDocumentRegion = child.getStructuredDocumentRegion(); + if (childStructuredDocumentRegion != null) + return childStructuredDocumentRegion.getStart(); + child = (NodeImpl) child.getFirstChild(); + } + return 0; + } + + public IStructuredDocumentRegion getStartStructuredDocumentRegion() { + return getFirstStructuredDocumentRegion(); + } + + /** + * Every node (indirectly) knows its structuredDocument + */ + public IStructuredDocument getStructuredDocument() { + return getModel().getStructuredDocument(); + } + + /** + */ + IStructuredDocumentRegion getStructuredDocumentRegion() { + return this.flatNode; + } + + /** + * all but attr return null + */ + public ITextRegion getValueRegion() { + return null; + } + + /** + */ + public String getValueSource() { + return getNodeValue(); + } + + /** + */ + public boolean hasAttributes() { + return false; + } + + /** + * hasChildNodes method + * + * @return boolean + */ + public boolean hasChildNodes() { + return false; + } + + /** + * hasProperties method + * + * @return boolean + */ + public boolean hasProperties() { + return false; + } + + /** + * insertBefore method + * + * @return org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + * @param refChild + * org.w3c.dom.Node + */ + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, new String()); + } + + public boolean isChildEditable() { + return false; + } + + /** + */ + public boolean isClosed() { + return true; + } + + /** + * isContainer method + * + * @return boolean + */ + public boolean isContainer() { + return false; + } + + public boolean isDataEditable() { + if (!fDataEditable) { + XMLModelImpl model = (XMLModelImpl) getModel(); + if (model != null && model.isReparsing()) { + return true; + } + } + return fDataEditable; + } + + /** + */ + public boolean isSupported(String feature, String version) { + if (this.ownerDocument == null) + return false; + DOMImplementation impl = this.ownerDocument.getImplementation(); + if (impl == null) + return false; + return impl.hasFeature(feature, version); + } + + /** + * @param s + * @return + */ + private String lookupMessage(short s) { + // TODO: make localized version + String result = null; + switch (s) { + case DOMException.INVALID_CHARACTER_ERR : + result = "INVALID_CHARACTER_ERR"; //$NON-NLS-1$ + break; + + default : + result = new String(); + break; + } + return result; + } + + /** + * normalize method + */ + public void normalize() { + TextImpl prevText = null; + for (Node child = getFirstChild(); child != null; child = child.getNextSibling()) { + switch (child.getNodeType()) { + case TEXT_NODE : { + if (prevText == null) { + prevText = (TextImpl) child; + break; + } + Text text = (Text) child; + removeChild(text); + prevText.appendText(text); + child = prevText; + break; + } + case ELEMENT_NODE : { + Element element = (Element) child; + element.normalize(); + prevText = null; + break; + } + default : + prevText = null; + break; + } + } + } + + protected void notifyEditableChanged() { + DocumentImpl document = (DocumentImpl) getContainerDocument(); + if (document == null) + return; + XMLModelImpl model = (XMLModelImpl) document.getModel(); + if (model == null) + return; + model.editableChanged(this); + } + + /** + * notifyValueChanged method + */ + protected void notifyValueChanged() { + DocumentImpl document = (DocumentImpl) getContainerDocument(); + if (document == null) + return; + + syncDataEditableState(); + + XMLModelImpl model = (XMLModelImpl) document.getModel(); + if (model == null) + return; + model.valueChanged(this); + } + + /** + * removeChild method + * + * @return org.w3c.dom.Node + * @param oldChild + * org.w3c.dom.Node + */ + public Node removeChild(Node oldChild) throws DOMException { + throw new DOMException(DOMException.NOT_FOUND_ERR, new String()); + } + + /** + * removeChildNodes method + */ + public void removeChildNodes() { + } + + /** + * removeChildNodes method + * + * @return org.w3c.dom.DocumentFragment + * @param firstChild + * org.w3c.dom.Node + * @param lastChild + * org.w3c.dom.Node + */ + public DocumentFragment removeChildNodes(Node firstChild, Node lastChild) { + return null; + } + + /** + * replaceChild method + * + * @return org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + * @param oldChild + * org.w3c.dom.Node + */ + public Node replaceChild(Node newChild, Node oldChild) throws DOMException { + throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, new String()); + } + + /** + * Resets children values from IStructuredDocumentRegion. + */ + void resetStructuredDocumentRegions() { + for (NodeImpl child = (NodeImpl) getFirstChild(); child != null; child = (NodeImpl) child.getNextSibling()) { + child.resetStructuredDocumentRegions(); + } + this.flatNode = null; + } + + public void setChildEditable(boolean editable) { + // nop + } + + public void setDataEditable(boolean editable) { + if (fDataEditable == editable) { + return; + } + + ReadOnlyController roc = ReadOnlyController.getInstance(); + if (editable) { + roc.unlockData(this); + } else { + roc.lockData(this); + } + + fDataEditable = editable; + + notifyEditableChanged(); + } + + public void setEditable(boolean editable, boolean deep) { + if (deep) { + XMLNode node = (XMLNode) getFirstChild(); + while (node != null) { + node.setEditable(editable, deep); + node = (XMLNode) node.getNextSibling(); + } + } + setChildEditable(editable); + setDataEditable(editable); + } + + /** + * setNextSibling method + * + * @param nextSibling + * org.w3c.dom.Node + */ + protected void setNextSibling(Node nextSibling) { + this.nextSibling = (NodeImpl) nextSibling; + } + + /** + * setNodeValue method + * + * @param nodeValue + * java.lang.String + */ + public void setNodeValue(String nodeValue) throws DOMException { + } + + /** + * setOwnerDocument method + * + * @param ownerDocument + * org.w3c.dom.Document + */ + protected void setOwnerDocument(Document ownerDocument) { + this.ownerDocument = (DocumentImpl) ownerDocument; + } + + /** + */ + protected void setOwnerDocument(Document ownerDocument, boolean deep) { + this.ownerDocument = (DocumentImpl) ownerDocument; + + if (deep) { + for (NodeImpl child = (NodeImpl) getFirstChild(); child != null; child = (NodeImpl) child.getNextSibling()) { + child.setOwnerDocument(ownerDocument, deep); + } + } + } + + /** + * setParentNode method + * + * @param parentNode + * org.w3c.dom.Node + */ + protected void setParentNode(Node parentNode) { + this.parentNode = (NodeImpl) parentNode; + } + + /** + */ + public void setPrefix(String prefix) throws DOMException { + } + + /** + * setPreviousSibling method + * + * @param previousSibling + * org.w3c.dom.Node + */ + protected void setPreviousSibling(Node previousSibling) { + this.previousSibling = (NodeImpl) previousSibling; + } + + /** + */ + public void setSource(String source) throws InvalidCharacterException { + // not supported + } + + /** + */ + void setStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + this.flatNode = flatNode; + } + + /** + */ + public void setValueSource(String source) { + setNodeValue(source); + } + + protected void syncDataEditableState() { + ReadOnlyController roc = ReadOnlyController.getInstance(); + if (fDataEditable) { + roc.unlockData(this); + } else { + roc.lockData(this); + } + } + + /** + * toString method + * + * @return java.lang.String + */ + public String toString() { + return getNodeName(); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeIteratorImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeIteratorImpl.java new file mode 100644 index 0000000000..c4a278b694 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeIteratorImpl.java @@ -0,0 +1,257 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; +import org.w3c.dom.traversal.NodeFilter; +import org.w3c.dom.traversal.NodeIterator; + +/** + * NodeIteratorImpl class + */ +public class NodeIteratorImpl implements NodeIterator { + private NodeFilter filter = null; + private Node nextNode = null; + + private Node rootNode = null; + private int whatToShow = NodeFilter.SHOW_ALL; + + /** + * NodeIteratorImpl constructor + * + * @param rootNode + * org.w3c.dom.Node + */ + NodeIteratorImpl(Node rootNode, int whatToShow, NodeFilter filter) { + this.rootNode = rootNode; + this.nextNode = rootNode; + this.whatToShow = whatToShow; + this.filter = filter; + } + + /** + */ + private final boolean acceptNode(Node node) { + if (this.whatToShow != NodeFilter.SHOW_ALL) { + if (node == null) + return false; + short nodeType = node.getNodeType(); + switch (this.whatToShow) { + case NodeFilter.SHOW_ELEMENT : + if (nodeType != Node.ELEMENT_NODE) + return false; + break; + case NodeFilter.SHOW_ATTRIBUTE : + if (nodeType != Node.ATTRIBUTE_NODE) + return false; + break; + case NodeFilter.SHOW_TEXT : + if (nodeType != Node.TEXT_NODE) + return false; + break; + case NodeFilter.SHOW_CDATA_SECTION : + if (nodeType != Node.CDATA_SECTION_NODE) + return false; + break; + case NodeFilter.SHOW_ENTITY_REFERENCE : + if (nodeType != Node.ENTITY_REFERENCE_NODE) + return false; + break; + case NodeFilter.SHOW_ENTITY : + if (nodeType != Node.ENTITY_NODE) + return false; + break; + case NodeFilter.SHOW_PROCESSING_INSTRUCTION : + if (nodeType != Node.PROCESSING_INSTRUCTION_NODE) + return false; + break; + case NodeFilter.SHOW_COMMENT : + if (nodeType != Node.COMMENT_NODE) + return false; + break; + case NodeFilter.SHOW_DOCUMENT : + if (nodeType != Node.DOCUMENT_NODE) + return false; + break; + case NodeFilter.SHOW_DOCUMENT_TYPE : + if (nodeType != Node.DOCUMENT_TYPE_NODE) + return false; + break; + case NodeFilter.SHOW_DOCUMENT_FRAGMENT : + if (nodeType != Node.DOCUMENT_FRAGMENT_NODE) + return false; + break; + case NodeFilter.SHOW_NOTATION : + if (nodeType != Node.NOTATION_NODE) + return false; + break; + default : + return false; + } + } + if (this.filter != null) { + return (this.filter.acceptNode(node) == NodeFilter.FILTER_ACCEPT); + } + return true; + } + + /** + * Detaches the <code>NodeIterator</code> from the set which it iterated + * over, releasing any computational resources and placing the iterator in + * the INVALID state. After <code>detach</code> has been invoked, calls + * to <code>nextNode</code> or <code>previousNode</code> will raise + * the exception INVALID_STATE_ERR. + */ + public void detach() { + this.rootNode = null; + this.nextNode = null; + this.filter = null; + } + + /** + * The value of this flag determines whether the children of entity + * reference nodes are visible to the iterator. If false, they and their + * descendants will be rejected. Note that this rejection takes precedence + * over <code>whatToShow</code> and the filter. Also note that this is + * currently the only situation where <code>NodeIterators</code> may + * reject a complete subtree rather than skipping individual nodes. <br> + * <br> + * To produce a view of the document that has entity references expanded + * and does not expose the entity reference node itself, use the + * <code>whatToShow</code> flags to hide the entity reference node and + * set <code>expandEntityReferences</code> to true when creating the + * iterator. To produce a view of the document that has entity reference + * nodes but no entity expansion, use the <code>whatToShow</code> flags + * to show the entity reference node and set + * <code>expandEntityReferences</code> to false. + */ + public boolean getExpandEntityReferences() { + // not supported + return false; + } + + /** + * The <code>NodeFilter</code> used to screen nodes. + */ + public NodeFilter getFilter() { + return this.filter; + } + + /** + */ + private final Node getNextNode() { + if (this.nextNode == null) + return null; + Node oldNext = this.nextNode; + Node child = this.nextNode.getFirstChild(); + if (child != null) { + this.nextNode = child; + return oldNext; + } + for (Node node = this.nextNode; node != null && node != this.rootNode; node = node.getParentNode()) { + Node next = node.getNextSibling(); + if (next != null) { + this.nextNode = next; + return oldNext; + } + } + this.nextNode = null; + return oldNext; + } + + /** + */ + private final Node getPreviousNode() { + if (this.nextNode == this.rootNode) + return null; + Node prev = null; + if (this.nextNode == null) { + prev = this.rootNode; // never null + } else { + prev = this.nextNode.getPreviousSibling(); + if (prev == null) { + this.nextNode = this.nextNode.getParentNode(); + return this.nextNode; + } + } + Node last = prev.getLastChild(); + while (last != null) { + prev = last; + last = prev.getLastChild(); + } + this.nextNode = prev; + return this.nextNode; + } + + /** + * The root node of the <code>NodeIterator</code>, as specified when it + * was created. + */ + public Node getRoot() { + return this.rootNode; + } + + /** + * This attribute determines which node types are presented via the + * iterator. The available set of constants is defined in the + * <code>NodeFilter</code> interface. Nodes not accepted by + * <code>whatToShow</code> will be skipped, but their children may still + * be considered. Note that this skip takes precedence over the filter, if + * any. + */ + public int getWhatToShow() { + return this.whatToShow; + } + + /** + * Returns the next node in the set and advances the position of the + * iterator in the set. After a <code>NodeIterator</code> is created, + * the first call to <code>nextNode()</code> returns the first node in + * the set. + * + * @return The next <code>Node</code> in the set being iterated over, or + * <code>null</code> if there are no more members in that set. + * @exception DOMException + * INVALID_STATE_ERR: Raised if this method is called after + * the <code>detach</code> method was invoked. + */ + public Node nextNode() throws DOMException { + for (Node node = getNextNode(); node != null; node = getNextNode()) { + if (acceptNode(node)) + return node; + } + return null; + } + + /** + * Returns the previous node in the set and moves the position of the + * <code>NodeIterator</code> backwards in the set. + * + * @return The previous <code>Node</code> in the set being iterated + * over, or <code>null</code> if there are no more members in + * that set. + * @exception DOMException + * INVALID_STATE_ERR: Raised if this method is called after + * the <code>detach</code> method was invoked. + */ + public Node previousNode() throws DOMException { + for (Node node = getPreviousNode(); node != null; node = getPreviousNode()) { + if (acceptNode(node)) + return node; + } + return null; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeListImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeListImpl.java new file mode 100644 index 0000000000..4bfed52901 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NodeListImpl.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import java.util.Vector; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * NodeListImpl class + */ +public class NodeListImpl implements NodeList { + + Object lockObject = new byte[0]; + + private Vector nodes = null; + + /** + * NodeListImpl constructor + */ + public NodeListImpl() { + super(); + } + + /** + * appendNode method + * + * @return org.w3c.dom.Node + * @param node + * org.w3c.dom.Node + */ + protected Node appendNode(Node node) { + if (node == null) + return null; + if (this.nodes == null) + this.nodes = new Vector(); + this.nodes.addElement(node); + return node; + } + + /** + * getLength method + * + * @return int + */ + public int getLength() { + synchronized (lockObject) { + if (this.nodes == null) + return 0; + return this.nodes.size(); + } + } + + /** + */ + protected Node insertNode(Node node, int index) { + if (node == null) + return null; + if (this.nodes == null || index >= this.nodes.size()) { + return appendNode(node); + } + this.nodes.insertElementAt(node, index); + return node; + } + + /** + * item method + * + * @return org.w3c.dom.Node + */ + public Node item(int index) { + synchronized (lockObject) { + if (this.nodes == null) + return null; + if (index < 0 || index >= this.nodes.size()) + return null; + return (Node) this.nodes.elementAt(index); + } + } + + /** + * removeNode method + * + * @return org.w3c.dom.Node + * @param index + * int + */ + protected Node removeNode(int index) { + if (this.nodes == null) + return null; // no node + if (index < 0 || index >= this.nodes.size()) + return null; // invalid parameter + + Node removed = (Node) this.nodes.elementAt(index); + this.nodes.removeElementAt(index); + return removed; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NotationImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NotationImpl.java new file mode 100644 index 0000000000..e0489499ed --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/NotationImpl.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; +import org.w3c.dom.Notation; + +/** + * NotationImpl class + */ +public class NotationImpl extends NodeImpl implements Notation { + + private String name = null; + private String publicId = null; + private String systemId = null; + + /** + * NotationImpl constructor + */ + protected NotationImpl() { + super(); + } + + /** + * NotationImpl constructor + * + * @param that + * NotationImpl + */ + protected NotationImpl(NotationImpl that) { + super(that); + + if (that != null) { + this.name = that.name; + this.publicId = that.publicId; + this.systemId = that.systemId; + } + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + * @param deep + * boolean + */ + public Node cloneNode(boolean deep) { + NotationImpl cloned = new NotationImpl(this); + return cloned; + } + + /** + * getNodeName method + * + * @return java.lang.String + */ + public String getNodeName() { + if (this.name == null) + return new String(); + return this.name; + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return NOTATION_NODE; + } + + /** + * getPublicId method + * + * @return java.lang.String + */ + public String getPublicId() { + return this.publicId; + } + + /** + * getSystemId method + * + * @return java.lang.String + */ + public String getSystemId() { + return this.systemId; + } + + /** + * setName method + * + * @param name + * java.lang.String + */ + protected void setName(String name) { + this.name = name; + } + + /** + * setPublicId method + * + * @param publicId + * java.lang.String + */ + public void setPublicId(String publicId) { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + this.publicId = publicId; + } + + /** + * setSystemId method + * + * @param systemId + * java.lang.String + */ + public void setSystemId(String systemId) { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + this.systemId = systemId; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ProcessingInstructionImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ProcessingInstructionImpl.java new file mode 100644 index 0000000000..de9a4fe014 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ProcessingInstructionImpl.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import java.util.Iterator; + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.jsp.model.parser.temp.XMLJSPRegionContexts; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; +import org.w3c.dom.DOMException; +import org.w3c.dom.Node; +import org.w3c.dom.ProcessingInstruction; + + +/** + * ProcessingInstructionImpl class + */ +public class ProcessingInstructionImpl extends NodeImpl implements XMLJSPRegionContexts, ProcessingInstruction { + private String data = null; + + private String target = null; + + /** + * ProcessingInstructionImpl constructor + */ + protected ProcessingInstructionImpl() { + super(); + } + + /** + * ProcessingInstructionImpl constructor + * + * @param that + * ProcessingInstructionImpl + */ + protected ProcessingInstructionImpl(ProcessingInstructionImpl that) { + super(that); + + if (that != null) { + this.target = that.target; + this.data = that.getData(); + } + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + * @param deep + * boolean + */ + public Node cloneNode(boolean deep) { + ProcessingInstructionImpl cloned = new ProcessingInstructionImpl(this); + return cloned; + } + + /** + * getData method + * + * @return java.lang.String + */ + public String getData() { + if (this.data != null) + return this.data; + + IStructuredDocumentRegion flatNode = getFirstStructuredDocumentRegion(); + if (flatNode == null) + return new String(); + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return new String(); + + ITextRegion targetRegion = null; + ITextRegion dataRegion = null; + ITextRegion closeRegion = null; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_PI_OPEN) + continue; + if (regionType == XMLRegionContext.XML_PI_CLOSE) { + closeRegion = region; + } else { + if (targetRegion == null) + targetRegion = region; + else if (dataRegion == null) + dataRegion = region; + } + } + if (dataRegion == null) + return new String(); + int offset = dataRegion.getStart(); + int end = flatNode.getLength(); + if (closeRegion != null) + end = closeRegion.getStart(); + String source = flatNode.getText(); + return source.substring(offset, end); + } + + /** + * getNodeName method + * + * @return java.lang.String + */ + public String getNodeName() { + return getTarget(); + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return PROCESSING_INSTRUCTION_NODE; + } + + /** + * getNodeValue method + * + * @return java.lang.String + */ + public String getNodeValue() { + return getData(); + } + + /** + * getTarget method + * + * @return java.lang.String + */ + public String getTarget() { + if (this.target == null) + return new String(); + return this.target; + } + + /** + */ + public boolean isClosed() { + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null) + return true; // will be generated + String regionType = StructuredDocumentRegionUtil.getLastRegionType(flatNode); + return (regionType == XMLRegionContext.XML_PI_CLOSE); + } + + /** + */ + void resetStructuredDocumentRegions() { + this.data = getData(); + setStructuredDocumentRegion(null); + } + + /** + * setData method + * + * @param data + * java.lang.String + */ + public void setData(String data) throws DOMException { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.data = data; + + notifyValueChanged(); + } + + /** + * setNodeValue method + * + * @param nodeValue + * java.lang.String + */ + public void setNodeValue(String nodeValue) throws DOMException { + setData(nodeValue); + } + + /** + */ + void setStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + super.setStructuredDocumentRegion(flatNode); + if (flatNode != null) + this.data = null; + } + + /** + * setTarget method + * + * @param target + * java.lang.String + */ + protected void setTarget(String target) { + this.target = target; + } + + /** + * toString method + * + * @return java.lang.String + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append(getTarget()); + buffer.append('('); + buffer.append(getData()); + buffer.append(')'); + return buffer.toString(); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/RangeImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/RangeImpl.java new file mode 100644 index 0000000000..1c54ba53d0 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/RangeImpl.java @@ -0,0 +1,630 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.xml.core.document.XMLNode; +import org.w3c.dom.DOMException; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; +import org.w3c.dom.ranges.Range; +import org.w3c.dom.ranges.RangeException; + + +/** + */ +public class RangeImpl implements Range { + private Node endContainer = null; + private int endOffset = 0; + + private Node startContainer = null; + private int startOffset = 0; + + /** + */ + protected RangeImpl() { + super(); + } + + /** + */ + protected RangeImpl(RangeImpl that) { + super(); + + if (that != null) { + this.startContainer = that.startContainer; + this.startOffset = that.startOffset; + this.endContainer = that.endContainer; + this.endOffset = that.endOffset; + } + } + + /** + * Duplicates the contents of a Range + * + * @return A DocumentFragment that contains content equivalent to this + * Range. + * @exception DOMException + * HIERARCHY_REQUEST_ERR: Raised if a DocumentType node + * would be extracted into the new DocumentFragment. <br> + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public DocumentFragment cloneContents() throws DOMException { + // not supported + return null; + } + + /** + * Produces a new Range whose boundary-points are equal to the + * boundary-points of the Range. + * + * @return The duplicated Range. + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public Range cloneRange() throws DOMException { + return new RangeImpl(this); + } + + /** + * Collapse a Range onto one of its boundary-points + * + * @param toStartIf + * TRUE, collapses the Range onto its start; if FALSE, + * collapses it onto its end. + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public void collapse(boolean toStart) throws DOMException { + if (toStart) { + this.endContainer = this.startContainer; + this.endOffset = this.startOffset; + } else { + this.startContainer = this.endContainer; + this.startOffset = this.endOffset; + } + } + + /** + * Compare the boundary-points of two Ranges in a document. + * + * @param howA + * code representing the type of comparison, as defined above. + * @param sourceRangeThe + * <code>Range</code> on which this current + * <code>Range</code> is compared to. + * @return -1, 0 or 1 depending on whether the corresponding + * boundary-point of the Range is respectively before, equal to, + * or after the corresponding boundary-point of + * <code>sourceRange</code>. + * @exception DOMException + * WRONG_DOCUMENT_ERR: Raised if the two Ranges are not in + * the same Document or DocumentFragment. <br> + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public short compareBoundaryPoints(short how, Range sourceRange) throws DOMException { + if (sourceRange == null) + return (short) 0; // error + + Node container1 = null; + int offset1 = 0; + Node container2 = null; + int offset2 = 0; + + switch (how) { + case START_TO_START : + container1 = this.startContainer; + offset1 = this.startOffset; + container2 = sourceRange.getStartContainer(); + offset2 = sourceRange.getStartOffset(); + break; + case START_TO_END : + container1 = this.startContainer; + offset1 = this.startOffset; + container2 = sourceRange.getEndContainer(); + offset2 = sourceRange.getEndOffset(); + break; + case END_TO_END : + container1 = this.endContainer; + offset1 = this.endOffset; + container2 = sourceRange.getEndContainer(); + offset2 = sourceRange.getEndOffset(); + break; + case END_TO_START : + container1 = this.endContainer; + offset1 = this.endOffset; + container2 = sourceRange.getStartContainer(); + offset2 = sourceRange.getStartOffset(); + break; + default : + return (short) 0; // error + } + + return comparePoints(container1, offset1, container2, offset2); + } + + /* + */ + protected short comparePoints(Node container1, int offset1, Node container2, int offset2) { + if (container1 == null || container2 == null) + return (short) 0; // error + + if (container1 == container2) { + if (offset1 > offset2) + return (short) 1; + if (offset1 < offset2) + return (short) -1; + return 0; + } + + // get node offsets + XMLNode node1 = null; + if (container1.hasChildNodes()) { + Node child = container1.getFirstChild(); + for (int i = 0; i < offset1; i++) { + Node next = child.getNextSibling(); + if (next == null) + break; + child = next; + } + node1 = (XMLNode) child; + offset1 = 0; + } else { + node1 = (XMLNode) container1; + } + int nodeOffset1 = node1.getStartOffset(); + XMLNode node2 = null; + if (container2.hasChildNodes()) { + Node child = container2.getFirstChild(); + for (int i = 0; i < offset2; i++) { + Node next = child.getNextSibling(); + if (next == null) + break; + child = next; + } + node2 = (XMLNode) child; + offset2 = 0; + } else { + node2 = (XMLNode) container1; + } + int nodeOffset2 = node2.getStartOffset(); + + if (nodeOffset1 > nodeOffset2) + return (short) 1; + if (nodeOffset1 < nodeOffset2) + return (short) -1; + if (offset1 > offset2) + return (short) 1; + if (offset1 < offset2) + return (short) -1; + return (short) 0; + } + + /** + * Removes the contents of a Range from the containing document or + * document fragment without returning a reference to the removed content. + * + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised if any portion of + * the content of the Range is read-only or any of the + * nodes that contain any of the content of the Range are + * read-only. <br> + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public void deleteContents() throws DOMException { + // not supported + } + + /** + * Called to indicate that the Range is no longer in use and that the + * implementation may relinquish any resources associated with this Range. + * Subsequent calls to any methods or attribute getters on this Range will + * result in a <code>DOMException</code> being thrown with an error code + * of <code>INVALID_STATE_ERR</code>. + * + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public void detach() throws DOMException { + this.startContainer = null; + this.startOffset = 0; + this.endContainer = null; + this.endOffset = 0; + } + + /** + * Moves the contents of a Range from the containing document or document + * fragment to a new DocumentFragment. + * + * @return A DocumentFragment containing the extracted contents. + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised if any portion of + * the content of the Range is read-only or any of the + * nodes which contain any of the content of the Range are + * read-only. <br> + * HIERARCHY_REQUEST_ERR: Raised if a DocumentType node + * would be extracted into the new DocumentFragment. <br> + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public DocumentFragment extractContents() throws DOMException { + // not supported + return null; + } + + /** + * TRUE if the Range is collapsed + * + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public boolean getCollapsed() throws DOMException { + if (this.startContainer == this.endContainer && this.startOffset == this.endOffset) + return true; + return false; + } + + /** + * The deepest common ancestor container of the Range's two + * boundary-points. + * + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public Node getCommonAncestorContainer() throws DOMException { + if (this.startContainer == null) + return null; + return ((NodeImpl) this.startContainer).getCommonAncestor(this.endContainer); + } + + /** + * Node within which the Range ends + * + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public Node getEndContainer() throws DOMException { + return this.endContainer; + } + + /** + * Offset within the ending node of the Range. + * + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public int getEndOffset() throws DOMException { + return this.endOffset; + } + + /** + * Node within which the Range begins + * + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public Node getStartContainer() throws DOMException { + return this.startContainer; + } + + /** + * Offset within the starting node of the Range. + * + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public int getStartOffset() throws DOMException { + return this.startOffset; + } + + /** + * Inserts a node into the Document or DocumentFragment at the start of + * the Range. If the container is a Text node, this will be split at the + * start of the Range (as if the Text node's splitText method was + * performed at the insertion point) and the insertion will occur between + * the two resulting Text nodes. Adjacent Text nodes will not be + * automatically merged. If the node to be inserted is a DocumentFragment + * node, the children will be inserted rather than the DocumentFragment + * node itself. + * + * @param newNodeThe + * node to insert at the start of the Range + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor + * container of the start of the Range is read-only. <br> + * WRONG_DOCUMENT_ERR: Raised if <code>newNode</code> and + * the container of the start of the Range were not created + * from the same document. <br> + * HIERARCHY_REQUEST_ERR: Raised if the container of the + * start of the Range is of a type that does not allow + * children of the type of <code>newNode</code> or if + * <code>newNode</code> is an ancestor of the container. + * <br> + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + * @exception RangeException + * INVALID_NODE_TYPE_ERR: Raised if <code>newNode</code> + * is an Attr, Entity, Notation, or Document node. + */ + public void insertNode(Node newNode) throws RangeException, DOMException { + // not supported + } + + /** + * Select a node and its contents + * + * @param refNodeThe + * node to select. + * @exception RangeException + * INVALID_NODE_TYPE_ERR: Raised if an ancestor of + * <code>refNode</code> is an Entity, Notation or + * DocumentType node or if <code>refNode</code> is a + * Document, DocumentFragment, Attr, Entity, or Notation + * node. + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public void selectNode(Node refNode) throws RangeException, DOMException { + if (refNode == null) + return; + Node parent = refNode.getParentNode(); + if (parent == null) + return; + int index = ((NodeImpl) refNode).getIndex(); + if (index < 0) + return; + setStart(parent, index); + setEnd(parent, index + 1); + } + + /** + * Select the contents within a node + * + * @param refNodeNode + * to select from + * @exception RangeException + * INVALID_NODE_TYPE_ERR: Raised if <code>refNode</code> + * or an ancestor of <code>refNode</code> is an Entity, + * Notation or DocumentType node. + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public void selectNodeContents(Node refNode) throws RangeException, DOMException { + if (refNode == null) + return; + if (refNode.getNodeType() == Node.TEXT_NODE) { + Text text = (Text) refNode; + setStart(refNode, 0); + setEnd(refNode, text.getLength()); + } else { + NodeList childNodes = refNode.getChildNodes(); + int length = (childNodes != null ? childNodes.getLength() : 0); + setStart(refNode, 0); + setEnd(refNode, length); + } + } + + /** + * Sets the attributes describing the end of a Range. + * + * @param refNodeThe + * <code>refNode</code> value. This parameter must be + * different from <code>null</code>. + * @param offsetThe + * <code>endOffset</code> value. + * @exception RangeException + * INVALID_NODE_TYPE_ERR: Raised if <code>refNode</code> + * or an ancestor of <code>refNode</code> is an Entity, + * Notation, or DocumentType node. + * @exception DOMException + * INDEX_SIZE_ERR: Raised if <code>offset</code> is + * negative or greater than the number of child units in + * <code>refNode</code>. Child units are 16-bit units if + * <code>refNode</code> is a type of CharacterData node + * (e.g., a Text or Comment node) or a + * ProcessingInstruction node. Child units are Nodes in all + * other cases. <br> + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public void setEnd(Node refNode, int offset) throws RangeException, DOMException { + this.endContainer = refNode; + this.endOffset = offset; + } + + /** + * Sets the end of a Range to be after a node + * + * @param refNodeRange + * ends after <code>refNode</code>. + * @exception RangeException + * INVALID_NODE_TYPE_ERR: Raised if the root container of + * <code>refNode</code> is not an Attr, Document or + * DocumentFragment node or if <code>refNode</code> is a + * Document, DocumentFragment, Attr, Entity, or Notation + * node. + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public void setEndAfter(Node refNode) throws RangeException, DOMException { + if (refNode == null) + return; + Node parent = refNode.getParentNode(); + if (parent == null) + return; + int index = ((NodeImpl) refNode).getIndex(); + if (index < 0) + return; + setEnd(parent, index + 1); + } + + /** + * Sets the end position to be before a node. + * + * @param refNodeRange + * ends before <code>refNode</code> + * @exception RangeException + * INVALID_NODE_TYPE_ERR: Raised if the root container of + * <code>refNode</code> is not an Attr, Document, or + * DocumentFragment node or if <code>refNode</code> is a + * Document, DocumentFragment, Attr, Entity, or Notation + * node. + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public void setEndBefore(Node refNode) throws RangeException, DOMException { + if (refNode == null) + return; + Node parent = refNode.getParentNode(); + if (parent == null) + return; + int index = ((NodeImpl) refNode).getIndex(); + if (index < 0) + return; + setEnd(parent, index); + } + + /** + * Sets the attributes describing the start of the Range. + * + * @param refNodeThe + * <code>refNode</code> value. This parameter must be + * different from <code>null</code>. + * @param offsetThe + * <code>startOffset</code> value. + * @exception RangeException + * INVALID_NODE_TYPE_ERR: Raised if <code>refNode</code> + * or an ancestor of <code>refNode</code> is an Entity, + * Notation, or DocumentType node. + * @exception DOMException + * INDEX_SIZE_ERR: Raised if <code>offset</code> is + * negative or greater than the number of child units in + * <code>refNode</code>. Child units are 16-bit units if + * <code>refNode</code> is a type of CharacterData node + * (e.g., a Text or Comment node) or a + * ProcessingInstruction node. Child units are Nodes in all + * other cases. <br> + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public void setStart(Node refNode, int offset) throws RangeException, DOMException { + this.startContainer = refNode; + this.startOffset = offset; + } + + /** + * Sets the start position to be after a node + * + * @param refNodeRange + * starts after <code>refNode</code> + * @exception RangeException + * INVALID_NODE_TYPE_ERR: Raised if the root container of + * <code>refNode</code> is not an Attr, Document, or + * DocumentFragment node or if <code>refNode</code> is a + * Document, DocumentFragment, Attr, Entity, or Notation + * node. + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public void setStartAfter(Node refNode) throws RangeException, DOMException { + if (refNode == null) + return; + Node parent = refNode.getParentNode(); + if (parent == null) + return; + int index = ((NodeImpl) refNode).getIndex(); + if (index < 0) + return; + setStart(parent, index + 1); + } + + /** + * Sets the start position to be before a node + * + * @param refNodeRange + * starts before <code>refNode</code> + * @exception RangeException + * INVALID_NODE_TYPE_ERR: Raised if the root container of + * <code>refNode</code> is not an Attr, Document, or + * DocumentFragment node or if <code>refNode</code> is a + * Document, DocumentFragment, Attr, Entity, or Notation + * node. + * @exception DOMException + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + */ + public void setStartBefore(Node refNode) throws RangeException, DOMException { + if (refNode == null) + return; + Node parent = refNode.getParentNode(); + if (parent == null) + return; + int index = ((NodeImpl) refNode).getIndex(); + if (index < 0) + return; + setStart(parent, index); + } + + /** + * Reparents the contents of the Range to the given node and inserts the + * node at the position of the start of the Range. + * + * @param newParentThe + * node to surround the contents with. + * @exception DOMException + * NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor + * container of either boundary-point of the Range is + * read-only. <br> + * WRONG_DOCUMENT_ERR: Raised if <code> newParent</code> + * and the container of the start of the Range were not + * created from the same document. <br> + * HIERARCHY_REQUEST_ERR: Raised if the container of the + * start of the Range is of a type that does not allow + * children of the type of <code>newParent</code> or if + * <code>newParent</code> is an ancestor of the container + * or if <code>node</code> would end up with a child node + * of a type not allowed by the type of <code>node</code>. + * <br> + * INVALID_STATE_ERR: Raised if <code>detach()</code> has + * already been invoked on this object. + * @exception RangeException + * BAD_BOUNDARYPOINTS_ERR: Raised if the Range partially + * selects a non-text node. <br> + * INVALID_NODE_TYPE_ERR: Raised if <code> node</code> is + * an Attr, Entity, DocumentType, Notation, Document, or + * DocumentFragment node. + */ + public void surroundContents(Node newParent) throws RangeException, DOMException { + // not supported + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ReadOnlyController.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ReadOnlyController.java new file mode 100644 index 0000000000..67aa59c493 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/ReadOnlyController.java @@ -0,0 +1,334 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.sse.core.text.IStructuredDocument; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.document.XMLElement; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.eclipse.wst.xml.core.document.XMLText; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; +import org.w3c.dom.Node; + + +class ReadOnlyController { + + class Span { + int length; + int offset; + + Span(int offset, int length) { + this.offset = offset; + this.length = length; + } + } + + private static ReadOnlyController fInstance; + + static synchronized ReadOnlyController getInstance() { + if (fInstance == null) { + fInstance = new ReadOnlyController(); + } + return fInstance; + } + + static private void lock(IStructuredDocument doc, int offset, int length, boolean canInsertBefore, boolean canInsertAfter) { + if (doc == null) { + return; + } + doc.makeReadOnly(offset, length); + } + + static private void lock(IStructuredDocumentRegion node, boolean canInsertBefore, boolean canInsertAfter) { + if (node == null) { + return; + } + IStructuredDocument doc = node.getParentDocument(); + if (doc == null) { + return; + } + doc.makeReadOnly(node.getStart(), node.getLength()); + } + + static private void unlock(IStructuredDocumentRegion node) { + if (node == null) { + return; + } + IStructuredDocument doc = node.getParentDocument(); + if (doc == null) { + return; + } + doc.clearReadOnly(node.getStart(), node.getLength()); + } + + private ReadOnlyController() { + super(); + } + + private Span getDataSpan(XMLNode node) { + switch (node.getNodeType()) { + case Node.ELEMENT_NODE : + return getDataSpanForElement((XMLElement) node); + case Node.TEXT_NODE : + return getDataSpanForText((XMLText) node); + default : + return new Span(0, -1); + } + } + + private Span getDataSpanForElement(XMLElement node) { + IStructuredDocumentRegion docRegion = node.getStartStructuredDocumentRegion(); + if (docRegion == null) { + return new Span(0, -1); + } + + ITextRegionList regions = docRegion.getRegions(); + if (regions == null) { + return new Span(0, -1); + } + + String startType; + String endType; + if (node.isCommentTag()) { + startType = XMLRegionContext.XML_COMMENT_OPEN; + endType = XMLRegionContext.XML_COMMENT_CLOSE; + } else { + startType = XMLRegionContext.XML_TAG_NAME; + endType = XMLRegionContext.XML_TAG_CLOSE; + } + + int startOffset = -1; + int endOffset = -1; + ITextRegion prevRegion = null; + ITextRegion region; + for (int i = 0; i < regions.size(); i++) { + region = regions.get(i); + String type = region.getType(); + if (type == startType) { + startOffset = region.getEnd(); + } else if (type == endType && prevRegion != null) { + endOffset = prevRegion.getTextEnd(); + } + prevRegion = region; + } + + if (0 <= startOffset && 0 <= endOffset) { + return new Span(startOffset, endOffset - startOffset); + } else { + return new Span(0, -1); + } + } + + private Span getDataSpanForText(XMLText node) { + IStructuredDocumentRegion docRegion = ((NodeImpl) node).getStructuredDocumentRegion(); + if (docRegion == null) { + return new Span(0, -1); + } + return new Span(0, docRegion.getLength()); + } + + /** + * This method is used from parent's setChildEditable() + * + * case 1:<parent><node attr="value"/> <node2></parent> + * x####################x case 2:<parent><node attr="value"> <child> + * </child> </node> </parent> x###################? ?#######x (? : + * editable if node.isEditable() == true) + */ + void lockBoth(XMLNode node) { + if (node == null) { + return; + } + + IStructuredDocumentRegion flatNode; + boolean canInsert = false; + + // end node (element) + if (node.getNodeType() == Node.ELEMENT_NODE) { + flatNode = node.getEndStructuredDocumentRegion(); + if (flatNode != null) { + canInsert = node.isChildEditable(); + lock(flatNode, canInsert, false); + } + } + // start node + flatNode = node.getStartStructuredDocumentRegion(); + if (flatNode != null) { + lock(flatNode, false, canInsert); + } + } + + void lockData(XMLNode node) { + if (node == null) { + return; + } + + Span span = getDataSpan(node); + if (0 <= span.length) { + lock(node.getModel().getStructuredDocument(), node.getStartOffset() + span.offset, span.length, false, false); + } + } + + /** + * lock itself and all descendants + */ + void lockDeep(XMLNode node) { + if (node == null) { + return; + } + + int offset = node.getStartOffset(); + int length = node.getEndOffset() - offset; + + boolean canInsert = true; + XMLNode parent = (XMLNode) node.getParentNode(); + if (parent != null && !parent.isChildEditable()) { + canInsert = false; + } + lock(node.getStructuredDocument(), offset, length, canInsert, canInsert); + } + + /** + * This method is used from parent's setChildEditable() + * + * case 1:<parent><node attr="value"/> <node2></parent> x######x x##x + * case 2:<parent><node attr="value"> <child></child> </node> </parent> + * x######x x#? ?#######x (? : editable if node.isEditable() == true) + */ + void lockNode(XMLNode node) { + if (node == null) { + return; + } + if (!node.isDataEditable()) { + lockBoth(node); + return; + } + + IStructuredDocumentRegion flatNode; + boolean canInsert = false; + + // end node (element) + if (node.getNodeType() == Node.ELEMENT_NODE) { + flatNode = node.getEndStructuredDocumentRegion(); + if (flatNode != null) { + canInsert = node.isChildEditable(); + lock(flatNode, canInsert, false); + } + } + // start node + flatNode = node.getStartStructuredDocumentRegion(); + if (flatNode != null) { + Span span = getDataSpan(node); + if (0 <= span.length) { + IStructuredDocument structuredDocument = flatNode.getParentDocument(); + int offset, length; + offset = flatNode.getStart(); + length = span.offset; + lock(structuredDocument, offset, length, false, false); + offset = offset + span.offset + span.length; + length = flatNode.getEnd() - offset; + lock(structuredDocument, offset, length, canInsert, false); + } else { + lock(flatNode, false, canInsert); + } + } + } + + private void unlock(IStructuredDocument doc, int offset, int length) { + if (doc == null) { + return; + } + doc.clearReadOnly(offset, length); + } + + void unlockBoth(XMLNode node) { + if (node == null) { + return; + } + + IStructuredDocumentRegion flatNode; + // start node + flatNode = node.getStartStructuredDocumentRegion(); + if (flatNode != null) { + unlock(flatNode); + } + // end node + flatNode = node.getEndStructuredDocumentRegion(); + if (flatNode != null) { + unlock(flatNode); + } + } + + void unlockData(XMLNode node) { + if (node == null) { + return; + } + + Span span = getDataSpan(node); + if (0 <= span.length) { + unlock(node.getModel().getStructuredDocument(), span.offset, span.length); + } + } + + void unlockDeep(XMLNode node) { + if (node == null) { + return; + } + + int offset = node.getStartOffset(); + int length = node.getEndOffset() - offset; + + unlock(node.getStructuredDocument(), offset, length); + } + + void unlockNode(XMLNode node) { + if (node == null) { + return; + } + + IStructuredDocumentRegion flatNode; + // end node + if (node.getNodeType() == Node.ELEMENT_NODE) { + flatNode = node.getEndStructuredDocumentRegion(); + if (flatNode != null) { + unlock(flatNode); + } + } + + // start node + flatNode = node.getStartStructuredDocumentRegion(); + if (flatNode != null) { + if (node.isDataEditable()) { + unlock(flatNode); + } else { + Span span = getDataSpan(node); + if (span.length <= 0) { + unlock(flatNode); + } else { + IStructuredDocument structuredDocument = flatNode.getParentDocument(); + int offset, length; + offset = flatNode.getStart(); + length = span.offset - offset; + unlock(structuredDocument, offset, length); + offset = span.offset + span.length; + length = flatNode.getEnd() - span.offset; + unlock(structuredDocument, offset, length); + } + } + } + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/SourceValidator.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/SourceValidator.java new file mode 100644 index 0000000000..3b3d941c6c --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/SourceValidator.java @@ -0,0 +1,351 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.sse.core.internal.nls.ResourceHandler1; +import org.eclipse.wst.xml.core.document.InvalidCharacterException; +import org.eclipse.wst.xml.core.document.JSPTag; +import org.eclipse.wst.xml.core.document.XMLCharEntity; +import org.eclipse.wst.xml.core.document.XMLDocument; +import org.w3c.dom.Attr; +import org.w3c.dom.Node; + + +/** + */ +public class SourceValidator { + + private NodeImpl node = null; + + /** + */ + public SourceValidator(Node node) { + super(); + + if (node != null) { + this.node = (NodeImpl) node; + } + } + + /** + */ + public String convertSource(String source) { + if (source == null) + return null; + if (this.node == null) + return null; // error + + // setup conversion conditions + boolean acceptTag = false; + boolean acceptClose = false; + boolean acceptQuote = false; + boolean acceptAmpersand = false; + boolean acceptEntityRef = true; + boolean acceptJSPEnd = true; + String endTagName = null; + if (this.node.getNodeType() == Node.ATTRIBUTE_NODE) { + XMLDocument document = (XMLDocument) this.node.getOwnerDocument(); + if (document != null && document.isJSPType()) + acceptTag = true; + if (acceptTag) { + Attr attr = (Attr) this.node; + ElementImpl element = (ElementImpl) attr.getOwnerElement(); + if (element != null && element.isJSPTag()) + acceptTag = false; + } + // if the source does not include single quote, + // double quote is valid + acceptQuote = (source.indexOf('\'') < 0); + } else if (this.node.getNodeType() == Node.TEXT_NODE) { + TextImpl text = (TextImpl) this.node; + if (text.isJSPContent()) { + int index = source.indexOf(JSPTag.TAG_CLOSE); + if (index < 0) + return source; + acceptTag = true; + acceptClose = true; + acceptQuote = true; + acceptAmpersand = true; + acceptJSPEnd = false; + } else if (text.isCDATAContent()) { + endTagName = text.getParentNode().getNodeName(); + if (endTagName == null) + return null; // error + acceptTag = true; + acceptClose = true; + acceptQuote = true; + acceptAmpersand = true; + } + } else { + XMLDocument document = null; + if (this.node.getNodeType() == Node.DOCUMENT_NODE) { + document = (XMLDocument) this.node; + } else { + document = (XMLDocument) this.node.getOwnerDocument(); + } + if (document != null && document.isJSPType()) + acceptTag = true; + } + + StringBuffer buffer = null; + int copiedLength = 0; + int length = source.length(); + for (int i = 0; i < length; i++) { + String ref = null; + char c = source.charAt(i); + switch (c) { + case '<' : + if (acceptTag) { + if (endTagName != null) { + if (!matchEndTag(source, i + 1, endTagName)) + continue; + } else { + int skip = skipTag(source, i + 1); + if (skip >= 0) { + i += skip; + continue; + } + } + // invalid JSP tag + } + ref = XMLCharEntity.LT_REF; + break; + case '>' : + if (acceptClose) + continue; + ref = XMLCharEntity.GT_REF; + break; + case '&' : + if (acceptAmpersand) + continue; + if (acceptEntityRef) { + int skip = skipEntityRef(source, i + 1); + if (skip >= 0) { + i += skip; + continue; + } + } + ref = XMLCharEntity.AMP_REF; + break; + case '"' : + if (acceptQuote) + continue; + ref = XMLCharEntity.QUOT_REF; + break; + case '%' : + if (acceptJSPEnd) + continue; + if (source.charAt(i + 1) != '>') + continue; + i++; + ref = XMLCharEntity.GT_REF; + break; + default : + continue; + } + + if (ref != null) { + if (buffer == null) { + buffer = new StringBuffer(length + 8); + } + if (i > copiedLength) { + buffer.append(source.substring(copiedLength, i)); + } + buffer.append(ref); + copiedLength = i + 1; // skip this character + } + } + + if (buffer != null) { + if (copiedLength < length) { + buffer.append(source.substring(copiedLength, length)); + } + return buffer.toString(); + } + return source; + } + + /** + */ + private final boolean matchEndTag(String source, int offset, String endTagName) { + if (source == null || endTagName == null) + return false; + int length = source.length(); + if (offset < 0 || offset >= length) + return false; + if (source.charAt(offset) != '/') + return false; + offset++; + int end = offset + endTagName.length(); + if (end > length) + return false; + return endTagName.equalsIgnoreCase(source.substring(offset, end)); + } + + /** + */ + private final int skipEntityRef(String source, int offset) { + if (source == null) + return -1; + if (offset < 0 || offset >= source.length()) + return -1; + DocumentImpl document = (DocumentImpl) this.node.getOwnerDocument(); + if (document == null) + return -1; // error + + int end = source.indexOf(';', offset); + if (end < 0 || end == offset) + return -1; + String name = source.substring(offset, end); + if (name == null || document.getCharValue(name) == null) + return -1; + return (end + 1 - offset); + } + + /** + */ + private final int skipTag(String source, int offset) { + if (source == null) + return -1; + if (offset < 0 || offset >= source.length()) + return -1; + + int end = offset; + if (source.charAt(offset) == '%') { + // JSP tag + int found = source.indexOf(JSPTag.TAG_CLOSE, offset + 1); + if (found < 0) + return -1; // invalid JSP tag + end = found + 2; + } else { + // normal tag + int found = source.indexOf('>', offset); + if (found < 0) + return -1; // invalid tag + end = found + 1; + } + return (end - offset); + } + + /** + */ + public boolean validateSource(String source) throws InvalidCharacterException { + if (source == null) + return true; + if (this.node == null) + return false; // error + String message = null; + + // setup validation conditions + boolean acceptTag = false; + boolean acceptClose = false; + boolean acceptQuote = true; + boolean acceptEntityRef = true; + String endTagName = null; + if (this.node.getNodeType() == Node.ATTRIBUTE_NODE) { + XMLDocument document = (XMLDocument) this.node.getOwnerDocument(); + if (document != null && document.isJSPType()) + acceptTag = true; + if (acceptTag) { + Attr attr = (Attr) this.node; + ElementImpl element = (ElementImpl) attr.getOwnerElement(); + if (element != null && element.isJSPTag()) + acceptTag = false; + } + // if the source does not include single quote, + // double quote is valid + acceptQuote = (source.indexOf('\'') < 0); + } else if (this.node.getNodeType() == Node.TEXT_NODE) { + TextImpl text = (TextImpl) this.node; + if (text.isJSPContent()) { + int index = source.indexOf(JSPTag.TAG_CLOSE); + if (index < 0) + return true; + message = ResourceHandler1.getString("Invalid_character_('>')_fo_ERROR_"); //$NON-NLS-1$ = "Invalid character ('>') found" + throw new InvalidCharacterException(message, '>', index + 1); + } else if (text.isCDATAContent()) { + endTagName = text.getParentNode().getNodeName(); + if (endTagName == null) + return false; // error + acceptTag = true; + acceptClose = true; + } + } else { + XMLDocument document = null; + if (this.node.getNodeType() == Node.DOCUMENT_NODE) { + document = (XMLDocument) this.node; + } else { + document = (XMLDocument) this.node.getOwnerDocument(); + } + if (document != null && document.isJSPType()) + acceptTag = true; + } + + char c = 0; + int length = source.length(); + for (int i = 0; i < length; i++) { + c = source.charAt(i); + switch (c) { + case '<' : + if (acceptTag) { + if (endTagName != null) { + if (!matchEndTag(source, i + 1, endTagName)) + continue; + } else { + int skip = skipTag(source, i + 1); + if (skip >= 0) { + i += skip; + continue; + } + } + // invalid tag + } + message = ResourceHandler1.getString("Invalid_character_('<')_fo_ERROR_"); //$NON-NLS-1$ = "Invalid character ('<') found" + break; + case '>' : + if (acceptClose) + continue; + message = ResourceHandler1.getString("Invalid_character_('>')_fo_ERROR_"); //$NON-NLS-1$ = "Invalid character ('>') found" + break; + case '&' : + if (acceptEntityRef) { + if (endTagName != null) + continue; + int skip = skipEntityRef(source, i + 1); + if (skip >= 0) { + i += skip; + continue; + } + // invalid entity reference + } + message = ResourceHandler1.getString("Invalid_character_('&')_fo_ERROR_"); //$NON-NLS-1$ = "Invalid character ('&') found" + break; + case '"' : + if (acceptQuote) + continue; + message = ResourceHandler1.getString("Invalid_character_('__')_f_EXC_"); //$NON-NLS-1$ = "Invalid character ('\"') found" + break; + default : + continue; + } + + if (message != null) { + throw new InvalidCharacterException(message, c, i); + } + } + + return true; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionChecker.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionChecker.java new file mode 100644 index 0000000000..47a50fd2c8 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionChecker.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import java.io.IOException; +import java.io.Writer; + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.xml.core.document.XMLModel; +import org.w3c.dom.Node; + + +/** + * This class is only for debug purpose. + */ +public class StructuredDocumentRegionChecker { + String EOL = System.getProperty("line.separator"); //$NON-NLS-1$ + + private int offset = 0; + Writer testWriter = null; + + /** + */ + public StructuredDocumentRegionChecker() { + super(); + } + + public StructuredDocumentRegionChecker(Writer writer) { + super(); + testWriter = writer; + } + + /** + */ + private void checkChildNodes(Node node) { + for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { + checkNode(child); + } + } + + /** + */ + public void checkModel(XMLModel model) { + checkChildNodes(model.getDocument()); + } + + /** + */ + private void checkNode(Node node) { + checkStructuredDocumentRegion(((NodeImpl) node).getStructuredDocumentRegion()); + if (node.getNodeType() == Node.ELEMENT_NODE) { + checkChildNodes(node); + checkStructuredDocumentRegion(((ElementImpl) node).getEndStructuredDocumentRegion()); + } + } + + /** + */ + private void checkStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return; + + if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + int n = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < n; i++) { + IStructuredDocumentRegion c = container.getStructuredDocumentRegion(i); + if (c == null) { + reportError("null"); //$NON-NLS-1$ + continue; + } + checkStructuredDocumentRegion(c); + } + return; + } + + int start = flatNode.getStart(); + if (start < this.offset) + reportError("overwrap"); //$NON-NLS-1$ + if (start > this.offset) + reportError("gap"); //$NON-NLS-1$ + int end = flatNode.getEnd(); + this.offset = end; + + if (flatNode instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) flatNode; + IStructuredDocumentRegion p = proxy.getStructuredDocumentRegion(); + if (p == null) { + reportError("null"); //$NON-NLS-1$ + return; + } + int s = p.getStart(); + int e = p.getEnd(); + if (s > start || e < end) + reportError("out"); //$NON-NLS-1$ + if (s == start && e == end) + reportWarning("vain"); //$NON-NLS-1$ + } + } + + /** + */ + private void reportError(String message) { + String msg = "StructuredDocumentRegionChecker : error : " + message; //$NON-NLS-1$ + if (testWriter != null) { + try { + testWriter.write(msg + EOL); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + System.out.println(msg); + } + throw new StructuredDocumentRegionManagementException(); + } + + /** + */ + private void reportWarning(String message) { + String msg = "StructuredDocumentRegionChecker : warning : " + message; //$NON-NLS-1$ + if (testWriter != null) { + try { + testWriter.write(msg + EOL); + } catch (IOException e) { + e.printStackTrace(); + } + } else { + System.out.println(msg); + } + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionContainer.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionContainer.java new file mode 100644 index 0000000000..166d8c67b1 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionContainer.java @@ -0,0 +1,605 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import java.util.Vector; + +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocument; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionContainer; +import org.eclipse.wst.sse.core.text.ITextRegionList; + + +class StructuredDocumentRegionContainer implements IStructuredDocumentRegion { + + private Vector flatNodes = new Vector(2); + + /** + */ + StructuredDocumentRegionContainer() { + super(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#addRegion(com.ibm.sed.structured.text.ITextRegion) + */ + public void addRegion(ITextRegion aRegion) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjust(int) + */ + public void adjust(int i) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustLengthWith(int) + */ + public void adjustLengthWith(int i) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustStart(int) + */ + public void adjustStart(int i) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + // XXX Auto-generated method stub + + } + + /** + */ + void appendStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return; + if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + if (container.getStructuredDocumentRegionCount() > 0) { + this.flatNodes.addAll(container.flatNodes); + } + } else { + this.flatNodes.addElement(flatNode); + } + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#containsOffset(int) + */ + public boolean containsOffset(int i) { + // XXX Auto-generated method stub + return false; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#containsOffset(com.ibm.sed.structured.text.ITextRegion, + * int) + */ + public boolean containsOffset(ITextRegion region, int i) { + // XXX Auto-generated method stub + return false; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#equatePositions(com.ibm.sed.structured.text.ITextRegion) + */ + public void equatePositions(ITextRegion region) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getDeepestRegionAtCharacterOffset(int) + */ + public ITextRegion getDeepestRegionAtCharacterOffset(int offset) { + // XXX Auto-generated method stub + return null; + } + + /** + */ + public int getEnd() { + IStructuredDocumentRegion last = getLastStructuredDocumentRegion(); + if (last == null) + return 0; + return last.getEnd(); + } + + /** + */ + public int getEndOffset() { + return getEnd(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getEndOffset(com.ibm.sed.structured.text.ITextRegion) + */ + public int getEndOffset(ITextRegion containedRegion) { + // XXX Auto-generated method stub + return 0; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getFirstRegion() + */ + public ITextRegion getFirstRegion() { + // XXX Auto-generated method stub + return null; + } + + /** + */ + IStructuredDocumentRegion getFirstStructuredDocumentRegion() { + if (this.flatNodes.isEmpty()) + return null; + return (IStructuredDocumentRegion) this.flatNodes.elementAt(0); + } + + /** + */ + public String getFullText() { + return getText(); + } + + /** + */ + public String getFullText(ITextRegion aRegion) { + // not supported + return null; + } + + /** + */ + public String getFullText(String context) { + // not supported + return null; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getLastRegion() + */ + public ITextRegion getLastRegion() { + // XXX Auto-generated method stub + return null; + } + + /** + */ + IStructuredDocumentRegion getLastStructuredDocumentRegion() { + int size = this.flatNodes.size(); + if (size == 0) + return null; + return (IStructuredDocumentRegion) this.flatNodes.elementAt(size - 1); + } + + /** + */ + public int getLength() { + return (getEnd() - getStart()); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#getNext() + */ + public IStructuredDocumentRegion getNext() { + // XXX Auto-generated method stub + return null; + } + + /** + */ + public int getNumberOfRegions() { + // not supported + return 0; + } + + /** + */ + public ITextRegionContainer getParent() { + return null; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#getParentDocument() + */ + public IStructuredDocument getParentDocument() { + // XXX Auto-generated method stub + return null; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#getPrevious() + */ + public IStructuredDocumentRegion getPrevious() { + // XXX Auto-generated method stub + return null; + } + + /** + */ + public ITextRegion getRegionAtCharacterOffset(int offset) { + // not supported + return null; + } + + /** + */ + public ITextRegionList getRegions() { + // not supported + return null; + } + + /** + */ + public int getStart() { + IStructuredDocumentRegion first = getFirstStructuredDocumentRegion(); + if (first == null) + return 0; + return first.getStart(); + } + + /** + */ + public int getStartOffset() { + return getStart(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getStartOffset(com.ibm.sed.structured.text.ITextRegion) + */ + public int getStartOffset(ITextRegion containedRegion) { + // XXX Auto-generated method stub + return 0; + } + + /** + */ + public IStructuredDocument getStructuredDocument() { + IStructuredDocumentRegion first = getFirstStructuredDocumentRegion(); + if (first == null) + return null; + return first.getParentDocument(); + } + + /** + */ + IStructuredDocumentRegion getStructuredDocumentRegion(int index) { + if (index < 0 || index >= this.flatNodes.size()) + return null; + return (IStructuredDocumentRegion) this.flatNodes.elementAt(index); + } + + /** + */ + int getStructuredDocumentRegionCount() { + return this.flatNodes.size(); + } + + /** + */ + public String getText() { + int size = this.flatNodes.size(); + if (size == 0) + return new String(); + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < size; i++) { + IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) this.flatNodes.elementAt(i); + if (flatNode == null) + continue; + buffer.append(flatNode.getText()); + } + return buffer.toString(); + } + + /** + */ + public String getText(ITextRegion aRegion) { + // not supported + return null; + } + + /** + */ + public String getText(String context) { + // not supported + return null; + } + + /** + */ + public int getTextEnd() { + return getEnd(); + } + + /** + */ + public int getTextEndOffset() { + return getTextEnd(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getTextEndOffset(com.ibm.sed.structured.text.ITextRegion) + */ + public int getTextEndOffset(ITextRegion containedRegion) { + // XXX Auto-generated method stub + return 0; + } + + /** + * The text length is equal to length if there is no white space at the + * end of a region. Otherwise it is smaller than length. + */ + public int getTextLength() { + return (getTextEnd() - getStart()); + } + + /** + */ + public String getType() { + return "StructuredDocumentRegionContainer";//$NON-NLS-1$ + } + + /** + */ + void insertStructuredDocumentRegion(IStructuredDocumentRegion flatNode, int index) { + if (flatNode == null) + return; + if (index < 0) + return; + int size = this.flatNodes.size(); + if (index > size) + return; + if (index == size) { + appendStructuredDocumentRegion(flatNode); + return; + } + this.flatNodes.insertElementAt(flatNode, index); + } + + public boolean isDeleted() { + // I'll assume never really needed here + return false; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#isEnded() + */ + public boolean isEnded() { + // XXX Auto-generated method stub + return false; + } + + /** + */ + IStructuredDocumentRegion removeStructuredDocumentRegion(int index) { + if (index < 0 || index >= this.flatNodes.size()) + return null; + IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) this.flatNodes.elementAt(index); + this.flatNodes.removeElementAt(index); + return flatNode; + } + + /** + */ + IStructuredDocumentRegion removeStructuredDocumentRegion(IStructuredDocumentRegion oldStructuredDocumentRegion) { + if (oldStructuredDocumentRegion == null) + return null; + int size = this.flatNodes.size(); + for (int i = 0; i < size; i++) { + IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) this.flatNodes.elementAt(i); + if (flatNode == oldStructuredDocumentRegion) { + this.flatNodes.removeElementAt(i); + return flatNode; + } + } + return null; // not found + } + + /** + */ + IStructuredDocumentRegion replaceStructuredDocumentRegion(IStructuredDocumentRegion flatNode, int index) { + if (flatNode == null) + return removeStructuredDocumentRegion(index); + if (index < 0 || index >= this.flatNodes.size()) + return null; + IStructuredDocumentRegion oldStructuredDocumentRegion = (IStructuredDocumentRegion) this.flatNodes.elementAt(index); + this.flatNodes.setElementAt(flatNode, index); + return oldStructuredDocumentRegion; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#sameAs(com.ibm.sed.structured.text.IStructuredDocumentRegion, + * int) + */ + public boolean sameAs(IStructuredDocumentRegion region, int shift) { + // XXX Auto-generated method stub + return false; + } + + /** + */ + public boolean sameAs(ITextRegion region, int shift) { + // not support + return false; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#sameAs(com.ibm.sed.structured.text.ITextRegion, + * com.ibm.sed.structured.text.IStructuredDocumentRegion, + * com.ibm.sed.structured.text.ITextRegion, int) + */ + public boolean sameAs(ITextRegion oldRegion, IStructuredDocumentRegion documentRegion, ITextRegion newRegion, int shift) { + // XXX Auto-generated method stub + return false; + } + + public void setDeleted(boolean deleted) { + // I'll assume never really needed here + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#setEnded(boolean) + */ + public void setEnded(boolean hasEnd) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#setLength(int) + */ + public void setLength(int newLength) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#setNext(com.ibm.sed.structured.text.IStructuredDocumentRegion) + */ + public void setNext(IStructuredDocumentRegion newNext) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#setParentDocument(com.ibm.sed.structured.text.IStructuredDocument) + */ + public void setParentDocument(IStructuredDocument document) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#setPrevious(com.ibm.sed.structured.text.IStructuredDocumentRegion) + */ + public void setPrevious(IStructuredDocumentRegion newPrevious) { + // XXX Auto-generated method stub + + } + + /** + */ + public void setRegions(ITextRegionList embeddedRegions) { + // not supported + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#setStart(int) + */ + public void setStart(int newStart) { + // XXX Auto-generated method stub + + } + + /** + * toString method + * + * @return java.lang.String + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append('{'); + int count = getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + if (i != 0) + buffer.append(','); + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(i); + if (flatNode == null) + buffer.append("null");//$NON-NLS-1$ + else + buffer.append(flatNode.toString()); + } + buffer.append('}'); + return buffer.toString(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#updateModel(java.lang.Object, + * com.ibm.sed.structured.text.IStructuredDocumentRegion, + * java.lang.String, int, int) + */ + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion flatnode, String changes, int start, int end) { + // XXX Auto-generated method stub + return null; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionManagementException.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionManagementException.java new file mode 100644 index 0000000000..0057dd537f --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionManagementException.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + +import org.eclipse.wst.sse.core.exceptions.SourceEditingRuntimeException; + + + +/** + */ +public class StructuredDocumentRegionManagementException extends SourceEditingRuntimeException { + + /** + * StructuredDocumentRegionManagementException constructor + */ + public StructuredDocumentRegionManagementException() { + super("IStructuredDocumentRegion management failed.");//$NON-NLS-1$ + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionProxy.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionProxy.java new file mode 100644 index 0000000000..1503845032 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionProxy.java @@ -0,0 +1,548 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocument; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionContainer; +import org.eclipse.wst.sse.core.text.ITextRegionList; + + +class StructuredDocumentRegionProxy implements IStructuredDocumentRegion { + private IStructuredDocumentRegion flatNode = null; + private int length = 0; + + private int offset = 0; + + /** + */ + StructuredDocumentRegionProxy() { + super(); + } + + /** + */ + StructuredDocumentRegionProxy(int offset, int length) { + super(); + + this.offset = offset; + this.length = length; + } + + /** + */ + StructuredDocumentRegionProxy(int offset, int length, IStructuredDocumentRegion flatNode) { + super(); + + this.offset = offset; + this.length = length; + this.flatNode = flatNode; + if (this.flatNode != null) + this.offset -= this.flatNode.getStart(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#addRegion(com.ibm.sed.structured.text.ITextRegion) + */ + public void addRegion(ITextRegion aRegion) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjust(int) + */ + public void adjust(int i) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustLengthWith(int) + */ + public void adjustLengthWith(int i) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustStart(int) + */ + public void adjustStart(int i) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#containsOffset(int) + */ + public boolean containsOffset(int i) { + // XXX Auto-generated method stub + return false; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#containsOffset(com.ibm.sed.structured.text.ITextRegion, + * int) + */ + public boolean containsOffset(ITextRegion region, int i) { + // XXX Auto-generated method stub + return false; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#equatePositions(com.ibm.sed.structured.text.ITextRegion) + */ + public void equatePositions(ITextRegion region) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getDeepestRegionAtCharacterOffset(int) + */ + public ITextRegion getDeepestRegionAtCharacterOffset(int offset) { + // XXX Auto-generated method stub + return null; + } + + /** + */ + public int getEnd() { + int flatNodeOffset = 0; + if (this.flatNode != null) + flatNodeOffset = this.flatNode.getStart(); + return flatNodeOffset + this.offset + this.length; + } + + /** + */ + public int getEndOffset() { + return getEnd(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getEndOffset(com.ibm.sed.structured.text.ITextRegion) + */ + public int getEndOffset(ITextRegion containedRegion) { + // XXX Auto-generated method stub + return 0; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getFirstRegion() + */ + public ITextRegion getFirstRegion() { + // XXX Auto-generated method stub + return null; + } + + /** + */ + public String getFullText() { + return getText(); + } + + /** + */ + public String getFullText(ITextRegion aRegion) { + // not supported + return null; + } + + /** + */ + public String getFullText(String context) { + // not supported + return null; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getLastRegion() + */ + public ITextRegion getLastRegion() { + // XXX Auto-generated method stub + return null; + } + + /** + */ + public int getLength() { + return this.length; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#getNext() + */ + public IStructuredDocumentRegion getNext() { + // XXX Auto-generated method stub + return null; + } + + /** + */ + public int getNumberOfRegions() { + // not supported + return 0; + } + + /** + */ + int getOffset() { + int flatNodeOffset = 0; + if (this.flatNode != null) + flatNodeOffset = this.flatNode.getStart(); + return flatNodeOffset + this.offset; + } + + /** + */ + public ITextRegionContainer getParent() { + return null; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#getParentDocument() + */ + public IStructuredDocument getParentDocument() { + // XXX Auto-generated method stub + return null; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#getPrevious() + */ + public IStructuredDocumentRegion getPrevious() { + // XXX Auto-generated method stub + return null; + } + + /** + */ + public ITextRegion getRegionAtCharacterOffset(int offset) { + // not supported + return null; + } + + /** + */ + public ITextRegionList getRegions() { + // not supported + return null; + } + + /** + */ + public int getStart() { + int flatNodeOffset = 0; + if (this.flatNode != null) + flatNodeOffset = this.flatNode.getStart(); + return flatNodeOffset + this.offset; + } + + /** + */ + public int getStartOffset() { + return getStart(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getStartOffset(com.ibm.sed.structured.text.ITextRegion) + */ + public int getStartOffset(ITextRegion containedRegion) { + // XXX Auto-generated method stub + return 0; + } + + /** + */ + public IStructuredDocument getStructuredDocument() { + // not supported + return null; + } + + /** + */ + IStructuredDocumentRegion getStructuredDocumentRegion() { + return this.flatNode; + } + + /** + */ + public String getText() { + if (this.flatNode == null) + return new String(); + String text = this.flatNode.getText(); + if (text == null) + return new String(); + int end = this.offset + this.length; + return text.substring(this.offset, end); + } + + /** + */ + public String getText(ITextRegion aRegion) { + // not supported + return null; + } + + /** + */ + public String getText(String context) { + // not supported + return null; + } + + /** + */ + public int getTextEnd() { + return getEnd(); + } + + /** + */ + public int getTextEndOffset() { + return getTextEnd(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getTextEndOffset(com.ibm.sed.structured.text.ITextRegion) + */ + public int getTextEndOffset(ITextRegion containedRegion) { + // XXX Auto-generated method stub + return 0; + } + + /** + * The text length is equal to length if there is no white space at the + * end of a region. Otherwise it is smaller than length. + */ + public int getTextLength() { + return getLength(); + } + + /** + */ + public String getType() { + return "StructuredDocumentRegionProxy";//$NON-NLS-1$ + } + + public boolean isDeleted() { + // I'll assume never really needed here + return false; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#isEnded() + */ + public boolean isEnded() { + // XXX Auto-generated method stub + return false; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#sameAs(com.ibm.sed.structured.text.IStructuredDocumentRegion, + * int) + */ + public boolean sameAs(IStructuredDocumentRegion region, int shift) { + // XXX Auto-generated method stub + return false; + } + + /** + */ + public boolean sameAs(ITextRegion region, int shift) { + // not supported + return false; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#sameAs(com.ibm.sed.structured.text.ITextRegion, + * com.ibm.sed.structured.text.IStructuredDocumentRegion, + * com.ibm.sed.structured.text.ITextRegion, int) + */ + public boolean sameAs(ITextRegion oldRegion, IStructuredDocumentRegion documentRegion, ITextRegion newRegion, int shift) { + // XXX Auto-generated method stub + return false; + } + + public void setDeleted(boolean deleted) { + // I'll assume never really needed here + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#setEnded(boolean) + */ + public void setEnded(boolean hasEnd) { + // XXX Auto-generated method stub + + } + + /** + * had to make public, due to API transition. + */ + public void setLength(int length) { + this.length = length; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#setNext(com.ibm.sed.structured.text.IStructuredDocumentRegion) + */ + public void setNext(IStructuredDocumentRegion newNext) { + // XXX Auto-generated method stub + + } + + /** + */ + void setOffset(int offset) { + this.offset = offset; + if (this.flatNode != null) + this.offset -= this.flatNode.getStart(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#setParentDocument(com.ibm.sed.structured.text.IStructuredDocument) + */ + public void setParentDocument(IStructuredDocument document) { + // XXX Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#setPrevious(com.ibm.sed.structured.text.IStructuredDocumentRegion) + */ + public void setPrevious(IStructuredDocumentRegion newPrevious) { + // XXX Auto-generated method stub + + } + + /** + */ + public void setRegions(ITextRegionList embeddedRegions) { + // not supported + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.IStructuredDocumentRegion#setStart(int) + */ + public void setStart(int newStart) { + // XXX Auto-generated method stub + + } + + /** + */ + void setStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + if (this.flatNode != null) + this.offset += this.flatNode.getStart(); + this.flatNode = flatNode; + if (this.flatNode != null) + this.offset -= flatNode.getStart(); + } + + /** + * toString method + * + * @return java.lang.String + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer.append('['); + buffer.append(getStart()); + buffer.append(','); + buffer.append(getEnd()); + buffer.append(']'); + buffer.append('('); + if (this.flatNode != null) + buffer.append(this.flatNode.toString()); + else + buffer.append("null");//$NON-NLS-1$ + buffer.append(')'); + return buffer.toString(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#updateModel(java.lang.Object, + * com.ibm.sed.structured.text.IStructuredDocumentRegion, + * java.lang.String, int, int) + */ + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion flatnode, String changes, int start, int end) { + // XXX Auto-generated method stub + return null; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionUtil.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionUtil.java new file mode 100644 index 0000000000..82c848d940 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/StructuredDocumentRegionUtil.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.jsp.model.parser.temp.XMLJSPRegionContexts; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +/** + * Provides convenient functions to handle IStructuredDocumentRegion and + * ITextRegion. + */ +class StructuredDocumentRegionUtil implements XMLJSPRegionContexts { + + /** + * Extracts contents enclosed with quotes. Quotes may be double or single. + */ + static String getAttrValue(IStructuredDocumentRegion flatNode, ITextRegion region) { + if (region == null) + return null; + if (flatNode == null) + return null; + String value = flatNode.getText(region); + if (value == null) + return null; + int length = value.length(); + if (length == 0) + return value; + char firstChar = value.charAt(0); + if (firstChar == '"' || firstChar == '\'') { + if (length == 1) + return null; + if (value.charAt(length - 1) == firstChar) + length--; + return value.substring(1, length); + } + return value; + } + + /** + * Extracts the name without heading '&' and tailing ';'. + */ + static String getEntityRefName(IStructuredDocumentRegion flatNode, ITextRegion region) { + if (region == null) + return null; + String ref = flatNode.getText(region); + int length = ref.length(); + if (length == 0) + return ref; + int offset = 0; + if (ref.charAt(0) == '&') + offset = 1; + if (ref.charAt(length - 1) == ';') + length--; + if (offset >= length) + return null; + return ref.substring(offset, length); + } + + /** + * Returns the first region. + */ + static ITextRegion getFirstRegion(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return null; + ITextRegionList regions = flatNode.getRegions(); + if (regions == null || regions.size() == 0) + return null; + return regions.get(0); + } + + /** + * Returns the type of the first region. + */ + static String getFirstRegionType(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return XMLRegionContext.UNDEFINED; + ITextRegionList regions = flatNode.getRegions(); + if (regions == null || regions.size() == 0) + return XMLRegionContext.UNDEFINED; + ITextRegion region = regions.get(0); + return region.getType(); + } + + /** + */ + static IStructuredDocumentRegion getFirstStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return null; + if (flatNode instanceof StructuredDocumentRegionContainer) { + flatNode = ((StructuredDocumentRegionContainer) flatNode).getFirstStructuredDocumentRegion(); + } + if (flatNode instanceof StructuredDocumentRegionProxy) { + flatNode = ((StructuredDocumentRegionProxy) flatNode).getStructuredDocumentRegion(); + } + return flatNode; + } + + /** + * Returns the last region. + */ + static ITextRegion getLastRegion(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return null; + ITextRegionList regions = flatNode.getRegions(); + if (regions == null || regions.size() == 0) + return null; + return regions.get(regions.size() - 1); + } + + /** + * Returns the type of the first region. + */ + static String getLastRegionType(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return XMLRegionContext.UNDEFINED; + ITextRegionList regions = flatNode.getRegions(); + if (regions == null || regions.size() == 0) + return XMLRegionContext.UNDEFINED; + ITextRegion region = regions.get(regions.size() - 1); + return region.getType(); + } + + /** + */ + static IStructuredDocumentRegion getLastStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return null; + if (flatNode instanceof StructuredDocumentRegionContainer) { + flatNode = ((StructuredDocumentRegionContainer) flatNode).getLastStructuredDocumentRegion(); + } + if (flatNode instanceof StructuredDocumentRegionProxy) { + flatNode = ((StructuredDocumentRegionProxy) flatNode).getStructuredDocumentRegion(); + } + return flatNode; + } + + /** + */ + static IStructuredDocumentRegion getStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return null; + if (flatNode instanceof StructuredDocumentRegionProxy) { + flatNode = ((StructuredDocumentRegionProxy) flatNode).getStructuredDocumentRegion(); + } + return flatNode; + } + + StructuredDocumentRegionUtil() { + super(); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/TextImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/TextImpl.java new file mode 100644 index 0000000000..826fe54b3b --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/TextImpl.java @@ -0,0 +1,1107 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.xml.core.document.InvalidCharacterException; +import org.eclipse.wst.xml.core.document.XMLGenerator; +import org.eclipse.wst.xml.core.document.XMLModel; +import org.eclipse.wst.xml.core.document.XMLText; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.Text; + + +/** + * TextImpl class + */ +public class TextImpl extends CharacterDataImpl implements XMLText { + + /** + */ + private class StringPair { + private String fFirst = null; + private String fSecond = null; + + StringPair(String first, String second) { + this.fFirst = first; + this.fSecond = second; + } + + String getFirst() { + return this.fFirst; + } + + String getSecond() { + return this.fSecond; + } + } + + private String fSource = null; + + /** + * TextImpl constructor + */ + protected TextImpl() { + super(); + } + + /** + * TextImpl constructor + * + * @param that + * TextImpl + */ + protected TextImpl(TextImpl that) { + super(that); + + if (that != null) { + this.fSource = that.getSource(); + } + } + + /** + * appendData method + * + * @param arg + * java.lang.String + */ + public void appendData(String arg) throws DOMException { + if (arg == null || arg.length() == 0) + return; + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + String newSource = getSource(arg); + if (newSource == null) + return; + String source = getSource(); + if (source != null) + setTextSource(source + newSource); + else + setTextSource(newSource); + } + + /** + */ + IStructuredDocumentRegion appendStructuredDocumentRegion(IStructuredDocumentRegion newStructuredDocumentRegion) { + if (newStructuredDocumentRegion == null) + return null; + + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null) { + setStructuredDocumentRegion(newStructuredDocumentRegion); + return newStructuredDocumentRegion; + } + + if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + container.appendStructuredDocumentRegion(newStructuredDocumentRegion); + } else { + StructuredDocumentRegionContainer container = new StructuredDocumentRegionContainer(); + container.appendStructuredDocumentRegion(flatNode); + container.appendStructuredDocumentRegion(newStructuredDocumentRegion); + setStructuredDocumentRegion(container); + } + + return newStructuredDocumentRegion; + } + + /** + * appendText method + * + * @param text + * org.w3c.dom.Text + */ + public void appendText(Text newText) { + if (newText == null) + return; + + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + TextImpl text = (TextImpl) newText; + String newSource = text.getSource(); + if (newSource == null && newSource.length() == 0) + return; + String source = getSource(); + if (source != null) + setTextSource(source + newSource); + else + setTextSource(newSource); + } + + /** + * cloneNode method + * + * @return org.w3c.dom.Node + * @param deep + * boolean + */ + public Node cloneNode(boolean deep) { + TextImpl cloned = new TextImpl(this); + return cloned; + } + + /** + * deleteData method + * + * @param offset + * int + * @param count + * int + */ + public void deleteData(int offset, int count) throws DOMException { + if (count == 0) + return; + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + if (count < 0 || offset < 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + String source = getSource(); + if (source == null || source.length() == 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + StringPair pair = substringSourceExcluded(source, offset, count); + if (pair == null) + return; + source = null; + String first = pair.getFirst(); + if (first != null) + source = first; + String second = pair.getSecond(); + if (second != null) { + if (source != null) + source += second; + else + source = second; + } + if (source == null) + source = new String(); // delete all + setTextSource(source); + } + + /** + * getData method + * + * @return java.lang.String + */ + public String getData() throws DOMException { + if (this.fSource != null) + return getData(this.fSource); + String data = super.getData(); + if (data != null) + return data; + return getData(getStructuredDocumentRegion()); + } + + /** + */ + private String getData(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return new String(); + + if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + int length = container.getLength(); + if (length < 16) + length = 16; // default + StringBuffer buffer = new StringBuffer(length); + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + String data = getData(content); + if (data == null) + continue; + buffer.append(data); + } + return buffer.toString(); + } + + if (flatNode instanceof StructuredDocumentRegionProxy) { + return flatNode.getText(); + } + + ITextRegion region = StructuredDocumentRegionUtil.getFirstRegion(flatNode); + if (region != null) { + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_ENTITY_REFERENCE || regionType == XMLRegionContext.XML_CHAR_REFERENCE) { + String name = StructuredDocumentRegionUtil.getEntityRefName(flatNode, region); + if (name != null) { + DocumentImpl document = (DocumentImpl) getOwnerDocument(); + if (document != null) { + String value = document.getCharValue(name); + if (value != null) + return value; + } + } + } + } + + return flatNode.getText(); + } + + /** + * Returns data for the source + */ + private String getData(String source) { + if (source == null) + return null; + StringBuffer buffer = null; + int offset = 0; + int length = source.length(); + int ref = source.indexOf('&'); + while (ref >= 0) { + int end = source.indexOf(';', ref + 1); + if (end > ref + 1) { + String name = source.substring(ref + 1, end); + String value = getCharValue(name); + if (value != null) { + if (buffer == null) + buffer = new StringBuffer(length); + if (ref > offset) + buffer.append(source.substring(offset, ref)); + buffer.append(value); + offset = end + 1; + ref = end; + } + } + ref = source.indexOf('&', ref + 1); + } + if (buffer == null) + return source; + if (length > offset) + buffer.append(source.substring(offset)); + return buffer.toString(); + } + + /** + * getFirstStructuredDocumentRegion method + * + * @return com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + public IStructuredDocumentRegion getFirstStructuredDocumentRegion() { + return StructuredDocumentRegionUtil.getFirstStructuredDocumentRegion(getStructuredDocumentRegion()); + } + + /** + * getLastStructuredDocumentRegion method + * + * @return com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + public IStructuredDocumentRegion getLastStructuredDocumentRegion() { + return StructuredDocumentRegionUtil.getLastStructuredDocumentRegion(getStructuredDocumentRegion()); + } + + /** + * getNodeName method + * + * @return java.lang.String + */ + public String getNodeName() { + return "#text";//$NON-NLS-1$ + } + + /** + * getNodeType method + * + * @return short + */ + public short getNodeType() { + return TEXT_NODE; + } + + /** + */ + public String getSource() { + if (this.fSource != null) + return this.fSource; + String data = super.getData(); + if (data != null && data.length() > 0) { + String source = getSource(data); + if (source != null) + return source; + } + return super.getSource(); + } + + /** + * Returns source for the data + */ + private String getSource(String data) { + if (data == null) + return null; + XMLModel model = getModel(); + if (model == null) + return null; // error + XMLGenerator generator = model.getGenerator(); + if (generator == null) + return null; // error + return generator.generateTextData(this, data); + } + + /** + */ + String getTextSource() { + return this.fSource; + } + + /** + */ + public String getValueSource() { + return getSource(); + } + + /** + */ + boolean hasStructuredDocumentRegion(IStructuredDocumentRegion askedStructuredDocumentRegion) { + if (askedStructuredDocumentRegion == null) + return false; + + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null) + return false; + + if (flatNode == askedStructuredDocumentRegion) + return true; + + if (flatNode instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) flatNode; + if (proxy.getStructuredDocumentRegion() == askedStructuredDocumentRegion) + return true; + return false; + } + + if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (content == null) + continue; + if (content == askedStructuredDocumentRegion) + return true; + if (content instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) content; + if (proxy.getStructuredDocumentRegion() == askedStructuredDocumentRegion) + return true; + } + } + return false; + } + + return false; + } + + /** + * insertData method + * + * @param offset + * int + * @param arg + * java.lang.String + */ + public void insertData(int offset, String arg) throws DOMException { + if (arg == null || arg.length() == 0) + return; + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + if (offset < 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + String source = getSource(); + if (source == null || source.length() == 0) { + if (offset > 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + source = getSource(arg); + if (source != null) + setTextSource(source); + return; + } + + StringPair pair = substringSourceExcluded(source, offset, 0); + if (pair == null) + return; // error + StringBuffer buffer = new StringBuffer(source.length() + arg.length()); + String first = pair.getFirst(); + if (first != null) + buffer.append(first); + source = getSource(arg); + if (source != null) + buffer.append(source); + String second = pair.getSecond(); + if (second != null) + buffer.append(second); + setTextSource(buffer.toString()); + } + + /** + */ + IStructuredDocumentRegion insertStructuredDocumentRegion(IStructuredDocumentRegion newStructuredDocumentRegion, IStructuredDocumentRegion nextStructuredDocumentRegion) { + if (newStructuredDocumentRegion == null) + return null; + if (nextStructuredDocumentRegion == null) + return appendStructuredDocumentRegion(newStructuredDocumentRegion); + + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null) + return null; // error + + if (flatNode == nextStructuredDocumentRegion) { + StructuredDocumentRegionContainer container = new StructuredDocumentRegionContainer(); + container.appendStructuredDocumentRegion(newStructuredDocumentRegion); + container.appendStructuredDocumentRegion(flatNode); + setStructuredDocumentRegion(container); + return newStructuredDocumentRegion; + } + + if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (content == nextStructuredDocumentRegion) { + container.insertStructuredDocumentRegion(newStructuredDocumentRegion, i); + return newStructuredDocumentRegion; + } + } + return null; // error + } + + return null; // error + } + + /** + * insertText method + * + * @param text + * org.w3c.dom.Text + * @param offset + * int + */ + public void insertText(Text newText, int offset) throws DOMException { + if (newText == null) + return; + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + TextImpl text = (TextImpl) newText; + String newSource = text.getSource(); + if (newSource == null && newSource.length() == 0) + return; + if (offset < 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + String source = getSource(); + if (source == null || source.length() == 0) { + if (offset > 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + setTextSource(newSource); + return; + } + + StringPair pair = substringSourceExcluded(source, offset, 0); + if (pair == null) + return; // error + StringBuffer buffer = new StringBuffer(source.length() + newSource.length()); + String first = pair.getFirst(); + if (first != null) + buffer.append(first); + buffer.append(newSource); + String second = pair.getSecond(); + if (second != null) + buffer.append(second); + setTextSource(buffer.toString()); + } + + /** + * isCDATAContent method + * + * @return boolean + */ + public boolean isCDATAContent() { + Node parent = getParentNode(); + if (parent == null || parent.getNodeType() != Node.ELEMENT_NODE) + return false; + ElementImpl element = (ElementImpl) parent; + return element.isCDATAContainer(); + } + + /** + */ + public boolean isInvalid() { + return isInvalid(getStructuredDocumentRegion()); + } + + /** + */ + private boolean isInvalid(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return false; + + if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (isInvalid(content)) + return true; + } + return false; + } + + if (flatNode instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) flatNode; + return isInvalid(proxy.getStructuredDocumentRegion()); + } + + String regionType = StructuredDocumentRegionUtil.getFirstRegionType(flatNode); + if (regionType != XMLRegionContext.XML_CONTENT && regionType != JSP_CONTENT && regionType != XMLRegionContext.XML_ENTITY_REFERENCE && regionType != XMLRegionContext.XML_CHAR_REFERENCE && regionType != XMLRegionContext.BLOCK_TEXT && regionType != XMLRegionContext.WHITE_SPACE) { + return true; + } + + return false; + } + + /** + */ + boolean isSharingStructuredDocumentRegion(IStructuredDocumentRegion sharedStructuredDocumentRegion) { + if (sharedStructuredDocumentRegion == null) + return false; + + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null) + return false; + + if (flatNode == sharedStructuredDocumentRegion) + return false; + + if (flatNode instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) flatNode; + if (proxy.getStructuredDocumentRegion() == sharedStructuredDocumentRegion) + return true; + return false; + } + + if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (content == null) + continue; + if (content == sharedStructuredDocumentRegion) + return false; + if (content instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) content; + if (proxy.getStructuredDocumentRegion() == sharedStructuredDocumentRegion) + return true; + } + } + return false; + } + + return false; + } + + /** + */ + public boolean isWhitespace() { + String data = getData(); + if (data == null) + return true; + int length = data.length(); + for (int i = 0; i < length; i++) { + if (!Character.isWhitespace(data.charAt(i))) + return false; + } + return true; + } + + /** + */ + IStructuredDocumentRegion removeStructuredDocumentRegion(IStructuredDocumentRegion oldStructuredDocumentRegion) { + if (oldStructuredDocumentRegion == null) + return null; + + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null) + return null; // error + + if (flatNode == oldStructuredDocumentRegion) { + setStructuredDocumentRegion(null); + return oldStructuredDocumentRegion; + } + + if (flatNode instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) flatNode; + if (proxy.getStructuredDocumentRegion() == oldStructuredDocumentRegion) { + // removed with proxy + setStructuredDocumentRegion(null); + return oldStructuredDocumentRegion; + } + return null; // error + } + + if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (content == oldStructuredDocumentRegion) { + container.removeStructuredDocumentRegion(i); + if (container.getStructuredDocumentRegionCount() == 1) { + // get back to single IStructuredDocumentRegion + setStructuredDocumentRegion(container.getStructuredDocumentRegion(0)); + } + return oldStructuredDocumentRegion; + } + + if (content instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) content; + if (proxy.getStructuredDocumentRegion() == oldStructuredDocumentRegion) { + // removed with proxy + container.removeStructuredDocumentRegion(i); + if (container.getStructuredDocumentRegionCount() == 1) { + // get back to single IStructuredDocumentRegion + setStructuredDocumentRegion(container.getStructuredDocumentRegion(0)); + } + return oldStructuredDocumentRegion; + } + } + } + return null; // error + } + + return null; // error + } + + /** + * replaceData method + * + * @param offset + * int + * @param count + * int + * @param arg + * java.lang.String + */ + public void replaceData(int offset, int count, String arg) throws DOMException { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + if (arg == null || arg.length() == 0) { + deleteData(offset, count); + return; + } + if (count == 0) { + insertData(offset, arg); + return; + } + if (offset < 0 || count < 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + String source = getSource(); + if (source == null || source.length() == 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + StringPair pair = substringSourceExcluded(source, offset, count); + if (pair == null) + return; // error + StringBuffer buffer = new StringBuffer(source.length() + arg.length()); + String first = pair.getFirst(); + if (first != null) + buffer.append(first); + source = getSource(arg); + if (source != null) + buffer.append(source); + String second = pair.getSecond(); + if (second != null) + buffer.append(second); + setTextSource(buffer.toString()); + } + + /** + */ + IStructuredDocumentRegion replaceStructuredDocumentRegion(IStructuredDocumentRegion newStructuredDocumentRegion, IStructuredDocumentRegion oldStructuredDocumentRegion) { + if (oldStructuredDocumentRegion == null) + return null; + if (newStructuredDocumentRegion == null) + return removeStructuredDocumentRegion(oldStructuredDocumentRegion); + + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null) + return null; // error + + if (flatNode == oldStructuredDocumentRegion) { + setStructuredDocumentRegion(newStructuredDocumentRegion); + return oldStructuredDocumentRegion; + } + + if (flatNode instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) flatNode; + if (proxy.getStructuredDocumentRegion() == oldStructuredDocumentRegion) { + if (newStructuredDocumentRegion instanceof StructuredDocumentRegionProxy) { + // proxy must not be nested + setStructuredDocumentRegion(newStructuredDocumentRegion); + } else { + proxy.setStructuredDocumentRegion(newStructuredDocumentRegion); + } + return oldStructuredDocumentRegion; + } + return null; // error + } + + if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (content == null) + continue; // error + if (content == oldStructuredDocumentRegion) { + container.replaceStructuredDocumentRegion(newStructuredDocumentRegion, i); + return oldStructuredDocumentRegion; + } + + if (content instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) content; + if (proxy.getStructuredDocumentRegion() == oldStructuredDocumentRegion) { + if (newStructuredDocumentRegion instanceof StructuredDocumentRegionProxy) { + // proxy must not be nested + container.replaceStructuredDocumentRegion(newStructuredDocumentRegion, i); + } else { + proxy.setStructuredDocumentRegion(newStructuredDocumentRegion); + } + return oldStructuredDocumentRegion; + } + } + } + return null; // error + } + + return null; // error + } + + /** + */ + void resetStructuredDocumentRegions() { + String source = getSource(); + if (source != null && source.length() > 0) + this.fSource = source; + super.resetStructuredDocumentRegions(); + } + + /** + * getData method + * + * @return java.lang.String + */ + public void setData(String data) throws DOMException { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.fSource = null; + super.setData(data); + } + + /** + */ + public void setSource(String source) throws InvalidCharacterException { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + SourceValidator validator = new SourceValidator(this); + if (validator.validateSource(source)) + setTextSource(source); + } + + /** + */ + void setStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + super.setStructuredDocumentRegion(flatNode); + if (flatNode != null) + this.fSource = null; + } + + /** + */ + public void setTextSource(String source) { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + this.fSource = source; + + notifyValueChanged(); + } + + /** + */ + public void setValueSource(String source) { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + + SourceValidator validator = new SourceValidator(this); + setTextSource(validator.convertSource(source)); + } + + /** + * splitText method + * + * @return org.w3c.dom.Text + * @param offset + * int + */ + public Text splitText(int offset) throws DOMException { + if (!isDataEditable()) { + throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, new String()); + } + if (offset < 0) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + int length = getLength(); + if (offset > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + Document document = getOwnerDocument(); + if (document == null) + return null; + + String source = null; + if (offset < length) { + int count = length - offset; + source = substringSource(offset, count); + deleteData(offset, count); + } + TextImpl text = (TextImpl) document.createTextNode(null); + if (source != null) + text.setTextSource(source); + + Node parent = getParentNode(); + if (parent != null) + parent.insertBefore(text, getNextSibling()); + + return text; + } + + /** + */ + Text splitText(IStructuredDocumentRegion nextStructuredDocumentRegion) { + if (nextStructuredDocumentRegion == null) + return null; + + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode == null || !(flatNode instanceof StructuredDocumentRegionContainer)) + return null; // error + + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + int count = container.getStructuredDocumentRegionCount(); + int index = 0; + for (; index < count; index++) { + if (container.getStructuredDocumentRegion(index) == nextStructuredDocumentRegion) + break; + } + if (index >= count) { + // this is the case nextStructuredDocumentRegion is a new + // IStructuredDocumentRegion + // search gap by offset + int offset = nextStructuredDocumentRegion.getStart(); + for (index = 0; index < count; index++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(index); + if (content == null) + continue; // error + if (content.getStart() >= offset) + break; + } + if (index >= count) + return null; // error + } + if (index == 0) + return this; // nothing to do + + Document document = getOwnerDocument(); + if (document == null) + return null; // error + Node parent = getParentNode(); + if (parent == null) + return null; // error + TextImpl nextText = (TextImpl) document.createTextNode(null); + if (nextText == null) + return null; // error + + for (; index < count; count--) { + nextText.appendStructuredDocumentRegion(container.removeStructuredDocumentRegion(index)); + } + + // normalize IStructuredDocumentRegion + if (index == 1) { + setStructuredDocumentRegion(container.getStructuredDocumentRegion(0)); + } + + parent.insertBefore(nextText, getNextSibling()); + return nextText; + } + + /** + * Retruns data for the range + */ + private String substringData(String data, int offset, int count) throws DOMException { + // sure offset and count are non-negative + if (count == 0) + return new String(); + if (data == null) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + int length = data.length(); + if (offset > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + int end = offset + count; + if (end > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + return data.substring(offset, end); + } + + /** + * Returns source for the range specified by: offset: data offset count: + * data count + */ + private String substringSource(int offset, int count) throws DOMException { + // sure offset and count are non-negative + if (this.fSource != null) + return substringSource(this.fSource, offset, count); + + String data = super.getData(); + if (data != null && data.length() > 0) { + data = substringData(data, offset, count); + if (data == null) + return new String(); + String source = getSource(data); + if (source != null) + return source; + } + + return substringSource(getSource(), offset, count); + } + + /** + * Returns source for the range specified by: offset: data offset count: + * data count + */ + private String substringSource(String source, int offset, int count) throws DOMException { + // sure offset and count are non-negative + if (count == 0) + return new String(); + if (source == null) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + int length = source.length(); + int end = offset + count; + + // find character reference + int ref = source.indexOf('&'); + while (ref >= 0) { + if (ref >= end) + break; + int refEnd = source.indexOf(';', ref + 1); + if (refEnd > ref + 1) { + String name = source.substring(ref + 1, refEnd); + if (getCharValue(name) != null) { + // found, shift for source offsets + int refCount = refEnd - ref; + if (ref < offset) + offset += refCount; + if (ref < end) + end += refCount; + ref = refEnd; + } + } + ref = source.indexOf('&', ref + 1); + } + + if (offset > length || end > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + return source.substring(offset, end); + } + + /** + * Returns sources before and after the range specified by: offset: data + * offset count: data count + */ + private StringPair substringSourceExcluded(String source, int offset, int count) throws DOMException { + // sure offset and count are non-negative + if (source == null) { + if (offset == 0 && count == 0) + return new StringPair(null, null); + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + int length = source.length(); + int end = offset + count; + + // find character reference + int ref = source.indexOf('&'); + while (ref >= 0) { + if (ref >= end) + break; + int refEnd = source.indexOf(';', ref + 1); + if (refEnd > ref + 1) { + String name = source.substring(ref + 1, refEnd); + if (getCharValue(name) != null) { + // found, shift for source offsets + int refCount = refEnd - ref; + if (ref < offset) + offset += refCount; + if (ref < end) + end += refCount; + ref = refEnd; + } + } + ref = source.indexOf('&', ref + 1); + } + + if (offset > length || end > length) { + throw new DOMException(DOMException.INDEX_SIZE_ERR, new String()); + } + + String first = (offset > 0 ? source.substring(0, offset) : null); + String second = (end < length ? source.substring(end, length) : null); + return new StringPair(first, second); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLGeneratorImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLGeneratorImpl.java new file mode 100644 index 0000000000..5000a4d009 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLGeneratorImpl.java @@ -0,0 +1,738 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.eclipse.wst.common.contentmodel.CMAttributeDeclaration; +import org.eclipse.wst.common.contentmodel.CMDataType; +import org.eclipse.wst.xml.core.commentelement.impl.CommentElementRegistry; +import org.eclipse.wst.xml.core.document.DocumentTypeAdapter; +import org.eclipse.wst.xml.core.document.JSPTag; +import org.eclipse.wst.xml.core.document.TagAdapter; +import org.eclipse.wst.xml.core.document.XMLAttr; +import org.eclipse.wst.xml.core.document.XMLCharEntity; +import org.eclipse.wst.xml.core.document.XMLDocument; +import org.eclipse.wst.xml.core.document.XMLElement; +import org.eclipse.wst.xml.core.document.XMLGenerator; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.w3c.dom.Attr; +import org.w3c.dom.CDATASection; +import org.w3c.dom.Comment; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.w3c.dom.EntityReference; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.ProcessingInstruction; +import org.w3c.dom.Text; + + +/** + */ +public class XMLGeneratorImpl implements XMLGenerator { + private static final String CDATA_CLOSE = "]]>";//$NON-NLS-1$ + private static final String CDATA_OPEN = "<![CDATA[";//$NON-NLS-1$ + private static final String COMMENT_CLOSE = "-->";//$NON-NLS-1$ + private static final String COMMENT_OPEN = "<!--";//$NON-NLS-1$ + private static final String DOCTYPE_OPEN = "<!DOCTYPE";//$NON-NLS-1$ + private static final String EMPTY_CLOSE = " />";//$NON-NLS-1$ + private static final String END_OPEN = "</";//$NON-NLS-1$ + + private static XMLGeneratorImpl instance = null; + private static final String PI_CLOSE = "?>";//$NON-NLS-1$ + private static final String PI_OPEN = "<?";//$NON-NLS-1$ + private static final String PUBLIC_ID = "PUBLIC";//$NON-NLS-1$ + private static final String SSI_PREFIX = "ssi";//$NON-NLS-1$ + //private static final String SSI_FEATURE = "SSI";//$NON-NLS-1$ + private static final String SSI_TOKEN = "#";//$NON-NLS-1$ + private static final String SYSTEM_ID = "SYSTEM";//$NON-NLS-1$ + private static final String TAG_CLOSE = ">";//$NON-NLS-1$ + + /** + */ + public synchronized static XMLGenerator getInstance() { + if (instance == null) + instance = new XMLGeneratorImpl(); + return instance; + } + + /** + */ + //private boolean isCommentTag(XMLElement element) { + // if (element == null) return false; + // DocumentImpl document = (DocumentImpl)element.getOwnerDocument(); + // if (document == null) return false; + // DocumentTypeAdapter adapter = document.getDocumentTypeAdapter(); + // if (adapter == null) return false; + // if (!adapter.hasFeature(SSI_FEATURE)) return false; + // String prefix = element.getPrefix(); + // return (prefix != null && prefix.equals(SSI_PREFIX)); + //} + /** + * Helper to modify the tag name in sub-classes + */ + private static void setTagName(Element element, String tagName) { + if (element == null || tagName == null) + return; + ((ElementImpl) element).setTagName(tagName); + } + + /** + * XMLModelGenerator constructor + */ + private XMLGeneratorImpl() { + super(); + } + + /** + */ + public String generateAttrName(Attr attr) { + if (attr == null) + return null; + String attrName = attr.getName(); + if (attrName == null) + return null; + if (attrName.startsWith(JSPTag.TAG_OPEN)) { + if (!attrName.endsWith(JSPTag.TAG_CLOSE)) { + // close JSP + return (attrName + JSPTag.TAG_CLOSE); + } + } + if (((XMLAttr) attr).isGlobalAttr() && CMNodeUtil.getAttributeDeclaration(attr) != null) { + switch (getAttrNameCase(attr)) { + case DocumentTypeAdapter.UPPER_CASE : + attrName = attrName.toUpperCase(); + break; + case DocumentTypeAdapter.LOWER_CASE : + attrName = attrName.toLowerCase(); + break; + default : + // ASIS_CASE + break; + } + } + return attrName; + } + + /** + */ + public String generateAttrValue(Attr attr) { + return generateAttrValue(attr, (char) 0); // no quote preference + } + + /** + */ + public String generateAttrValue(Attr attr, char quote) { + if (attr == null) + return null; + String name = attr.getName(); + SourceValidator validator = new SourceValidator(attr); + String value = validator.convertSource(((XMLNode) attr).getValueSource()); + if (value == null || value.length() == 0) { + if (name != null && name.startsWith(JSPTag.TAG_OPEN)) + return null; + if (isBooleanAttr(attr)) { + if (((AttrImpl) attr).isXMLAttr()) { + // generate the name as value + value = attr.getName(); + } else { + // not to generate '=' and value for HTML boolean + return null; + } + } + } + return generateAttrValue(value, quote); + } + + /** + */ + public String generateAttrValue(String value, char quote) { + // assume the valid is already validated not to include both quotes + if (quote == '"') { + if ((value != null) && (value.indexOf('"') >= 0)) + quote = '\''; // force + } else if (quote == '\'') { + if ((value != null) && (value.indexOf('\'') >= 0)) + quote = '"'; // force + } else { // no preference + if ((value != null) && (value.indexOf('"') < 0)) + quote = '"'; + else + quote = '\''; + } + + int length = (value == null ? 0 : value.length()); + StringBuffer buffer = new StringBuffer(length + 2); + buffer.append(quote); + if (value != null) + buffer.append(value); + buffer.append(quote); + return buffer.toString(); + } + + /** + * generateCDATASection method + * + * @return java.lang.String + * @param comment + * org.w3c.dom.CDATASection + */ + public String generateCDATASection(CDATASection cdata) { + if (cdata == null) + return null; + + String data = cdata.getData(); + int length = (data != null ? data.length() : 0); + StringBuffer buffer = new StringBuffer(length + 16); + buffer.append(CDATA_OPEN); + if (data != null) + buffer.append(data); + buffer.append(CDATA_CLOSE); + return buffer.toString(); + } + + /** + * generateChild method + * + * @return java.lang.String + * @param org.w3c.dom.Node + */ + public String generateChild(Node parentNode) { + if (parentNode == null) + return null; + if (!parentNode.hasChildNodes()) + return null; + + StringBuffer buffer = new StringBuffer(); + for (Node child = parentNode.getFirstChild(); child != null; child = child.getNextSibling()) { + String childSource = generateSource(child); + if (childSource != null) + buffer.append(childSource); + } + return buffer.toString(); + } + + /** + */ + public String generateCloseTag(Node node) { + if (node == null) + return null; + + switch (node.getNodeType()) { + case Node.ELEMENT_NODE : { + ElementImpl element = (ElementImpl) node; + if (element.isCommentTag()) { + if (element.isJSPTag()) + return JSPTag.COMMENT_CLOSE; + return COMMENT_CLOSE; + } + if (element.isJSPTag()) + return JSPTag.TAG_CLOSE; + if (element.isEmptyTag()) + return EMPTY_CLOSE; + return TAG_CLOSE; + } + case Node.COMMENT_NODE : { + CommentImpl comment = (CommentImpl) node; + if (comment.isJSPTag()) + return JSPTag.COMMENT_CLOSE; + return COMMENT_CLOSE; + } + case Node.DOCUMENT_TYPE_NODE : + return TAG_CLOSE; + case Node.PROCESSING_INSTRUCTION_NODE : + return PI_CLOSE; + case Node.CDATA_SECTION_NODE : + return CDATA_CLOSE; + default : + break; + } + + return null; + } + + /** + * generateComment method + * + * @return java.lang.String + * @param comment + * org.w3c.dom.Comment + */ + public String generateComment(Comment comment) { + if (comment == null) + return null; + + String data = comment.getData(); + int length = (data != null ? data.length() : 0); + StringBuffer buffer = new StringBuffer(length + 8); + CommentImpl impl = (CommentImpl) comment; + if (!impl.isJSPTag()) + buffer.append(COMMENT_OPEN); + else + buffer.append(JSPTag.COMMENT_OPEN); + if (data != null) + buffer.append(data); + if (!impl.isJSPTag()) + buffer.append(COMMENT_CLOSE); + else + buffer.append(JSPTag.COMMENT_CLOSE); + return buffer.toString(); + } + + /** + * generateDoctype method + * + * @return java.lang.String + * @param docType + * org.w3c.dom.DocumentType + */ + public String generateDoctype(DocumentType docType) { + if (docType == null) + return null; + + String name = docType.getName(); + int length = (name != null ? name.length() : 0); + StringBuffer buffer = new StringBuffer(length + 16); + buffer.append(DOCTYPE_OPEN); + buffer.append(' '); + if (name != null) + buffer.append(name); + DocumentTypeImpl dt = (DocumentTypeImpl) docType; + String publicID = dt.getPublicId(); + String systemID = dt.getSystemId(); + if (publicID != null) { + buffer.append(' '); + buffer.append(PUBLIC_ID); + buffer.append(' '); + buffer.append('"'); + buffer.append(publicID); + buffer.append('"'); + if (systemID != null) { + buffer.append(' '); + buffer.append('"'); + buffer.append(systemID); + buffer.append('"'); + } + } else { + if (systemID != null) { + buffer.append(' '); + buffer.append(SYSTEM_ID); + buffer.append(' '); + buffer.append('"'); + buffer.append(systemID); + buffer.append('"'); + } + } + buffer.append('>'); + return buffer.toString(); + } + + /** + * generateElement method + * + * @return java.lang.String + * @param element + * Element + */ + public String generateElement(Element element) { + if (element == null) + return null; + + // if empty tag is preferrable, generate as empty tag + ElementImpl impl = (ElementImpl) element; + if (impl.preferEmptyTag()) + impl.setEmptyTag(true); + + StringBuffer buffer = new StringBuffer(); + String startTag = generateStartTag(element); + if (startTag != null) + buffer.append(startTag); + String child = generateChild(element); + if (child != null) + buffer.append(child); + String endTag = generateEndTag(element); + if (endTag != null) + buffer.append(endTag); + return buffer.toString(); + } + + /** + * generateEndTag method + * + * @return java.lang.String + * @param element + * org.w3c.dom.Element + */ + public String generateEndTag(Element element) { + if (element == null) + return null; + + ElementImpl impl = (ElementImpl) element; + + // first check if tag adapter exists + TagAdapter adapter = (TagAdapter) impl.getExistingAdapter(TagAdapter.class); + if (adapter != null) { + String endTag = adapter.getEndTag(impl); + if (endTag != null) + return endTag; + } + + if (impl.isEmptyTag()) + return null; + if (!impl.isContainer()) + return null; + if (impl.isJSPTag()) + return JSPTag.TAG_CLOSE; + + String tagName = generateTagName(element); + int length = (tagName != null ? tagName.length() : 0); + StringBuffer buffer = new StringBuffer(length + 4); + buffer.append(END_OPEN); + if (tagName != null) + buffer.append(tagName); + buffer.append('>'); + return buffer.toString(); + } + + /** + * generateEntityRef method + * + * @return java.lang.String + * @param entityRef + * org.w3c.dom.EntityReference + */ + public String generateEntityRef(EntityReference entityRef) { + if (entityRef == null) + return null; + + String name = entityRef.getNodeName(); + int length = (name != null ? name.length() : 0); + StringBuffer buffer = new StringBuffer(length + 4); + buffer.append('&'); + if (name != null) + buffer.append(name); + buffer.append(';'); + return buffer.toString(); + } + + /** + * generatePI method + * + * @return java.lang.String + * @param pi + * org.w3c.dom.ProcessingInstruction + */ + public String generatePI(ProcessingInstruction pi) { + if (pi == null) + return null; + + String target = pi.getTarget(); + String data = pi.getData(); + int length = (target != null ? target.length() : 0); + if (data != null) + length += data.length(); + StringBuffer buffer = new StringBuffer(length + 8); + buffer.append(PI_OPEN); + if (target != null) + buffer.append(target); + buffer.append(' '); + if (data != null) + buffer.append(data); + buffer.append(PI_CLOSE); + return buffer.toString(); + } + + /** + * generateSource method + * + * @return java.lang.String + * @param node + * org.w3c.dom.Node + */ + public String generateSource(Node node) { + switch (node.getNodeType()) { + case Node.ELEMENT_NODE : + return generateElement((Element) node); + case Node.TEXT_NODE : + return generateText((Text) node); + case Node.COMMENT_NODE : + return generateComment((Comment) node); + case Node.DOCUMENT_TYPE_NODE : + return generateDoctype((DocumentType) node); + case Node.PROCESSING_INSTRUCTION_NODE : + return generatePI((ProcessingInstruction) node); + case Node.CDATA_SECTION_NODE : + return generateCDATASection((CDATASection) node); + case Node.ENTITY_REFERENCE_NODE : + return generateEntityRef((EntityReference) node); + default : + // DOCUMENT + break; + } + return generateChild(node); + } + + /** + * generateStartTag method + * + * @return java.lang.String + * @param element + * Element + */ + public String generateStartTag(Element element) { + if (element == null) + return null; + + ElementImpl impl = (ElementImpl) element; + + if (impl.isJSPTag()) { + // check if JSP content type and JSP Document + XMLDocument document = (XMLDocument) element.getOwnerDocument(); + if (document != null && document.isJSPType()) { + if (document.isJSPDocument() && !impl.hasChildNodes()) { + impl.setJSPTag(false); + } + } else { + impl.setJSPTag(false); + } + } + if (impl.isCommentTag() && impl.getExistingAdapter(TagAdapter.class) == null) { + CommentElementRegistry registry = CommentElementRegistry.getInstance(); + registry.setupCommentElement(impl); + } + + // first check if tag adapter exists + TagAdapter adapter = (TagAdapter) impl.getExistingAdapter(TagAdapter.class); + if (adapter != null) { + String startTag = adapter.getStartTag(impl); + if (startTag != null) + return startTag; + } + + StringBuffer buffer = new StringBuffer(); + + if (impl.isCommentTag()) { + if (impl.isJSPTag()) + buffer.append(JSPTag.COMMENT_OPEN); + else + buffer.append(COMMENT_OPEN); + String tagName = generateTagName(element); + if (tagName != null) + buffer.append(tagName); + } else if (impl.isJSPTag()) { + buffer.append(JSPTag.TAG_OPEN); + String tagName = generateTagName(element); + if (tagName != null) + buffer.append(tagName); + if (impl.isContainer()) + return buffer.toString(); // JSP container + } else { + buffer.append('<'); + String tagName = generateTagName(element); + if (tagName != null) + buffer.append(tagName); + } + + NamedNodeMap attributes = element.getAttributes(); + int length = attributes.getLength(); + for (int i = 0; i < length; i++) { + AttrImpl attr = (AttrImpl) attributes.item(i); + if (attr == null) + continue; + buffer.append(' '); + String attrName = generateAttrName(attr); + if (attrName != null) + buffer.append(attrName); + String attrValue = generateAttrValue(attr); + if (attrValue != null) { + // attr name only for HTML boolean and JSP + buffer.append('='); + buffer.append(attrValue); + } + } + + String closeTag = generateCloseTag(element); + if (closeTag != null) + buffer.append(closeTag); + + return buffer.toString(); + } + + /** + */ + public String generateTagName(Element element) { + if (element == null) + return null; + XMLElement xe = (XMLElement) element; + String tagName = element.getTagName(); + if (tagName == null) + return null; + if (xe.isJSPTag()) { + if (tagName.equals(JSPTag.JSP_EXPRESSION)) + return JSPTag.EXPRESSION_TOKEN; + if (tagName.equals(JSPTag.JSP_DECLARATION)) + return JSPTag.DECLARATION_TOKEN; + if (tagName.equals(JSPTag.JSP_DIRECTIVE)) + return JSPTag.DIRECTIVE_TOKEN; + if (tagName.startsWith(JSPTag.JSP_DIRECTIVE)) { + int offset = JSPTag.JSP_DIRECTIVE.length() + 1; // after '.' + return (JSPTag.DIRECTIVE_TOKEN + tagName.substring(offset)); + } + return (xe.isCommentTag()) ? tagName : null; + } else if (tagName.startsWith(JSPTag.TAG_OPEN)) { + if (!tagName.endsWith(JSPTag.TAG_CLOSE)) { + // close JSP + return (tagName + JSPTag.TAG_CLOSE); + } + } else if (xe.isCommentTag()) { + String prefix = element.getPrefix(); + if (prefix.equals(SSI_PREFIX)) { + return (SSI_TOKEN + element.getLocalName()); + } + } else { + if (!xe.isJSPTag() && xe.isGlobalTag() && // global tag + CMNodeUtil.getElementDeclaration(xe) != null) { + String newName = tagName; + switch (getTagNameCase(xe)) { + case DocumentTypeAdapter.UPPER_CASE : + newName = tagName.toUpperCase(); + break; + case DocumentTypeAdapter.LOWER_CASE : + newName = tagName.toLowerCase(); + break; + } + if (newName != tagName) { + tagName = newName; + setTagName(element, tagName); + } + } + } + return tagName; + } + + /** + * generateText method + * + * @return java.lang.String + * @param text + * org.w3c.dom.Text + */ + public String generateText(Text text) { + if (text == null) + return null; + TextImpl impl = (TextImpl) text; + String source = impl.getTextSource(); + if (source != null) + return source; + return generateTextData(text, impl.getData()); + } + + /** + */ + public String generateTextData(Text text, String data) { + if (data == null) + return null; + if (text == null) + return null; + TextImpl impl = (TextImpl) text; + if (impl.isJSPContent() || impl.isCDATAContent()) { + return new SourceValidator(impl).convertSource(data); + } + String source = data; + + // convert special characters to character entities + StringBuffer buffer = null; + int offset = 0; + int length = data.length(); + for (int i = 0; i < length; i++) { + String name = getCharName(data.charAt(i)); + if (name == null) + continue; + if (buffer == null) + buffer = new StringBuffer(length + 8); + if (i > offset) + buffer.append(data.substring(offset, i)); + buffer.append('&'); + buffer.append(name); + buffer.append(';'); + offset = i + 1; + } + if (buffer != null) { + if (length > offset) + buffer.append(data.substring(offset)); + source = buffer.toString(); + } + + if (source == null || source.length() == 0) + return null; + return source; + } + + /** + */ + private int getAttrNameCase(Attr attr) { + DocumentImpl document = (DocumentImpl) attr.getOwnerDocument(); + if (document == null) + return DocumentTypeAdapter.STRICT_CASE; + DocumentTypeAdapter adapter = document.getDocumentTypeAdapter(); + if (adapter == null) + return DocumentTypeAdapter.STRICT_CASE; + return adapter.getAttrNameCase(); + } + + /** + */ + private String getCharName(char c) { + switch (c) { + case '<' : + return XMLCharEntity.LT_NAME; + case '>' : + return XMLCharEntity.GT_NAME; + case '&' : + return XMLCharEntity.AMP_NAME; + case '"' : + return XMLCharEntity.QUOT_NAME; + } + return null; + } + + /** + */ + private int getTagNameCase(Element element) { + DocumentImpl document = (DocumentImpl) element.getOwnerDocument(); + if (document == null) + return DocumentTypeAdapter.STRICT_CASE; + DocumentTypeAdapter adapter = document.getDocumentTypeAdapter(); + if (adapter == null) + return DocumentTypeAdapter.STRICT_CASE; + return adapter.getTagNameCase(); + } + + /** + */ + private boolean isBooleanAttr(Attr attr) { + if (attr == null) + return false; + CMAttributeDeclaration decl = CMNodeUtil.getAttributeDeclaration(attr); + if (decl == null) + return false; + CMDataType type = decl.getAttrType(); + if (type == null) + return false; + String values[] = type.getEnumeratedValues(); + if (values == null) + return false; + return (values.length == 1 && values[0].equals(decl.getAttrName())); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelContext.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelContext.java new file mode 100644 index 0000000000..6a799f75f4 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelContext.java @@ -0,0 +1,237 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.Text; + +/** + * XMLModelContext class + */ +class XMLModelContext { + private Node nextNode = null; + private Node parentNode = null; + + // private XMLModelImpl model = null; + private Node rootNode = null; + + /** + * XMLModelContext constructor + * + * @param rootNode + * org.w3c.dom.Node + */ + XMLModelContext(Node rootNode) { + super(); + + this.rootNode = rootNode; + } + + /** + * findEndTag method + * + * @return org.w3c.dom.Element + * @param tagName + * java.lang.String + */ + Element findEndTag(String tagName) { + if (tagName == null) + return null; + if (this.parentNode == null) + return null; + + for (Node parent = this.parentNode.getParentNode(); parent != null; parent = parent.getParentNode()) { + if (parent.getNodeType() != Node.ELEMENT_NODE) + break; + ElementImpl element = (ElementImpl) parent; + if (element.hasEndTag()) { + if (element.matchTagName(tagName)) + return element; + // if ancestor element has end tag stop search + break; + } + if (element.getNextSibling() != null) + break; + } + + return null; + } + + /** + */ + Text findNextText() { + Node node = this.nextNode; + while (node != null) { + if (node != this.nextNode && node.getNodeType() == Node.TEXT_NODE) { + TextImpl text = (TextImpl) node; + // skip empty text + if (text.getStructuredDocumentRegion() != null) + return text; + } + Node child = node.getFirstChild(); + if (child != null) { + node = child; + continue; + } + while (node != null) { + Node next = node.getNextSibling(); + if (next != null) { + node = next; + break; + } + node = node.getParentNode(); + } + } + return null; + } + + /** + * findPreviousText method + * + * @return org.w3c.dom.Text + */ + Text findPreviousText() { + if (this.parentNode == null) + return null; + Node node = null; + if (this.nextNode != null) + node = this.nextNode.getPreviousSibling(); + else + node = this.parentNode.getLastChild(); + if (node == null || node.getNodeType() != Node.TEXT_NODE) + return null; + return (Text) node; + } + + /** + * findStartTag method + * + * @return org.w3c.dom.Element + * @param tagName + * java.lang.String + */ + Element findStartTag(String tagName, String rootName) { + if (tagName == null) + return null; + + // check previous for empty content element + Node prev = null; + if (this.nextNode != null) + prev = this.nextNode.getPreviousSibling(); + else if (this.parentNode != null) + prev = this.parentNode.getLastChild(); + if (prev != null && prev.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) prev; + if (!element.hasEndTag() && !element.isEmptyTag() && element.matchTagName(tagName)) + return element; + } + + for (Node parent = this.parentNode; parent != null; parent = parent.getParentNode()) { + if (parent.getNodeType() != Node.ELEMENT_NODE) + break; + ElementImpl element = (ElementImpl) parent; + if (element.matchTagName(tagName)) + return element; + if (rootName != null && element.matchTagName(rootName)) + break; + } + + return null; + } + + /** + * getNextNode method + * + * @return org.w3c.dom.Node + */ + Node getNextNode() { + return this.nextNode; + } + + /** + * getParentNode method + * + * @return org.w3c.dom.Node + */ + Node getParentNode() { + return this.parentNode; + } + + /** + * getRootNode method + * + * @return org.w3c.dom.Node + */ + Node getRootNode() { + return this.rootNode; + } + + /** + * setLast method + */ + void setLast() { + if (this.parentNode == null) + return; + if (this.nextNode != null) { + Node prev = this.nextNode.getPreviousSibling(); + if (prev == null || prev.getNodeType() != Node.ELEMENT_NODE) + return; + ElementImpl element = (ElementImpl) prev; + if (element.hasEndTag() || !element.isContainer() || element.isEmptyTag()) + return; + setParentNode(prev); + } + + // find last open parent + Node parent = this.parentNode; + Node last = parent.getLastChild(); + while (last != null) { + if (last.getNodeType() != Node.ELEMENT_NODE) + break; + ElementImpl element = (ElementImpl) last; + if (element.hasEndTag() || !element.isContainer() || element.isEmptyTag()) + break; + parent = element; + last = parent.getLastChild(); + } + if (parent != this.parentNode) + setParentNode(parent); + } + + /** + * setNextNode method + * + * @param nextNode + * org.w3c.dom.Node + */ + void setNextNode(Node nextNode) { + this.nextNode = nextNode; + if (nextNode == null) + return; + this.parentNode = nextNode.getParentNode(); + } + + /** + * setParentNode method + * + * @param parentNode + * org.w3c.dom.Node + */ + void setParentNode(Node parentNode) { + this.parentNode = parentNode; + this.nextNode = null; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelImpl.java new file mode 100644 index 0000000000..640a164113 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelImpl.java @@ -0,0 +1,875 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + +import org.eclipse.wst.sse.core.AbstractStructuredModel; +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.sse.core.events.IStructuredDocumentListener; +import org.eclipse.wst.sse.core.events.NewDocumentEvent; +import org.eclipse.wst.sse.core.events.NoChangeEvent; +import org.eclipse.wst.sse.core.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.events.RegionsReplacedEvent; +import org.eclipse.wst.sse.core.events.StructuredDocumentRegionsReplacedEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocument; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.Logger; +import org.eclipse.wst.xml.core.document.XMLDocument; +import org.eclipse.wst.xml.core.document.XMLGenerator; +import org.eclipse.wst.xml.core.document.XMLModel; +import org.eclipse.wst.xml.core.document.XMLModelNotifier; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + + +/** + * XMLModelImpl class + */ +public class XMLModelImpl extends AbstractStructuredModel implements IStructuredDocumentListener, XMLModel, DOMImplementation { + private static String TRACE_PARSER_MANAGEMENT_EXCEPTION = "parserManagement"; //$NON-NLS-1$ + private Object active = null; + protected DocumentImpl document = null; + private XMLGenerator generator = null; + private XMLModelNotifier notifier = null; + private XMLModelParser parser = null; + private boolean refresh = false; + private XMLModelUpdater updater = null; + + /** + * XMLModelImpl constructor + */ + public XMLModelImpl() { + super(); + this.document = (DocumentImpl) internalCreateDocument(); + } + + /** + * This API allows clients to declare that they are about to make a + * "large" change to the model. This change might be in terms of content + * or it might be in terms of the model id or base location. + * + * Note that in the case of embedded calls, notification to listners is + * sent only once. + * + * Note that the client who is making these changes has the responsibility + * to restore the models state once finished with the changes. See + * getMemento and restoreState. + * + * The method isModelStateChanging can be used by a client to determine if + * the model is already in a change sequence. + */ + public void aboutToChangeModel() { + super.aboutToChangeModel(); + // technically, no need to call beginChanging so often, + // since aboutToChangeModel can be nested. + // but will leave as is for this release. + // see modelChanged, and be sure stays coordinated there. + getModelNotifier().beginChanging(); + } + + public void aboutToReinitializeModel() { + XMLModelNotifier notifier = getModelNotifier(); + notifier.cancelPending(); + super.aboutToReinitializeModel(); + } + + /** + * attrReplaced method + * + * @param element + * org.w3c.dom.Element + * @param newAttr + * org.w3c.dom.Attr + * @param oldAttr + * org.w3c.dom.Attr + */ + protected void attrReplaced(Element element, Attr newAttr, Attr oldAttr) { + if (element == null) + return; + if (getActiveParser() == null) { + XMLModelUpdater updater = getModelUpdater(); + setActive(updater); + updater.initialize(); + updater.replaceAttr(element, newAttr, oldAttr); + setActive(null); + } + getModelNotifier().attrReplaced(element, newAttr, oldAttr); + } + + /** + * This API allows a client controlled way of notifying all ModelEvent + * listners that the model has been changed. This method is a matched pair + * to aboutToChangeModel, and must be called after aboutToChangeModel ... + * or some listeners could be left waiting indefinitely for the changed + * event. So, its suggested that changedModel always be in a finally + * clause. Likewise, a client should never call changedModel without + * calling aboutToChangeModel first. + * + * In the case of embedded calls, the notification is just sent once. + * + */ + public void changedModel() { + // NOTE: the order of 'changedModel' and 'endChanging' is significant. + // By calling changedModel first, this basically decrements the + // "isChanging" counter + // in super class and when zero all listeners to model state events + // will be notified + // that the model has been changed. 'endChanging' will notify all + // deferred adapters. + // So, the significance of order is that adapters (and methods they + // call) + // can count on the state of model "isChanging" to be accurate. + // But, remember, that this means the "modelChanged" event can be + // received before all + // adapters have finished their processing. + // NOTE NOTE: The above note is obsolete in fact (though still states + // issue correctly). + // Due to popular demand, the order of these calls were reversed and + // behavior + // changed on 07/22/2004. + // + // see also + // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=4302 + // for motivation for this 'on verge of' call. + // this could be improved in future if notifier also used counting + // flag to avoid nested calls. If/when changed be sure to check if + // aboutToChangeModel needs any changes too. + if (isModelChangeStateOnVergeOfEnding()) { + // end lock before noticiation loop, since directly or indirectly + // we may be "called from foriegn code" during notification. + endLock(); + + // the notifier is what controls adaper notification, which + // should be sent out before the 'modelChanged' event. + getModelNotifier().endChanging(); + } + // changedModel handles 'nesting', so only one event sent out + // when mulitple calls to 'aboutToChange/Changed'. + super.changedModel(); + handleRefresh(); + } + + /** + * childReplaced method + * + * @param parentNode + * org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + * @param oldChild + * org.w3c.dom.Node + */ + protected void childReplaced(Node parentNode, Node newChild, Node oldChild) { + if (parentNode == null) + return; + if (getActiveParser() == null) { + XMLModelUpdater updater = getModelUpdater(); + setActive(updater); + updater.initialize(); + updater.replaceChild(parentNode, newChild, oldChild); + setActive(null); + } + getModelNotifier().childReplaced(parentNode, newChild, oldChild); + } + + /** + * Creates an XML <code>Document</code> object of the specified type + * with its document element. HTML-only DOM implementations do not need to + * implement this method. + * + * @param namespaceURIThe + * namespace URI of the document element to create. + * @param qualifiedNameThe + * qualified name of the document element to be created. + * @param doctypeThe + * type of document to be created or <code>null</code>. When + * <code>doctype</code> is not <code>null</code>, its + * <code>Node.ownerDocument</code> attribute is set to the + * document being created. + * @return A new <code>Document</code> object. + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified qualified + * name contains an illegal character. <br> + * NAMESPACE_ERR: Raised if the <code>qualifiedName</code> + * is malformed, if the <code>qualifiedName</code> has a + * prefix and the <code>namespaceURI</code> is + * <code>null</code>, or if the + * <code>qualifiedName</code> has a prefix that is "xml" + * and the <code>namespaceURI</code> is different from " + * http://www.w3.org/XML/1998/namespace" .<br> + * WRONG_DOCUMENT_ERR: Raised if <code>doctype</code> has + * already been used with a different document or was + * created from a different implementation. + * @since DOM Level 2 + */ + public Document createDocument(String namespaceURI, String qualifiedName, DocumentType doctype) throws DOMException { + return null; + } + + /** + * Creates an empty <code>DocumentType</code> node. Entity declarations + * and notations are not made available. Entity reference expansions and + * default attribute additions do not occur. It is expected that a future + * version of the DOM will provide a way for populating a + * <code>DocumentType</code>.<br> + * HTML-only DOM implementations do not need to implement this method. + * + * @param qualifiedNameThe + * qualified name of the document type to be created. + * @param publicIdThe + * external subset public identifier. + * @param systemIdThe + * external subset system identifier. + * @return A new <code>DocumentType</code> node with + * <code>Node.ownerDocument</code> set to <code>null</code>. + * @exception DOMException + * INVALID_CHARACTER_ERR: Raised if the specified qualified + * name contains an illegal character. <br> + * NAMESPACE_ERR: Raised if the <code>qualifiedName</code> + * is malformed. + * @since DOM Level 2 + */ + public DocumentType createDocumentType(String qualifiedName, String publicId, String systemId) throws DOMException { + DocumentTypeImpl documentType = new DocumentTypeImpl(); + documentType.setName(qualifiedName); + documentType.setPublicId(publicId); + documentType.setSystemId(systemId); + return documentType; + } + + /** + */ + protected void documentTypeChanged() { + if (this.refresh) + return; + // unlike 'resfresh', 'reinitialize' finishes loop + // and flushes remaining notification que before + // actually reinitializing. + // FUTURE: perhaps all "handleRefresh" should be changed + // to "reinit needede"? + this.setReinitializeNeeded(true); + } + + protected void editableChanged(Node node) { + if (node != null) { + getModelNotifier().editableChanged(node); + } + } + + /** + */ + protected void endTagChanged(Element element) { + if (element == null) + return; + if (getActiveParser() == null) { + XMLModelUpdater updater = getModelUpdater(); + setActive(updater); + updater.initialize(); + updater.changeEndTag(element); + setActive(null); + } + getModelNotifier().endTagChanged(element); + } + + /** + */ + private XMLModelParser getActiveParser() { + if (this.parser == null) + return null; + if (this.parser != this.active) + return null; + return this.parser; + } + + /** + */ + private XMLModelUpdater getActiveUpdater() { + if (this.updater == null) + return null; + if (this.updater != this.active) + return null; + return this.updater; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) + */ + public Object getAdapter(Class adapter) { + if (Document.class.equals(adapter)) + return getDocument(); + return super.getAdapter(adapter); + } + + /** + * getDocument method + * + * @return XMLDocument + */ + public XMLDocument getDocument() { + return this.document; + } + + public XMLGenerator getGenerator() { + if (this.generator == null) { + this.generator = XMLGeneratorImpl.getInstance(); + } + return this.generator; + } + + /** + * getNode method + * + * @param offset + * int + */ + public IndexedRegion getIndexedRegion(int offset) { + if (this.document == null) + return null; + // search in document children + XMLNode parent = null; + int length = this.document.getEndOffset(); + if (offset * 2 < length) { + // search from the first + XMLNode child = (XMLNode) this.document.getFirstChild(); + while (child != null) { + if (child.getEndOffset() <= offset) { + child = (XMLNode) child.getNextSibling(); + continue; + } + if (child.getStartOffset() > offset) { + break; + } + IStructuredDocumentRegion startStructuredDocumentRegion = child.getStartStructuredDocumentRegion(); + if (startStructuredDocumentRegion != null) { + if (startStructuredDocumentRegion.getEnd() > offset) + return child; + } + IStructuredDocumentRegion endStructuredDocumentRegion = child.getEndStructuredDocumentRegion(); + if (endStructuredDocumentRegion != null) { + if (endStructuredDocumentRegion.getStart() <= offset) + return child; + } + // dig more + parent = child; + child = (XMLNode) parent.getFirstChild(); + } + } else { + // search from the last + XMLNode child = (XMLNode) this.document.getLastChild(); + while (child != null) { + if (child.getStartOffset() > offset) { + child = (XMLNode) child.getPreviousSibling(); + continue; + } + if (child.getEndOffset() <= offset) { + break; + } + IStructuredDocumentRegion startStructuredDocumentRegion = child.getStartStructuredDocumentRegion(); + if (startStructuredDocumentRegion != null) { + if (startStructuredDocumentRegion.getEnd() > offset) + return child; + } + IStructuredDocumentRegion endStructuredDocumentRegion = child.getEndStructuredDocumentRegion(); + if (endStructuredDocumentRegion != null) { + if (endStructuredDocumentRegion.getStart() <= offset) + return child; + } + // dig more + parent = child; + child = (XMLNode) parent.getLastChild(); + } + } + return parent; + } + + /** + */ + public XMLModelNotifier getModelNotifier() { + if (this.notifier == null) { + this.notifier = new XMLModelNotifierImpl(); + } + return this.notifier; + } + + /** + */ + private XMLModelParser getModelParser() { + if (this.parser == null) { + this.parser = new XMLModelParser(this); + } + return this.parser; + } + + /** + */ + private XMLModelUpdater getModelUpdater() { + if (this.updater == null) { + this.updater = new XMLModelUpdater(this); + } + return this.updater; + } + + /** + */ + private void handleRefresh() { + if (!this.refresh) + return; + XMLModelNotifier notifier = getModelNotifier(); + boolean isChanging = notifier.isChanging(); + if (!isChanging) + notifier.beginChanging(true); + XMLModelParser parser = getModelParser(); + setActive(parser); + this.document.removeChildNodes(); + try { + parser.replaceStructuredDocumentRegions(getStructuredDocument().getRegionList(), null); + } catch (Exception ex) { + Logger.logException(ex); + } finally { + setActive(null); + if (!isChanging) + notifier.endChanging(); + this.refresh = false; + } + } + + /** + * Test if the DOM implementation implements a specific feature. + * + * @param featureThe + * name of the feature to test (case-insensitive). The values + * used by DOM features are defined throughout the DOM Level 2 + * specifications and listed in the section. The name must be + * an XML name. To avoid possible conflicts, as a convention, + * names referring to features defined outside the DOM + * specification should be made unique by reversing the name of + * the Internet domain name of the person (or the organization + * that the person belongs to) who defines the feature, + * component by component, and using this as a prefix. For + * instance, the W3C SVG Working Group defines the feature + * "org.w3c.dom.svg". + * @param versionThis + * is the version number of the feature to test. In Level 2, + * the string can be either "2.0" or "1.0". If the version is + * not specified, supporting any version of the feature causes + * the method to return <code>true</code>. + * @return <code>true</code> if the feature is implemented in the + * specified version, <code>false</code> otherwise. + */ + public boolean hasFeature(String feature, String version) { + if (feature == null) + return false; + if (version != null) { + if (!version.equals("1.0") && !version.equals("2.0")) { //$NON-NLS-2$//$NON-NLS-1$ + return false; + } + } + if (feature.equalsIgnoreCase("Core")) //$NON-NLS-1$ + return true; //$NON-NLS-1$ + if (feature.equalsIgnoreCase("XML")) //$NON-NLS-1$ + return true; //$NON-NLS-1$ + return false; + } + + /** + * createDocument method + * + * @return org.w3c.dom.Document + */ + protected Document internalCreateDocument() { + DocumentImpl document = new DocumentImpl(); + document.setModel(this); + return document; + } + + boolean isReparsing() { + return (active != null); + } + + /** + * nameChanged method + * + * @param node + * org.w3c.dom.Node + */ + protected void nameChanged(Node node) { + if (node == null) + return; + if (getActiveParser() == null) { + XMLModelUpdater updater = getModelUpdater(); + setActive(updater); + updater.initialize(); + updater.changeName(node); + setActive(null); + } + // notification is already sent + } + + /** + * newModel method + * + * @param structuredDocumentEvent + * com.ibm.sed.structuredDocument.impl.events.NewModelEvent + */ + public void newModel(NewDocumentEvent structuredDocumentEvent) { + if (structuredDocumentEvent == null) + return; + IStructuredDocument structuredDocument = structuredDocumentEvent.getStructuredDocument(); + if (structuredDocument == null) + return; + // this should not happen, but for the case + if (structuredDocument != getStructuredDocument()) + setStructuredDocument(structuredDocument); + IStructuredDocumentRegionList flatNodes = structuredDocument.getRegionList(); + if (flatNodes == null) + return; + if (this.document == null) + return; // being constructed + XMLModelUpdater updater = getActiveUpdater(); + if (updater != null) { // being updated + try { + updater.replaceStructuredDocumentRegions(flatNodes, null); + } catch (Exception ex) { + Logger.logException(ex); + this.refresh = true; + handleRefresh(); + } finally { + setActive(null); + } + // // for new model, we might need to + // // re-init, e.g. if someone calls setText + // // on an existing model + // checkForReinit(); + return; + } + XMLModelNotifier notifier = getModelNotifier(); + boolean isChanging = notifier.isChanging(); + // call even if changing to notify doing new model + getModelNotifier().beginChanging(true); + XMLModelParser parser = getModelParser(); + setActive(parser); + this.document.removeChildNodes(); + try { + parser.replaceStructuredDocumentRegions(flatNodes, null); + } catch (Exception ex) { + Logger.logException(ex); + // meaningless to refresh, because the result might be the same + } finally { + setActive(null); + if (!isChanging) { + getModelNotifier().endChanging(); + } + // ignore refresh + this.refresh = false; + } + // checkForReinit(); + } + + /** + */ + public void noChange(NoChangeEvent event) { + XMLModelUpdater updater = getActiveUpdater(); + if (updater != null) { // being updated + // cleanup updater staffs + try { + updater.replaceStructuredDocumentRegions(null, null); + } catch (Exception ex) { + Logger.logException(ex); + this.refresh = true; + handleRefresh(); + } finally { + setActive(null); + } + // I guess no chanage means the model could not need re-init + //checkForReinit(); + return; + } + } + + /** + * nodesReplaced method + * + * @param event + * com.ibm.sed.structuredDocument.impl.events.NodesReplacedElement + */ + public void nodesReplaced(StructuredDocumentRegionsReplacedEvent event) { + if (event == null) + return; + IStructuredDocumentRegionList oldStructuredDocumentRegions = event.getOldStructuredDocumentRegions(); + IStructuredDocumentRegionList newStructuredDocumentRegions = event.getNewStructuredDocumentRegions(); + XMLModelUpdater updater = getActiveUpdater(); + if (updater != null) { // being updated + try { + updater.replaceStructuredDocumentRegions(newStructuredDocumentRegions, oldStructuredDocumentRegions); + } catch (Exception ex) { + if (ex.getClass().equals(StructuredDocumentRegionManagementException.class)) { + Logger.traceException(TRACE_PARSER_MANAGEMENT_EXCEPTION, ex); + } else { + Logger.logException(ex); + } + this.refresh = true; + handleRefresh(); + } finally { + setActive(null); + } + //checkForReinit(); + return; + } + XMLModelNotifier notifier = getModelNotifier(); + boolean isChanging = notifier.isChanging(); + if (!isChanging) + notifier.beginChanging(); + XMLModelParser parser = getModelParser(); + setActive(parser); + try { + parser.replaceStructuredDocumentRegions(newStructuredDocumentRegions, oldStructuredDocumentRegions); + } catch (Exception ex) { + Logger.logException(ex); + this.refresh = true; + handleRefresh(); + } finally { + setActive(null); + if (!isChanging) { + notifier.endChanging(); + handleRefresh(); + } + } + + } + + /** + * regionChanged method + * + * @param structuredDocumentEvent + * com.ibm.sed.structuredDocument.impl.events.RegionChangedEvent + */ + public void regionChanged(RegionChangedEvent event) { + if (event == null) + return; + IStructuredDocumentRegion flatNode = event.getStructuredDocumentRegion(); + if (flatNode == null) + return; + ITextRegion region = event.getRegion(); + if (region == null) + return; + XMLModelUpdater updater = getActiveUpdater(); + if (updater != null) { // being updated + try { + updater.changeRegion(flatNode, region); + } catch (Exception ex) { + Logger.logException(ex); + this.refresh = true; + handleRefresh(); + } finally { + setActive(null); + } + // checkForReinit(); + return; + } + XMLModelNotifier notifier = getModelNotifier(); + boolean isChanging = notifier.isChanging(); + if (!isChanging) + notifier.beginChanging(); + XMLModelParser parser = getModelParser(); + setActive(parser); + try { + parser.changeRegion(flatNode, region); + } catch (Exception ex) { + Logger.logException(ex); + this.refresh = true; + handleRefresh(); + } finally { + setActive(null); + if (!isChanging) { + notifier.endChanging(); + handleRefresh(); + } + } + // checkForReinit(); + } + + /** + * regionsReplaced method + * + * @param event + * com.ibm.sed.structuredDocument.impl.events.RegionReplacedEvent + */ + public void regionsReplaced(RegionsReplacedEvent event) { + if (event == null) + return; + IStructuredDocumentRegion flatNode = event.getStructuredDocumentRegion(); + if (flatNode == null) + return; + ITextRegionList oldRegions = event.getOldRegions(); + ITextRegionList newRegions = event.getNewRegions(); + if (oldRegions == null && newRegions == null) + return; + XMLModelUpdater updater = getActiveUpdater(); + if (updater != null) { // being updated + try { + updater.replaceRegions(flatNode, newRegions, oldRegions); + } catch (Exception ex) { + Logger.logException(ex); + this.refresh = true; + handleRefresh(); + } finally { + setActive(null); + } + // checkForReinit(); + return; + } + XMLModelNotifier notifier = getModelNotifier(); + boolean isChanging = notifier.isChanging(); + if (!isChanging) + notifier.beginChanging(); + XMLModelParser parser = getModelParser(); + setActive(parser); + try { + parser.replaceRegions(flatNode, newRegions, oldRegions); + } catch (Exception ex) { + Logger.logException(ex); + this.refresh = true; + handleRefresh(); + } finally { + setActive(null); + if (!isChanging) { + notifier.endChanging(); + handleRefresh(); + } + } + // checkForReinit(); + } + + /** + */ + public void releaseFromEdit() { + if (!isShared()) { + //this.document.releaseStyleSheets(); + this.document.releaseDocumentType(); + } + super.releaseFromEdit(); + } + + /** + */ + public void releaseFromRead() { + if (!isShared()) { + //this.document.releaseStyleSheets(); + this.document.releaseDocumentType(); + } + super.releaseFromRead(); + } + + /** + */ + private void setActive(Object active) { + this.active = active; + // side effect + // when ever becomes active, besure tagNameCache is cleared + // (and not used) + if (active == null) { + document.activateTagNameCache(true); + } else { + document.activateTagNameCache(false); + } + + } + + /** + */ + public void setGenerator(XMLGenerator generator) { + this.generator = generator; + } + + /** + */ + public void setModelNotifier(XMLModelNotifier notifier) { + this.notifier = notifier; + } + + /** + */ + public void setModelParser(XMLModelParser parser) { + this.parser = parser; + } + + /** + */ + public void setModelUpdater(XMLModelUpdater updater) { + this.updater = updater; + } + + /** + * setStructuredDocument method + * + * @param structuredDocument + * com.ibm.sed.structuredDocument.IStructuredDocument + */ + public void setStructuredDocument(IStructuredDocument structuredDocument) { + IStructuredDocument oldStructuredDocument = super.getStructuredDocument(); + if (structuredDocument == oldStructuredDocument) + return; // nothing to do + if (oldStructuredDocument != null) + oldStructuredDocument.removeDocumentChangingListener(this); + super.setStructuredDocument(structuredDocument); + if (structuredDocument.getLength() > 0) { + newModel(new NewDocumentEvent(structuredDocument, this)); + } + if (structuredDocument != null) + structuredDocument.addDocumentChangingListener(this); + } + + /** + */ + protected void startTagChanged(Element element) { + if (element == null) + return; + if (getActiveParser() == null) { + XMLModelUpdater updater = getModelUpdater(); + setActive(updater); + updater.initialize(); + updater.changeStartTag(element); + setActive(null); + } + getModelNotifier().startTagChanged(element); + } + + /** + * valueChanged method + * + * @param node + * org.w3c.dom.Node + */ + protected void valueChanged(Node node) { + if (node == null) + return; + if (getActiveParser() == null) { + XMLModelUpdater updater = getModelUpdater(); + setActive(updater); + updater.initialize(); + updater.changeValue(node); + setActive(null); + } + getModelNotifier().valueChanged(node); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelNotifierImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelNotifierImpl.java new file mode 100644 index 0000000000..4146e0c5af --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelNotifierImpl.java @@ -0,0 +1,469 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + +import java.util.Iterator; +import java.util.Vector; + +import org.eclipse.wst.sse.core.INodeNotifier; +import org.eclipse.wst.sse.core.util.Debug; +import org.eclipse.wst.xml.core.Logger; +import org.eclipse.wst.xml.core.document.XMLModelNotifier; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + + +public class XMLModelNotifierImpl implements XMLModelNotifier { + + /* end: for debugging only */ + private class NotifyEvent { + Object changedFeature; + boolean discarded; + Object newValue; + // note: don't initialize instance variables, since + // that causes double assignments, and lots of these are created. + INodeNotifier notifier; + Object oldValue; + int pos; + String reason; + int type; + + NotifyEvent(INodeNotifier notifier, int type, Object changedFeature, Object oldValue, Object newValue, int pos) { + this.notifier = notifier; + this.type = type; + this.changedFeature = changedFeature; + this.oldValue = oldValue; + this.newValue = newValue; + this.pos = pos; + this.reason = ""; //$NON-NLS-1$ + } + } + + private final static String ADDED_THEN_REMOVED = "Discard: Added then removed rule"; //$NON-NLS-1$ + private final static boolean fOptimizeDeferred = true; + private final static boolean fOptimizeDeferredAccordingToParentAdded = true; + private final static boolean fOptimizeDeferredAccordingToParentRemoved = true; + private final static String PARENT_IS_ADDED = "Disarded: Parent has just been added"; //$NON-NLS-1$ + /* start: for debugging only */ + private final static String PARENT_IS_REMOVED_TOO = "Discard: Parent was removed too"; //$NON-NLS-1$ + private final static String PARENT_IS_REPARENTED = "Not Discard: Parent was removed so this implies reparenting"; //$NON-NLS-1$ + private Node changedRoot = null; + + private boolean changing = false; + private boolean doingNewModel = false; + private Vector events = null; + private boolean flushing = false; + + /** + */ + public XMLModelNotifierImpl() { + super(); + } + + /** + * attrReplaced method + * + * @param element + * org.w3c.dom.Element + * @param newAttr + * org.w3c.dom.Attr + * @param oldAttr + * org.w3c.dom.Attr + */ + public void attrReplaced(Element element, Attr newAttr, Attr oldAttr) { + if (element == null) + return; + Attr attr = null; + String oldValue = null; + String newValue = null; + if (oldAttr != null) { + attr = oldAttr; + oldValue = oldAttr.getValue(); + } + if (newAttr != null) { + attr = newAttr; + newValue = newAttr.getValue(); + } + XMLNode notifier = (XMLNode) element; + int offset = notifier.getStartOffset(); + notify(notifier, INodeNotifier.CHANGE, attr, oldValue, newValue, offset); + propertyChanged(notifier); + } + + /** + */ + public void beginChanging() { + this.changing = true; + } + + /** + */ + public void beginChanging(boolean newModel) { + beginChanging(); + this.doingNewModel = newModel; + } + + /** + * @see com.ibm.sed.model.xml.XMLModelNotifier#cancelPending() + */ + public void cancelPending() { + // we don't want to change the size of this array, since + // the array may be being processed, in the defferred notification + // loop, but we can signal that all + // should be discarded, so any remaining ones will be ignored. + if (this.events != null) { + Iterator iterator = this.events.iterator(); + while (iterator.hasNext()) { + NotifyEvent event = (NotifyEvent) iterator.next(); + event.discarded = true; + } + } + // this cancel is presumably being called as a function of + // "reinitiailization" so we can ignore changes to the + // old root, and changes to the new one will be triggered during + // reinitialization. + changedRoot = null; + } + + /** + * childReplaced method + * + * @param parentNode + * org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + * @param oldChild + * org.w3c.dom.Node + */ + public void childReplaced(Node parentNode, Node newChild, Node oldChild) { + if (parentNode == null) + return; + XMLNode notifier = (XMLNode) parentNode; + int type = INodeNotifier.CHANGE; + if (newChild == null) + type = INodeNotifier.REMOVE; + else if (oldChild == null) + type = INodeNotifier.ADD; + int offset = notifier.getStartOffset(); + notify(notifier, type, oldChild, oldChild, newChild, offset); + structureChanged(notifier); + } + + public void editableChanged(Node node) { + if (node == null) + return; + XMLNode notifier = (XMLNode) node; + int offset = notifier.getStartOffset(); + notify(notifier, INodeNotifier.CHANGE, null, null, null, offset); + propertyChanged(notifier); + } + + /** + */ + public void endChanging() { + this.doingNewModel = false; + if (!this.changing) + return; // avoid nesting calls + notifyDeferred(); + if (this.changedRoot != null) { + notifyStructureChanged(this.changedRoot); + if (Debug.debugNotifyDeferred) { + String p = this.changedRoot.getNodeName(); + System.out.println("Deferred STRUCUTRE_CHANGED: " + p); //$NON-NLS-1$ + } + this.changedRoot = null; + } + this.changing = false; + } + + /** + */ + public void endTagChanged(Element element) { + if (element == null) + return; + XMLNode notifier = (XMLNode) element; + int offset = notifier.getStartOffset(); + notify(notifier, INodeNotifier.CHANGE, null, null, null, offset); + propertyChanged(element); + } + + /** + */ + public boolean hasChanged() { + return (this.events != null); + } + + /** + */ + public boolean isChanging() { + return this.changing; + } + + /** + */ + private void notify(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) { + if (notifier == null) + return; + if (this.changing && !this.flushing) { + // defer notification + if (this.events == null) + this.events = new Vector(); + // we do not defer anything if we are doing a new Model, + // except for the document event, since all others are + // trivial and not needed at that initial point. + // But even for that one document event, in the new model case, + // it is still important to defer it. + if ((!doingNewModel) || (((Node) notifier).getNodeType() == Node.DOCUMENT_NODE)) { + this.events.addElement(new NotifyEvent(notifier, eventType, changedFeature, oldValue, newValue, pos)); + } + return; + } + try { + // Its important to "keep going" if exception occurs, since this + // notification + // comes in between "about to change" and "changed" events. We do + // log, however, + // since would indicate a program error. + notifier.notify(eventType, changedFeature, oldValue, newValue, pos); + } catch (Exception e) { + Logger.logException("A structured model client threw following exception during adapter notification (" + INodeNotifier.EVENT_TYPE_STRINGS[eventType] + " )", e); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + */ + private void notifyDeferred() { + if (this.events == null) + return; + if (this.flushing) + return; + this.flushing = true; // force notification + int count = this.events.size(); + for (int i = 0; i < count; i++) { + NotifyEvent event = (NotifyEvent) this.events.elementAt(i); + if (event == null) + continue; // error + if (event.discarded) + continue; + if (!doingNewModel && fOptimizeDeferred) { + // check redundant events (no need to check if doing NewModel, + // since + // shouldn't be redunancies) + if (event.type == INodeNotifier.ADD) { + for (int n = i + 1; n < count; n++) { + NotifyEvent next = (NotifyEvent) this.events.elementAt(n); + if (next == null) + continue; // error + if (next.type == INodeNotifier.REMOVE && next.oldValue == event.newValue) { + // Added then removed later, discard both + event.discarded = true; + next.discarded = true; + if (Debug.debugNotifyDeferred) { + event.reason = event.reason + ADDED_THEN_REMOVED + "(see " + n + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + next.reason = next.reason + ADDED_THEN_REMOVED + "(see " + i + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + break; + } + } + if (event.discarded) + continue; + if (fOptimizeDeferredAccordingToParentAdded) { + for (int p = 0; p < i; p++) { + NotifyEvent prev = (NotifyEvent) this.events.elementAt(p); + if (prev == null) + continue; // error + if (prev.type == INodeNotifier.REMOVE && prev.oldValue == event.notifier) { + // parent is reparented, do not discard + if (Debug.debugNotifyDeferred) { + event.reason = event.reason + PARENT_IS_REPARENTED + "(see " + p + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + break; + } else if (prev.type == INodeNotifier.ADD && prev.newValue == event.notifier) { + // parent has been added, discard this + event.discarded = true; + if (Debug.debugNotifyDeferred) { + event.reason = event.reason + PARENT_IS_ADDED + "(see " + p + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + break; + } + } + if (event.discarded) + continue; + } + } else if (event.type == INodeNotifier.REMOVE) { + if (fOptimizeDeferredAccordingToParentRemoved) { + for (int n = i + 1; n < count; n++) { + NotifyEvent next = (NotifyEvent) this.events.elementAt(n); + if (next == null) + continue; // error + if (next.type == INodeNotifier.REMOVE) { + if (next.oldValue == event.notifier) { + // parent will be removed, discard this + event.discarded = true; + if (Debug.debugNotifyDeferred) { + event.reason = event.reason + PARENT_IS_REMOVED_TOO + "(see " + n + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + break; + } + } + } + if (event.discarded) + continue; + } + } + } + notify(event.notifier, event.type, event.changedFeature, event.oldValue, event.newValue, event.pos); + } + if (Debug.debugNotifyDeferred) { + for (int l = 0; l < count; l++) { + NotifyEvent event = (NotifyEvent) this.events.elementAt(l); + Object o = null; + String t = null; + if (event.type == INodeNotifier.ADD) { + o = event.newValue; + t = " + "; //$NON-NLS-1$ + } else if (event.type == INodeNotifier.REMOVE) { + o = event.oldValue; + t = " - "; //$NON-NLS-1$ + } + if (o instanceof Element) { + String p = ((Node) event.notifier).getNodeName(); + String c = ((Node) o).getNodeName(); + String d = (event.discarded ? "! " : " "); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.println(d + p + t + c); + } + } + } + this.flushing = false; + this.events = null; + } + + /** + */ + private void notifyStructureChanged(Node root) { + if (root == null) + return; + INodeNotifier notifier = (INodeNotifier) root; + try { + // Its important to "keep going" if exception occurs, since this + // notification + // comes in between "about to change" and "changed" events. We do + // log, however, + // since would indicate a program error. + notifier.notify(INodeNotifier.STRUCTURE_CHANGED, null, null, null, -1); + } catch (Exception e) { + Logger.logException("A structured model client threw following exception during adapter notification (" + INodeNotifier.EVENT_TYPE_STRINGS[INodeNotifier.STRUCTURE_CHANGED] + " )", e); //$NON-NLS-1$ //$NON-NLS-2$ + } + + } + + /** + */ + public void propertyChanged(Node node) { + } + + /** + * @param node + */ + private void setCommonRootIfNeeded(Node node) { + // defer notification + if (this.changedRoot == null) { + this.changedRoot = node; + } else { + // tiny optimization: if previous commonAncestor (changedRoot) is + // already 'document', + // or if already equal to this 'node', + // then no need to re-calculate + if (changedRoot.getNodeType() != Node.DOCUMENT_NODE && changedRoot != node) { + Node common = ((NodeImpl) this.changedRoot).getCommonAncestor(node); + if (common != null) + this.changedRoot = common; + else + this.changedRoot = node; + } + } + } + + /** + */ + public void startTagChanged(Element element) { + if (element == null) + return; + XMLNode notifier = (XMLNode) element; + int offset = notifier.getStartOffset(); + notify(notifier, INodeNotifier.CHANGE, null, null, null, offset); + propertyChanged(element); + } + + /** + */ + public void structureChanged(Node node) { + if (node == null) + return; + if (isChanging()) { + setCommonRootIfNeeded(node); + if (Debug.debugNotifyDeferred) { + String p = this.changedRoot.getNodeName(); + System.out.println("requested STRUCUTRE_CHANGED: " + p); //$NON-NLS-1$ + } + return; + } + if (Debug.debugNotifyDeferred) { + String p = node.getNodeName(); + System.out.println("STRUCUTRE_CHANGED: " + p); //$NON-NLS-1$ + } + notifyStructureChanged(node); + } + + /** + * valueChanged method + * + * @param node + * org.w3c.dom.Node + */ + public void valueChanged(Node node) { + if (node == null) + return; + XMLNode notifier = null; + if (node.getNodeType() == Node.ATTRIBUTE_NODE) { + Attr attr = (Attr) node; + notifier = (XMLNode) attr.getOwnerElement(); + // TODO_dmw: experimental: changed 06/29/2004 to send "strucuture + // changed" even for attribute value changes + // there are pros and cons to considering attribute value + // "structure changed". Will (re)consider + // setCommonRootIfNeeded(notifier); + if (notifier == null) + return; + String value = attr.getValue(); + int offset = notifier.getStartOffset(); + notify(notifier, INodeNotifier.CHANGE, attr, null, value, offset); + } else { + // note: we do not send structured changed event for content + // changed + notifier = (XMLNode) node; + String value = node.getNodeValue(); + int offset = notifier.getStartOffset(); + notify(notifier, INodeNotifier.CHANGE, null, null, value, offset); + if (node.getNodeType() != Node.ELEMENT_NODE) { + XMLNode parent = (XMLNode) node.getParentNode(); + if (parent != null) { + notify(parent, INodeNotifier.CONTENT_CHANGED, node, null, value, offset); + } + } + } + propertyChanged(notifier); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelParser.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelParser.java new file mode 100644 index 0000000000..7d64743d9a --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelParser.java @@ -0,0 +1,2365 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Vector; + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.commentelement.impl.CommentElementConfiguration; +import org.eclipse.wst.xml.core.commentelement.impl.CommentElementRegistry; +import org.eclipse.wst.xml.core.document.JSPTag; +import org.eclipse.wst.xml.core.document.XMLDocument; +import org.eclipse.wst.xml.core.document.XMLElement; +import org.eclipse.wst.xml.core.document.XMLModel; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.Text; + + +/** + * XMLModelParser + */ +public class XMLModelParser implements org.eclipse.wst.xml.core.jsp.model.parser.temp.XMLJSPRegionContexts { + private ModelParserAdapter adapter = null; + private XMLModelContext context = null; + private DocumentImpl document = null; + + private XMLModelImpl model = null; + + /** + */ + protected XMLModelParser(XMLModelImpl model) { + super(); + + if (model != null) { + this.model = model; + this.document = (DocumentImpl) model.getDocument(); + if (this.document != null) { + this.adapter = (ModelParserAdapter) this.document.getAdapterFor(ModelParserAdapter.class); + } + } + } + + /** + */ + protected boolean canBeImplicitTag(Element element) { + if (this.adapter != null) { + return this.adapter.canBeImplicitTag(element); + } + return false; + } + + /** + */ + protected boolean canBeImplicitTag(Element element, Node child) { + if (this.adapter != null) { + return this.adapter.canBeImplicitTag(element, child); + } + return false; + } + + /** + */ + protected boolean canContain(Element element, Node child) { + if (element == null || child == null) + return false; + ElementImpl impl = (ElementImpl) element; + if (impl.isEndTag()) + return false; // invalid (floating) end tag + if (!impl.isContainer()) + return false; + if (child.getNodeType() != Node.TEXT_NODE) { + if (impl.isJSPContainer() || impl.isCDATAContainer()) { + // accepts only Text child + return false; + } + } + if (this.adapter != null) { + return this.adapter.canContain(element, child); + } + return true; + } + + /** + */ + private void changeAttrEqual(IStructuredDocumentRegion flatNode, ITextRegion region) { + int offset = flatNode.getStart(); + if (offset < 0) + return; + NodeImpl root = (NodeImpl) this.context.getRootNode(); + if (root == null) + return; + Node node = root.getNodeAt(offset); + if (node == null) + return; + if (node.getNodeType() != Node.ELEMENT_NODE) { + if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { + // just notify the change instead of setting data + ProcessingInstructionImpl pi = (ProcessingInstructionImpl) node; + pi.notifyValueChanged(); + } + return; + } + // actually, do nothing + } + + /** + * changeAttrName method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + * @param region + * com.ibm.sed.structuredDocument.ITextRegion + */ + private void changeAttrName(IStructuredDocumentRegion flatNode, ITextRegion region) { + int offset = flatNode.getStart(); + if (offset < 0) + return; + NodeImpl root = (NodeImpl) this.context.getRootNode(); + if (root == null) + return; + Node node = root.getNodeAt(offset); + if (node == null) + return; + if (node.getNodeType() != Node.ELEMENT_NODE) { + if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { + // just notify the change instead of setting data + ProcessingInstructionImpl pi = (ProcessingInstructionImpl) node; + pi.notifyValueChanged(); + } + return; + } + + ElementImpl element = (ElementImpl) node; + NamedNodeMap attributes = element.getAttributes(); + if (attributes == null) + return; + int length = attributes.getLength(); + for (int i = 0; i < length; i++) { + AttrImpl attr = (AttrImpl) attributes.item(i); + if (attr == null) + continue; + if (attr.getNameRegion() != region) + continue; + + String name = flatNode.getText(region); + attr.setName(name); + break; + } + } + + /** + * changeAttrValue method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + * @param region + * com.ibm.sed.structuredDocument.ITextRegion + */ + private void changeAttrValue(IStructuredDocumentRegion flatNode, ITextRegion region) { + int offset = flatNode.getStart(); + if (offset < 0) + return; + NodeImpl root = (NodeImpl) this.context.getRootNode(); + if (root == null) + return; + Node node = root.getNodeAt(offset); + if (node == null) + return; + if (node.getNodeType() != Node.ELEMENT_NODE) { + if (node.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) { + // just notify the change instead of setting data + ProcessingInstructionImpl pi = (ProcessingInstructionImpl) node; + pi.notifyValueChanged(); + } + return; + } + + ElementImpl element = (ElementImpl) node; + NamedNodeMap attributes = element.getAttributes(); + if (attributes == null) + return; + int length = attributes.getLength(); + for (int i = 0; i < length; i++) { + AttrImpl attr = (AttrImpl) attributes.item(i); + if (attr == null) + continue; + if (attr.getValueRegion() != region) + continue; + // just notify the change instead of setting value + attr.notifyValueChanged(); + break; + } + } + + /** + * changeData method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + * @param region + * com.ibm.sed.structuredDocument.ITextRegion + */ + private void changeData(IStructuredDocumentRegion flatNode, ITextRegion region) { + int offset = flatNode.getStart(); + if (offset < 0) + return; + NodeImpl root = (NodeImpl) this.context.getRootNode(); + if (root == null) + return; + Node node = root.getNodeAt(offset); + if (node == null) + return; + switch (node.getNodeType()) { + case Node.TEXT_NODE : { + TextImpl text = (TextImpl) node; + if (text.isSharingStructuredDocumentRegion(flatNode)) { + // has consecutive text sharing IStructuredDocumentRegion + changeStructuredDocumentRegion(flatNode); + return; + } + this.context.setNextNode(node); + cleanupText(); + break; + } + case Node.CDATA_SECTION_NODE : + case Node.PROCESSING_INSTRUCTION_NODE : + break; + case Node.COMMENT_NODE : + case Node.ELEMENT_NODE : + // comment tag + changeStructuredDocumentRegion(flatNode); + return; + default : + return; + } + + // just notify the change instead of setting data + NodeImpl impl = (NodeImpl) node; + impl.notifyValueChanged(); + } + + /** + */ + private void changeEndTag(IStructuredDocumentRegion flatNode, ITextRegionList newRegions, ITextRegionList oldRegions) { + int offset = flatNode.getStart(); + if (offset < 0) + return; // error + NodeImpl root = (NodeImpl) this.context.getRootNode(); + if (root == null) + return; // error + Node node = root.getNodeAt(offset); + if (node == null) + return; // error + + if (node.getNodeType() != Node.ELEMENT_NODE) { + changeStructuredDocumentRegion(flatNode); + return; + } + + // check if change is only for close tag + if (newRegions != null) { + Iterator e = newRegions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_TAG_CLOSE) + continue; + + // other region has changed + changeStructuredDocumentRegion(flatNode); + return; + } + } + if (oldRegions != null) { + Iterator e = oldRegions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_TAG_CLOSE) + continue; + + // other region has changed + changeStructuredDocumentRegion(flatNode); + return; + } + } + + // change for close tag has no impact + // do nothing + } + + /** + * changeRegion method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + * @param region + * com.ibm.sed.structuredDocument.ITextRegion + */ + void changeRegion(IStructuredDocumentRegion flatNode, ITextRegion region) { + if (flatNode == null || region == null) + return; + if (this.document == null) + return; + this.context = new XMLModelContext(this.document); + + // optimize typical cases + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_CONTENT || regionType == XMLRegionContext.XML_COMMENT_TEXT || regionType == XMLRegionContext.XML_CDATA_TEXT || regionType == XMLRegionContext.BLOCK_TEXT || regionType == JSP_CONTENT) { + changeData(flatNode, region); + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME) { + changeAttrName(flatNode, region); + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) { + changeAttrValue(flatNode, region); + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { + changeAttrEqual(flatNode, region); + } else if (regionType == XMLRegionContext.XML_TAG_NAME || regionType == JSP_ROOT_TAG_NAME || regionType == JSP_DIRECTIVE_NAME) { + changeTagName(flatNode, region); + } else { + changeStructuredDocumentRegion(flatNode); + } + } + + /** + */ + private void changeStartTag(IStructuredDocumentRegion flatNode, ITextRegionList newRegions, ITextRegionList oldRegions) { + int offset = flatNode.getStart(); + if (offset < 0) + return; // error + NodeImpl root = (NodeImpl) this.context.getRootNode(); + if (root == null) + return; // error + Node node = root.getNodeAt(offset); + if (node == null) + return; // error + + if (node.getNodeType() != Node.ELEMENT_NODE) { + changeStructuredDocumentRegion(flatNode); + return; + } + ElementImpl element = (ElementImpl) node; + + // check if changes are only for attributes and close tag + boolean tagNameUnchanged = false; + if (newRegions != null) { + Iterator e = newRegions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME || regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS || regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) + continue; + if (regionType == XMLRegionContext.XML_TAG_CLOSE) { + // change from empty tag may have impact on structure + if (!element.isEmptyTag()) + continue; + } else if (regionType == XMLRegionContext.XML_TAG_NAME || regionType == JSP_ROOT_TAG_NAME || regionType == JSP_DIRECTIVE_NAME) { + String oldTagName = element.getTagName(); + String newTagName = flatNode.getText(region); + if (oldTagName != null && newTagName != null && oldTagName.equals(newTagName)) { + // the tag name is unchanged + tagNameUnchanged = true; + continue; + } + } + + // other region has changed + changeStructuredDocumentRegion(flatNode); + return; + } + } + if (oldRegions != null) { + Iterator e = oldRegions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME || regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS || regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) + continue; + if (regionType == XMLRegionContext.XML_TAG_CLOSE) { + // change from empty tag may have impact on structure + if (!element.isEmptyTag()) + continue; + } else if (regionType == XMLRegionContext.XML_TAG_NAME || regionType == JSP_ROOT_TAG_NAME) { + // if new tag name is unchanged, it's OK + if (tagNameUnchanged) + continue; + } + + // other region has changed + changeStructuredDocumentRegion(flatNode); + return; + } + } + + // update attributes + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; // error + NamedNodeMap attributes = element.getAttributes(); + if (attributes == null) + return; // error + + // first remove attributes + int regionIndex = 0; + int attrIndex = 0; + AttrImpl attr = null; + while (attrIndex < attributes.getLength()) { + attr = (AttrImpl) attributes.item(attrIndex); + if (attr == null) { // error + attrIndex++; + continue; + } + ITextRegion nameRegion = attr.getNameRegion(); + if (nameRegion == null) { // error + element.removeAttributeNode(attr); + continue; + } + boolean found = false; + for (int i = regionIndex; i < regions.size(); i++) { + ITextRegion region = regions.get(i); + if (region == nameRegion) { + regionIndex = i + 1; // next region + found = true; + break; + } + } + if (found) { + attrIndex++; + } else { + element.removeAttributeNode(attr); + } + } + + // insert or update attributes + attrIndex = 0; // reset to first + AttrImpl newAttr = null; + ITextRegion oldValueRegion = null; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME) { + if (newAttr != null) { + // insert deferred new attribute + element.insertAttributeNode(newAttr, attrIndex++); + newAttr = null; + } else if (attr != null && oldValueRegion != null) { + // notify existing attribute value removal + attr.notifyValueChanged(); + } + + oldValueRegion = null; + attr = (AttrImpl) attributes.item(attrIndex); + if (attr != null && attr.getNameRegion() == region) { + // existing attribute + attrIndex++; + // clear other regions + oldValueRegion = attr.getValueRegion(); + attr.setEqualRegion(null); + attr.setValueRegion(null); + } else { + String name = flatNode.getText(region); + attr = (AttrImpl) this.document.createAttribute(name); + if (attr != null) + attr.setNameRegion(region); + // defer insertion of new attribute + newAttr = attr; + } + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { + if (attr != null) { + attr.setEqualRegion(region); + } + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) { + if (attr != null) { + attr.setValueRegion(region); + if (attr != newAttr && oldValueRegion != region) { + // notify existing attribute value changed + attr.notifyValueChanged(); + } + oldValueRegion = null; + attr = null; + } + } + } + + if (newAttr != null) { + // insert deferred new attribute + element.appendAttributeNode(newAttr); + } else if (attr != null && oldValueRegion != null) { + // notify existing attribute value removal + attr.notifyValueChanged(); + } + } + + /** + * changeStructuredDocumentRegion method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void changeStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return; + if (this.document == null) + return; + + setupContext(flatNode); + + removeStructuredDocumentRegion(flatNode); + // make sure the parent is set to deepest level + // when end tag has been removed + this.context.setLast(); + insertStructuredDocumentRegion(flatNode); + + cleanupText(); + cleanupEndTag(); + } + + /** + */ + private void changeTagName(IStructuredDocumentRegion flatNode, ITextRegion region) { + int offset = flatNode.getStart(); + if (offset < 0) + return; // error + NodeImpl root = (NodeImpl) this.context.getRootNode(); + if (root == null) + return; // error + Node node = root.getNodeAt(offset); + if (node == null) + return; // error + + if (node.getNodeType() != Node.ELEMENT_NODE) { + changeStructuredDocumentRegion(flatNode); + return; + } + + ElementImpl element = (ElementImpl) node; + String newTagName = flatNode.getText(region); + if (newTagName == null || !element.matchTagName(newTagName)) { + // the tag name is changed + changeStructuredDocumentRegion(flatNode); + return; + } + + // the tag name is unchanged + // this happens when typing spaces after the tag name + // do nothing, but... + // if it's not a change in the end tag of an element with the start + // tag, + // and case has been changed, set to element and notify + if (!element.hasStartTag() || StructuredDocumentRegionUtil.getFirstRegionType(flatNode) != XMLRegionContext.XML_END_TAG_OPEN) { + String tagName = element.getTagName(); + if (tagName == null || !tagName.equals(newTagName)) { + element.setTagName(newTagName); + element.notifyValueChanged(); + } + } + } + + /** + * cleanupContext method + */ + private void cleanupEndTag() { + Node parent = this.context.getParentNode(); + Node next = this.context.getNextNode(); + while (parent != null) { + while (next != null) { + if (next.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) next; + if (element.isEndTag()) { + // floating end tag + String tagName = element.getTagName(); + String rootName = getFindRootName(tagName); + ElementImpl start = (ElementImpl) this.context.findStartTag(tagName, rootName); + if (start != null) { + insertEndTag(start); + // move the end tag from 'element' to 'start' + start.addEndTag(element); + removeNode(element); + parent = this.context.getParentNode(); + next = this.context.getNextNode(); + continue; + } + } + } + + Node first = next.getFirstChild(); + if (first != null) { + parent = next; + next = first; + this.context.setNextNode(next); + } else { + next = next.getNextSibling(); + this.context.setNextNode(next); + } + } + + if (parent.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) parent; + if (!element.hasEndTag() && element.hasStartTag() && element.getNextSibling() == null) { + String tagName = element.getTagName(); + ElementImpl end = (ElementImpl) this.context.findEndTag(tagName); + if (end != null) { + // move the end tag from 'end' to 'element' + element.addEndTag(end); + removeEndTag(end); + this.context.setParentNode(parent); // reset context + continue; + } + } + } + + next = parent.getNextSibling(); + parent = parent.getParentNode(); + if (next != null) { + this.context.setNextNode(next); + } else { + this.context.setParentNode(parent); + } + } + } + + /** + */ + private void cleanupText() { + Node parent = this.context.getParentNode(); + if (parent == null) + return; // error + Node next = this.context.getNextNode(); + Node prev = (next == null ? parent.getLastChild() : next.getPreviousSibling()); + + TextImpl nextText = null; + TextImpl prevText = null; + if (next != null && next.getNodeType() == Node.TEXT_NODE) { + nextText = (TextImpl) next; + } + if (prev != null && prev.getNodeType() == Node.TEXT_NODE) { + prevText = (TextImpl) prev; + } + if (nextText == null && prevText == null) + return; + if (nextText != null && prevText != null) { + // consecutive Text nodes created by setupContext(), + // concat them + IStructuredDocumentRegion flatNode = nextText.getStructuredDocumentRegion(); + if (flatNode != null) + prevText.appendStructuredDocumentRegion(flatNode); + Node newNext = next.getNextSibling(); + parent.removeChild(next); + next = null; + this.context.setNextNode(newNext); + } + + TextImpl childText = (prevText != null ? prevText : nextText); + if (childText.getNextSibling() == null && childText.getPreviousSibling() == null) { + if (parent.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl parentElement = (ElementImpl) parent; + if (!parentElement.hasStartTag() && !parentElement.hasEndTag()) { + if (childText.isWhitespace() || childText.isInvalid()) { + // implicit parent is not required + Node newParent = parent.getParentNode(); + if (newParent != null) { + Node newNext = parent.getNextSibling(); + newParent.removeChild(parent); + parent.removeChild(childText); + newParent.insertBefore(childText, newNext); + if (childText == next) { + this.context.setNextNode(childText); + } else if (newNext != null) { + this.context.setNextNode(newNext); + } else { + this.context.setParentNode(newParent); + } + // try again + cleanupText(); + } + } + } + } + } + } + + /** + * This routine create an Element from comment data for comment style + * elements, such as SSI and METADATA + */ + protected Element createCommentElement(String data, boolean isJSPTag) { + String trimmedData = data.trim(); + CommentElementConfiguration[] configs = CommentElementRegistry.getInstance().getConfigurations(); + for (int iConfig = 0; iConfig < configs.length; iConfig++) { + CommentElementConfiguration config = configs[iConfig]; + if ((isJSPTag && !config.acceptJSPComment()) || (!isJSPTag && !config.acceptXMLComment())) { + continue; + } + String[] prefixes = config.getPrefix(); + for (int iPrefix = 0; iPrefix < prefixes.length; iPrefix++) { + if (trimmedData.startsWith(prefixes[iPrefix])) { + return config.createElement(this.document, data, isJSPTag); + } + } + } + if (this.adapter != null) { + return this.adapter.createCommentElement(this.document, data, isJSPTag); + } + return null; + } + + /** + * This routine create an implicit Element for given parent and child, + * such as HTML, BODY, HEAD, and TBODY for HTML document. + */ + protected Element createImplicitElement(Node parent, Node child) { + if (this.adapter != null) { + return this.adapter.createImplicitElement(this.document, parent, child); + } + return null; + } + + /** + */ + private void demoteNodes(Node root, Node newParent, Node oldParent, Node next) { + if (newParent.getNodeType() != Node.ELEMENT_NODE) + return; + ElementImpl newElement = (ElementImpl) newParent; + + // find next + while (next == null) { + if (oldParent.getNodeType() != Node.ELEMENT_NODE) + return; + ElementImpl oldElement = (ElementImpl) oldParent; + if (oldElement.hasEndTag()) + return; + oldParent = oldElement.getParentNode(); + if (oldParent == null) + return; // error + next = oldElement.getNextSibling(); + } + + while (next != null) { + boolean done = false; + if (next.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl nextElement = (ElementImpl) next; + if (!nextElement.hasStartTag()) { + Node nextChild = nextElement.getFirstChild(); + if (nextChild != null) { + // demote children + next = nextChild; + oldParent = nextElement; + continue; + } + + if (nextElement.hasEndTag()) { + if (nextElement.matchEndTag(newElement)) { + // stop at the matched invalid end tag + next = nextElement.getNextSibling(); + oldParent.removeChild(nextElement); + newElement.addEndTag(nextElement); + + if (newElement == root) + return; + Node p = newElement.getParentNode(); + // check if reached to top + if (p == null || p == oldParent || p.getNodeType() != Node.ELEMENT_NODE) + return; + newElement = (ElementImpl) p; + done = true; + } + } else { + // remove implicit element + next = nextElement.getNextSibling(); + oldParent.removeChild(nextElement); + done = true; + } + } + } + + if (!done) { + if (!canContain(newElement, next)) { + if (newElement == root) + return; + Node p = newElement.getParentNode(); + // check if reached to top + if (p == null || p == oldParent || p.getNodeType() != Node.ELEMENT_NODE) + return; + newElement = (ElementImpl) p; + continue; + } + + Node child = next; + next = next.getNextSibling(); + oldParent.removeChild(child); + insertNode(newElement, child, null); + Node childParent = child.getParentNode(); + if (childParent != newElement) { + newElement = (ElementImpl) childParent; + } + } + + // find next parent and sibling + while (next == null) { + if (oldParent.getNodeType() != Node.ELEMENT_NODE) + return; + ElementImpl oldElement = (ElementImpl) oldParent; + + // dug parent must not have children at this point + if (!oldElement.hasChildNodes() && !oldElement.hasStartTag()) { + oldParent = oldElement.getParentNode(); + if (oldParent == null) + return; // error + next = oldElement; + break; + } + + if (oldElement.hasEndTag()) + return; + oldParent = oldElement.getParentNode(); + if (oldParent == null) + return; // error + next = oldElement.getNextSibling(); + } + } + } + + /** + */ + protected final XMLDocument getDocument() { + return this.document; + } + + /** + */ + protected String getFindRootName(String tagName) { + if (this.adapter != null) { + return this.adapter.getFindRootName(tagName); + } + return null; + } + + /** + */ + protected final XMLModel getModel() { + return this.model; + } + + /** + * insertCDATASection method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void insertCDATASection(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + CDATASectionImpl cdata = null; + try { + cdata = (CDATASectionImpl) this.document.createCDATASection(null); + } catch (DOMException ex) { + } + if (cdata == null) { // CDATA section might not be supported + insertInvalidDecl(flatNode); // regard as invalid decl + return; + } + + cdata.setStructuredDocumentRegion(flatNode); + insertNode(cdata); + } + + /** + * insertComment method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void insertComment(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + String data = null; + boolean isJSPTag = false; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == JSP_COMMENT_OPEN) { + isJSPTag = true; + } else if (regionType == XMLRegionContext.XML_COMMENT_TEXT || regionType == JSP_COMMENT_TEXT) { + if (data == null) { + data = flatNode.getText(region); + } + } + } + + if (data != null) { + ElementImpl element = (ElementImpl) createCommentElement(data, isJSPTag); + if (element != null) { + if (!isEndTag(element)) { + element.setStartStructuredDocumentRegion(flatNode); + insertStartTag(element); + return; + } + + // end tag + element.setEndStructuredDocumentRegion(flatNode); + + String tagName = element.getTagName(); + String rootName = getFindRootName(tagName); + ElementImpl start = (ElementImpl) this.context.findStartTag(tagName, rootName); + if (start != null) { // start tag found + insertEndTag(start); + start.addEndTag(element); + return; + } + + // invalid end tag + insertNode(element); + return; + } + } + + CommentImpl comment = (CommentImpl) this.document.createComment(null); + if (comment == null) + return; + if (isJSPTag) + comment.setJSPTag(true); + comment.setStructuredDocumentRegion(flatNode); + insertNode(comment); + } + + /** + * insertDecl method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void insertDecl(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + boolean isDocType = false; + String name = null; + String publicId = null; + String systemId = null; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_DOCTYPE_DECLARATION) { + isDocType = true; + } else if (regionType == XMLRegionContext.XML_DOCTYPE_NAME) { + if (name == null) + name = flatNode.getText(region); + } else if (regionType == XMLRegionContext.XML_DOCTYPE_EXTERNAL_ID_PUBREF) { + if (publicId == null) + publicId = StructuredDocumentRegionUtil.getAttrValue(flatNode, region); + } else if (regionType == XMLRegionContext.XML_DOCTYPE_EXTERNAL_ID_SYSREF) { + if (systemId == null) + systemId = StructuredDocumentRegionUtil.getAttrValue(flatNode, region); + } + } + + // invalid declaration + if (!isDocType) { + insertInvalidDecl(flatNode); + return; + } + + DocumentTypeImpl docType = (DocumentTypeImpl) this.document.createDoctype(name); + if (docType == null) + return; + if (publicId != null) + docType.setPublicId(publicId); + if (systemId != null) + docType.setSystemId(systemId); + docType.setStructuredDocumentRegion(flatNode); + insertNode(docType); + } + + /** + * insertEndTag method + * + * @param element + * org.w3c.dom.Element + */ + private void insertEndTag(Element element) { + if (element == null) + return; + + Node newParent = element.getParentNode(); + if (newParent == null) + return; // error + + if (!((ElementImpl) element).isContainer()) { + // just update context + Node elementNext = element.getNextSibling(); + if (elementNext != null) + this.context.setNextNode(elementNext); + else + this.context.setParentNode(newParent); + return; + } + + // promote children + Node newNext = element.getNextSibling(); + Node oldParent = this.context.getParentNode(); + if (oldParent == null) + return; // error + Node oldNext = this.context.getNextNode(); + promoteNodes(element, newParent, newNext, oldParent, oldNext); + + // update context + // re-check the next sibling + newNext = element.getNextSibling(); + if (newNext != null) + this.context.setNextNode(newNext); + else + this.context.setParentNode(newParent); + } + + /** + * insertEndTag method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void insertEndTag(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + String tagName = null; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + if (region.getType() == XMLRegionContext.XML_TAG_NAME || region.getType() == JSP_ROOT_TAG_NAME || region.getType() == JSP_DIRECTIVE_NAME) { + if (tagName == null) + tagName = flatNode.getText(region); + } + } + + if (tagName == null) { // invalid end tag + insertText(flatNode); // regard as invalid text + return; + } + + String rootName = getFindRootName(tagName); + ElementImpl start = (ElementImpl) this.context.findStartTag(tagName, rootName); + if (start != null) { // start tag found + insertEndTag(start); + start.setEndStructuredDocumentRegion(flatNode); + return; + } + + // invalid end tag + ElementImpl end = null; + try { + end = (ElementImpl) this.document.createElement(tagName); + } catch (DOMException ex) { + } + if (end == null) { // invalid end tag + insertText(flatNode); // regard as invalid text + return; + } + end.setEndStructuredDocumentRegion(flatNode); + insertNode(end); + } + + /** + * insertEntityRef method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void insertEntityRef(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + String name = null; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_ENTITY_REFERENCE || regionType == XMLRegionContext.XML_CHAR_REFERENCE) { + if (name == null) + name = StructuredDocumentRegionUtil.getEntityRefName(flatNode, region); + } + } + + if (name == null) { // invalid entity + insertText(flatNode); + return; + } + + String value = this.document.getCharValue(name); + if (value != null) { // character entity + TextImpl text = (TextImpl) this.context.findPreviousText(); + if (text != null) { // existing text found + // do not append data + text.appendStructuredDocumentRegion(flatNode); + // notify the change + text.notifyValueChanged(); + return; + } + + // new text + text = (TextImpl) this.document.createTextNode(null); + if (text == null) + return; + text.setStructuredDocumentRegion(flatNode); + insertNode(text); + return; + } + + // general entity reference + EntityReferenceImpl ref = null; + try { + ref = (EntityReferenceImpl) this.document.createEntityReference(name); + } catch (DOMException ex) { + } + if (ref == null) { // entity reference might not be supported + insertText(flatNode); // regard as invalid text + return; + } + + ref.setStructuredDocumentRegion(flatNode); + insertNode(ref); + } + + /** + * insertInvalidDecl method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void insertInvalidDecl(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + ElementImpl element = null; + try { + element = (ElementImpl) this.document.createElement("!");//$NON-NLS-1$ + } catch (DOMException ex) { + } + if (element == null) { // invalid tag + insertText(flatNode); // regard as invalid text + return; + } + element.setEmptyTag(true); + element.setStartStructuredDocumentRegion(flatNode); + insertNode(element); + } + + /** + * insertJSPTag method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void insertJSPTag(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + String tagName = null; + AttrImpl attr = null; + Vector attrNodes = null; + boolean isCloseTag = false; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == JSP_SCRIPTLET_OPEN) { + tagName = JSPTag.JSP_SCRIPTLET; + } else if (regionType == JSP_EXPRESSION_OPEN) { + tagName = JSPTag.JSP_EXPRESSION; + } else if (regionType == JSP_DECLARATION_OPEN) { + tagName = JSPTag.JSP_DECLARATION; + } else if (regionType == JSP_DIRECTIVE_OPEN) { + tagName = JSPTag.JSP_DIRECTIVE; + } else if (regionType == JSP_DIRECTIVE_NAME) { + tagName += '.'; + tagName += flatNode.getText(region); + } else if (regionType == JSP_CLOSE) { + isCloseTag = true; + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME) { + String name = flatNode.getText(region); + attr = (AttrImpl) this.document.createAttribute(name); + if (attr != null) { + attr.setNameRegion(region); + if (attrNodes == null) + attrNodes = new Vector(); + attrNodes.addElement(attr); + } + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { + if (attr != null) { + attr.setEqualRegion(region); + } + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) { + if (attr != null) { + attr.setValueRegion(region); + attr = null; + } + } + } + + if (tagName == null) { + if (isCloseTag) { + // close JSP tag + Node parent = this.context.getParentNode(); + if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl start = (ElementImpl) parent; + if (start.isJSPContainer()) { + insertEndTag(start); + start.setEndStructuredDocumentRegion(flatNode); + return; + } + } + } + // invalid JSP tag + insertText(flatNode); // regard as invalid text + return; + } + + ElementImpl element = null; + try { + element = (ElementImpl) this.document.createElement(tagName); + } catch (DOMException ex) { + } + if (element == null) { // invalid tag + insertText(flatNode); // regard as invalid text + return; + } + if (attrNodes != null) { + Enumeration ae = attrNodes.elements(); + while (ae.hasMoreElements()) { + Attr a = (Attr) ae.nextElement(); + if (a == null) + continue; + element.appendAttributeNode(a); + } + } + element.setJSPTag(true); + element.setStartStructuredDocumentRegion(flatNode); + insertStartTag(element); + } + + /** + * insertNode method + * + * @param child + * org.w3c.dom.Node + */ + private void insertNode(Node node) { + if (node == null) + return; + if (this.context == null) + return; + + Node parent = this.context.getParentNode(); + if (parent == null) + return; + Node next = this.context.getNextNode(); + while (parent.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) parent; + if (canContain(element, node)) { + if (!element.hasStartTag() && next == element.getFirstChild()) { + // first child of implicit tag + // deletege to the parent + parent = element.getParentNode(); + if (parent == null) + return; + next = element; + this.context.setNextNode(next); + continue; + } + break; + } + parent = element.getParentNode(); + if (parent == null) + return; + + // promote siblings + Node newNext = element.getNextSibling(); + Node child = next; + while (child != null) { + Node nextChild = child.getNextSibling(); + element.removeChild(child); + parent.insertBefore(child, newNext); + child = nextChild; + } + + // leave the old end tag where it is + if (element.hasEndTag()) { + Element end = element.removeEndTag(); + if (end != null) { + parent.insertBefore(end, newNext); + if (next == null) + next = end; + } + } + if (!element.hasStartTag()) { + // implicit element + if (!element.hasChildNodes()) { + parent.removeChild(element); + } + } + + // update context + if (next == null) + next = newNext; + if (next != null) + this.context.setNextNode(next); + else + this.context.setParentNode(parent); + } + + insertNode(parent, node, next); + next = node.getNextSibling(); + if (next != null) + this.context.setNextNode(next); + else + this.context.setParentNode(node.getParentNode()); + } + + /** + */ + private void insertNode(Node parent, Node node, Node next) { + while (next != null && next.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl nextElement = (ElementImpl) next; + if (nextElement.hasStartTag()) + break; + if (!canBeImplicitTag(nextElement, node)) + break; + parent = nextElement; + next = nextElement.getFirstChild(); + } + Element implicitElement = createImplicitElement(parent, node); + if (implicitElement != null) + node = implicitElement; + parent.insertBefore(node, next); + } + + /** + * insertPI method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void insertPI(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + String target = null; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_PI_OPEN || regionType == XMLRegionContext.XML_PI_CLOSE) + continue; + if (target == null) + target = flatNode.getText(region); + } + + ProcessingInstructionImpl pi = (ProcessingInstructionImpl) this.document.createProcessingInstruction(target, null); + if (pi == null) + return; + pi.setStructuredDocumentRegion(flatNode); + insertNode(pi); + } + + /** + * insertStartTag method + * + * @param element + * org.w3c.dom.Element + */ + private void insertStartTag(Element element) { + if (element == null) + return; + if (this.context == null) + return; + + insertNode(element); + + ElementImpl newElement = (ElementImpl) element; + if (newElement.isEmptyTag() || !newElement.isContainer()) + return; + + // demote siblings + Node parent = this.context.getParentNode(); + if (parent == null) + return; // error + Node next = this.context.getNextNode(); + demoteNodes(element, element, parent, next); + + // update context + Node firstChild = element.getFirstChild(); + if (firstChild != null) + this.context.setNextNode(firstChild); + else + this.context.setParentNode(element); + } + + /** + * insertStartTag method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void insertStartTag(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + String tagName = null; + boolean isEmptyTag = false; + AttrImpl attr = null; + Vector attrNodes = null; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_TAG_NAME || regionType == JSP_ROOT_TAG_NAME || regionType == JSP_DIRECTIVE_NAME) { + if (tagName == null) + tagName = flatNode.getText(region); + } else if (regionType == XMLRegionContext.XML_EMPTY_TAG_CLOSE) { + isEmptyTag = true; + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME) { + String name = flatNode.getText(region); + attr = (AttrImpl) this.document.createAttribute(name); + if (attr != null) { + attr.setNameRegion(region); + if (attrNodes == null) + attrNodes = new Vector(); + attrNodes.addElement(attr); + } + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { + if (attr != null) { + attr.setEqualRegion(region); + } + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) { + if (attr != null) { + attr.setValueRegion(region); + attr = null; + } + } + } + + if (tagName == null) { // invalid start tag + insertText(flatNode); // regard as invalid text + return; + } + + ElementImpl element = null; + try { + element = (ElementImpl) this.document.createElement(tagName); + } catch (DOMException ex) { + } + if (element == null) { // invalid tag + insertText(flatNode); // regard as invalid text + return; + } + if (attrNodes != null) { + Enumeration ae = attrNodes.elements(); + while (ae.hasMoreElements()) { + Attr a = (Attr) ae.nextElement(); + if (a == null) + continue; + element.appendAttributeNode(a); + } + } + if (isEmptyTag) + element.setEmptyTag(true); + element.setStartStructuredDocumentRegion(flatNode); + insertStartTag(element); + } + + /** + * insertStructuredDocumentRegion method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void insertStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + String regionType = StructuredDocumentRegionUtil.getFirstRegionType(flatNode); + if (regionType == XMLRegionContext.XML_TAG_OPEN) { + insertStartTag(flatNode); + } else if (regionType == XMLRegionContext.XML_END_TAG_OPEN) { + insertEndTag(flatNode); + } else if (regionType == XMLRegionContext.XML_COMMENT_OPEN || regionType == JSP_COMMENT_OPEN) { + insertComment(flatNode); + } else if (regionType == XMLRegionContext.XML_ENTITY_REFERENCE || regionType == XMLRegionContext.XML_CHAR_REFERENCE) { + insertEntityRef(flatNode); + } else if (regionType == XMLRegionContext.XML_DECLARATION_OPEN) { + insertDecl(flatNode); + } else if (regionType == XMLRegionContext.XML_PI_OPEN) { + insertPI(flatNode); + } else if (regionType == XMLRegionContext.XML_CDATA_OPEN) { + insertCDATASection(flatNode); + } else if (regionType == JSP_SCRIPTLET_OPEN || regionType == JSP_EXPRESSION_OPEN || regionType == JSP_DECLARATION_OPEN || regionType == JSP_DIRECTIVE_OPEN || regionType == JSP_CLOSE) { + insertJSPTag(flatNode); + } else { + insertText(flatNode); + } + } + + /** + * insertText method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void insertText(IStructuredDocumentRegion flatNode) { + TextImpl text = (TextImpl) this.context.findPreviousText(); + if (text != null) { // existing text found + text.appendStructuredDocumentRegion(flatNode); + // notify the change + text.notifyValueChanged(); + return; + } + + // new text + text = (TextImpl) this.document.createTextNode(null); + if (text == null) + return; + text.setStructuredDocumentRegion(flatNode); + insertNode(text); + } + + /** + */ + protected boolean isEndTag(XMLElement element) { + if (this.adapter != null) { + return this.adapter.isEndTag(element); + } + return element.isEndTag(); + } + + /** + */ + private void promoteNodes(Node root, Node newParent, Node newNext, Node oldParent, Node next) { + ElementImpl newElement = null; + if (newParent.getNodeType() == Node.ELEMENT_NODE) { + newElement = (ElementImpl) newParent; + } + + Node rootParent = root.getParentNode(); + while (oldParent != rootParent) { + while (next != null) { + boolean done = false; + boolean endTag = false; + if (next.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl nextElement = (ElementImpl) next; + if (!nextElement.hasStartTag()) { + Node nextChild = nextElement.getFirstChild(); + if (nextChild != null) { + // promote children + next = nextChild; + oldParent = nextElement; + continue; + } + + if (nextElement.hasEndTag()) { + if (nextElement.matchEndTag(newElement)) { + endTag = true; + } + } else { + // remove implicit element + next = nextElement.getNextSibling(); + oldParent.removeChild(nextElement); + done = true; + } + } + } + + if (!done) { + if (!endTag && newElement != null && !canContain(newElement, next)) { + newParent = newElement.getParentNode(); + if (newParent == null) + return; // error + Node elementNext = newElement.getNextSibling(); + // promote siblings + promoteNodes(newElement, newParent, elementNext, newElement, newNext); + newNext = newElement.getNextSibling(); + if (newParent.getNodeType() == Node.ELEMENT_NODE) { + newElement = (ElementImpl) newParent; + } else { + newElement = null; + } + continue; + } + + Node child = next; + next = next.getNextSibling(); + oldParent.removeChild(child); + insertNode(newParent, child, newNext); + Node childParent = child.getParentNode(); + if (childParent != newParent) { + newParent = childParent; + newElement = (ElementImpl) newParent; + newNext = child.getNextSibling(); + } + } + } + + if (oldParent.getNodeType() != Node.ELEMENT_NODE) + return; + ElementImpl oldElement = (ElementImpl) oldParent; + oldParent = oldElement.getParentNode(); + if (oldParent == null) + return; // error + next = oldElement.getNextSibling(); + + if (oldElement.hasEndTag()) { + Element end = null; + if (!oldElement.hasChildNodes() && !oldElement.hasStartTag()) { + oldParent.removeChild(oldElement); + end = oldElement; + } else { + end = oldElement.removeEndTag(); + } + if (end != null) { + insertNode(newParent, end, newNext); + Node endParent = end.getParentNode(); + if (endParent != newParent) { + newParent = endParent; + newElement = (ElementImpl) newParent; + newNext = end.getNextSibling(); + } + } + } + } + } + + /** + * removeEndTag method + * + * @param element + * org.w3c.dom.Element + */ + private void removeEndTag(Element element) { + if (element == null) + return; + if (this.context == null) + return; + + Node parent = element.getParentNode(); + if (parent == null) + return; // error + + if (!((ElementImpl) element).isContainer()) { + // just update context + Node elementNext = element.getNextSibling(); + if (elementNext != null) + this.context.setNextNode(elementNext); + else + this.context.setParentNode(parent); + return; + } + + // demote siblings + Node next = element.getNextSibling(); + ElementImpl newElement = (ElementImpl) element; + // find new parent + for (Node last = newElement.getLastChild(); last != null; last = last.getLastChild()) { + if (last.getNodeType() != Node.ELEMENT_NODE) + break; + ElementImpl lastElement = (ElementImpl) last; + if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer()) + break; + newElement = lastElement; + } + Node lastChild = newElement.getLastChild(); + demoteNodes(element, newElement, parent, next); + + // update context + Node newNext = null; + if (lastChild != null) + newNext = lastChild.getNextSibling(); + else + newNext = newElement.getFirstChild(); + if (newNext != null) + this.context.setNextNode(newNext); + else + this.context.setParentNode(newElement); + } + + /** + * Remove the specified node if it is no longer required implicit tag with + * remaining child nodes promoted. + */ + private Element removeImplicitElement(Node parent) { + if (parent == null) + return null; + if (parent.getNodeType() != Node.ELEMENT_NODE) + return null; + ElementImpl element = (ElementImpl) parent; + if (!element.isImplicitTag()) + return null; + if (canBeImplicitTag(element)) + return null; + + Node elementParent = element.getParentNode(); + if (elementParent == null) + return null; // error + Node firstChild = element.getFirstChild(); + Node child = firstChild; + Node elementNext = element.getNextSibling(); + while (child != null) { + Node nextChild = child.getNextSibling(); + element.removeChild(child); + elementParent.insertBefore(child, elementNext); + child = nextChild; + } + + // reset context + if (this.context.getParentNode() == element) { + Node oldNext = this.context.getNextNode(); + if (oldNext != null) { + this.context.setNextNode(oldNext); + } else { + if (elementNext != null) { + this.context.setNextNode(elementNext); + } else { + this.context.setParentNode(elementParent); + } + } + } else if (this.context.getNextNode() == element) { + if (firstChild != null) { + this.context.setNextNode(firstChild); + } else { + if (elementNext != null) { + this.context.setNextNode(elementNext); + } else { + this.context.setParentNode(elementParent); + } + } + } + + removeNode(element); + return element; + } + + /** + * removeNode method + * + * @param node + * org.w3c.dom.Node + */ + private void removeNode(Node node) { + if (node == null) + return; + if (this.context == null) + return; + + Node parent = node.getParentNode(); + if (parent == null) + return; + Node next = node.getNextSibling(); + Node prev = node.getPreviousSibling(); + + // update context + Node oldParent = this.context.getParentNode(); + if (node == oldParent) { + if (next != null) + this.context.setNextNode(next); + else + this.context.setParentNode(parent); + } else { + Node oldNext = this.context.getNextNode(); + if (node == oldNext) { + this.context.setNextNode(next); + } + } + + parent.removeChild(node); + + if (removeImplicitElement(parent) != null) + return; + + // demote sibling + if (prev != null && prev.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl newElement = (ElementImpl) prev; + if (!newElement.hasEndTag() && !newElement.isEmptyTag() && newElement.isContainer()) { + // find new parent + for (Node last = newElement.getLastChild(); last != null; last = last.getLastChild()) { + if (last.getNodeType() != Node.ELEMENT_NODE) + break; + ElementImpl lastElement = (ElementImpl) last; + if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer()) + break; + newElement = lastElement; + } + Node lastChild = newElement.getLastChild(); + demoteNodes(prev, newElement, parent, next); + + // update context + Node newNext = null; + if (lastChild != null) + newNext = lastChild.getNextSibling(); + else + newNext = newElement.getFirstChild(); + if (newNext != null) + this.context.setNextNode(newNext); + else + this.context.setParentNode(newElement); + } + } + } + + /** + * removeStartTag method + * + * @param element + * org.w3c.dom.Element + */ + private void removeStartTag(Element element) { + if (element == null) + return; + if (this.context == null) + return; + + // for implicit tag + ElementImpl oldElement = (ElementImpl) element; + if (canBeImplicitTag(oldElement)) { + Node newParent = null; + Node prev = oldElement.getPreviousSibling(); + if (prev != null && prev.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl prevElement = (ElementImpl) prev; + if (!prevElement.hasEndTag()) { + if (prevElement.hasStartTag() || prevElement.matchTagName(oldElement.getTagName())) { + newParent = prevElement; + } + } + } + if (newParent == null) { + // this element should stay as implicit tag + // just remove all attributes + oldElement.removeStartTag(); + + // update context + Node child = oldElement.getFirstChild(); + if (child != null) { + this.context.setNextNode(child); + } else if (oldElement.hasEndTag()) { + this.context.setParentNode(oldElement); + } + return; + } + } + // for comment tag + if (oldElement.isCommentTag()) + oldElement.removeStartTag(); + + // promote children + Node elementParent = element.getParentNode(); + Node parent = elementParent; + if (parent == null) + return; + Node first = element.getFirstChild(); + Node firstElement = null; // for the case first is removed as end tag + if (first != null) { + // find new parent for children + ElementImpl newElement = null; + for (Node last = element.getPreviousSibling(); last != null; last = last.getLastChild()) { + if (last.getNodeType() != Node.ELEMENT_NODE) + break; + ElementImpl lastElement = (ElementImpl) last; + if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer()) + break; + newElement = lastElement; + } + Node next = first; + if (newElement != null) { + while (next != null) { + if (!newElement.hasEndTag() && newElement.hasStartTag() && next.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl nextElement = (ElementImpl) next; + if (!nextElement.hasStartTag() && nextElement.hasEndTag() && nextElement.matchEndTag(newElement)) { + // stop at the matched invalid end tag + Node elementChild = nextElement.getFirstChild(); + while (elementChild != null) { + Node nextChild = elementChild.getNextSibling(); + nextElement.removeChild(elementChild); + newElement.appendChild(elementChild); + elementChild = nextChild; + } + + next = nextElement.getNextSibling(); + element.removeChild(nextElement); + newElement.addEndTag(nextElement); + if (nextElement == first) + firstElement = newElement; + + Node newParent = newElement.getParentNode(); + if (newParent == parent) + break; + if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE) + break; // error + newElement = (ElementImpl) newParent; + continue; + } + } + if (!canContain(newElement, next)) { + Node newParent = newElement.getParentNode(); + if (newParent == parent) + break; + if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE) + break; // error + newElement = (ElementImpl) newParent; + continue; + } + Node child = next; + next = next.getNextSibling(); + element.removeChild(child); + newElement.appendChild(child); + } + newElement = null; + } + if (parent.getNodeType() == Node.ELEMENT_NODE) { + newElement = (ElementImpl) parent; + } + while (next != null) { + if (newElement == null || canContain(newElement, next)) { + Node child = next; + next = next.getNextSibling(); + element.removeChild(child); + parent.insertBefore(child, element); + continue; + } + + parent = newElement.getParentNode(); + if (parent == null) + return; + + // promote siblings + Node newNext = newElement.getNextSibling(); + Node child = element; + while (child != null) { + Node nextChild = child.getNextSibling(); + newElement.removeChild(child); + parent.insertBefore(child, newNext); + child = nextChild; + } + + // leave the old end tag where it is + if (newElement.hasEndTag()) { + Element end = newElement.removeEndTag(); + if (end != null) { + parent.insertBefore(end, newNext); + } + } + if (!newElement.hasStartTag()) { + // implicit element + if (!newElement.hasChildNodes()) { + parent.removeChild(newElement); + } + } + + if (parent.getNodeType() == Node.ELEMENT_NODE) { + newElement = (ElementImpl) parent; + } else { + newElement = null; + } + } + } + + Node newNext = element; + Node startElement = null; // for the case element is removed as end + // tag + if (oldElement.hasEndTag()) { + // find new parent for invalid end tag and siblings + ElementImpl newElement = null; + for (Node last = element.getPreviousSibling(); last != null; last = last.getLastChild()) { + if (last.getNodeType() != Node.ELEMENT_NODE) + break; + ElementImpl lastElement = (ElementImpl) last; + if (lastElement.hasEndTag() || lastElement.isEmptyTag() || !lastElement.isContainer()) + break; + newElement = lastElement; + } + if (newElement != null) { + // demote invalid end tag and sibling + Node next = element; + while (next != null) { + if (!newElement.hasEndTag() && newElement.hasStartTag() && next.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl nextElement = (ElementImpl) next; + if (!nextElement.hasStartTag() && nextElement.hasEndTag() && nextElement.matchEndTag(newElement)) { + // stop at the matched invalid end tag + Node elementChild = nextElement.getFirstChild(); + while (elementChild != null) { + Node nextChild = elementChild.getNextSibling(); + nextElement.removeChild(elementChild); + newElement.appendChild(elementChild); + elementChild = nextChild; + } + + next = nextElement.getNextSibling(); + parent.removeChild(nextElement); + newElement.addEndTag(nextElement); + if (nextElement == newNext) + startElement = newElement; + + Node newParent = newElement.getParentNode(); + if (newParent == parent) + break; + if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE) + break; // error + newElement = (ElementImpl) newParent; + continue; + } + } + if (!canContain(newElement, next)) { + Node newParent = newElement.getParentNode(); + if (newParent == parent) + break; + if (newParent == null || newParent.getNodeType() != Node.ELEMENT_NODE) + break; // error + newElement = (ElementImpl) newParent; + continue; + } + Node child = next; + next = next.getNextSibling(); + parent.removeChild(child); + if (child == oldElement) { + if (!oldElement.isCommentTag()) { + // clone (re-create) end tag + Element end = oldElement.removeEndTag(); + if (end != null) { + child = end; + newNext = end; + } + } + } + newElement.appendChild(child); + } + } else { + if (!oldElement.isCommentTag()) { + // clone (re-create) end tag + Element end = oldElement.removeEndTag(); + if (end != null) { + parent.insertBefore(end, oldElement); + parent.removeChild(oldElement); + newNext = end; + } + } + } + } else { + newNext = oldElement.getNextSibling(); + parent.removeChild(oldElement); + } + + // update context + Node oldParent = this.context.getParentNode(); + Node oldNext = this.context.getNextNode(); + if (element == oldParent) { + if (oldNext != null) { + this.context.setNextNode(oldNext); // reset for new parent + } else if (newNext != null) { + this.context.setNextNode(newNext); + } else { + this.context.setParentNode(parent); + } + } else if (element == oldNext) { + if (firstElement != null) { + this.context.setParentNode(firstElement); + } else if (first != null) { + this.context.setNextNode(first); + } else if (startElement != null) { + this.context.setParentNode(startElement); + } else { + this.context.setNextNode(newNext); + } + } + + removeImplicitElement(elementParent); + } + + /** + * removeStructuredDocumentRegion method + * + * @param oldStructuredDocumentRegion + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void removeStructuredDocumentRegion(IStructuredDocumentRegion oldStructuredDocumentRegion) { + NodeImpl next = (NodeImpl) this.context.getNextNode(); + if (next != null) { + short nodeType = next.getNodeType(); + if (nodeType != Node.ELEMENT_NODE) { + IStructuredDocumentRegion flatNode = next.getStructuredDocumentRegion(); + if (flatNode == oldStructuredDocumentRegion) { + removeNode(next); + return; + } + if (nodeType != Node.TEXT_NODE) { + throw new StructuredDocumentRegionManagementException(); + } + if (flatNode == null) { + // this is the case for empty Text + // remove and continue + removeNode(next); + removeStructuredDocumentRegion(oldStructuredDocumentRegion); + return; + } + TextImpl text = (TextImpl) next; + boolean isShared = text.isSharingStructuredDocumentRegion(oldStructuredDocumentRegion); + if (isShared) { + // make sure there is next Text node sharing this + TextImpl nextText = (TextImpl) this.context.findNextText(); + if (nextText == null || !nextText.hasStructuredDocumentRegion(oldStructuredDocumentRegion)) { + isShared = false; + } + } + oldStructuredDocumentRegion = text.removeStructuredDocumentRegion(oldStructuredDocumentRegion); + if (oldStructuredDocumentRegion == null) { + throw new StructuredDocumentRegionManagementException(); + } + if (text.getStructuredDocumentRegion() == null) { + // this is the case partial IStructuredDocumentRegion is + // removed + removeNode(text); + } else { + // notify the change + text.notifyValueChanged(); + } + // if shared, continue to remove IStructuredDocumentRegion + // from them + if (isShared) + removeStructuredDocumentRegion(oldStructuredDocumentRegion); + return; + } + + ElementImpl element = (ElementImpl) next; + if (element.hasStartTag()) { + IStructuredDocumentRegion flatNode = element.getStartStructuredDocumentRegion(); + if (flatNode != oldStructuredDocumentRegion) { + throw new StructuredDocumentRegionManagementException(); + } + if (element.hasEndTag() || element.hasChildNodes()) { + element.setStartStructuredDocumentRegion(null); + removeStartTag(element); + } else { + removeNode(element); + } + } else { + Node child = element.getFirstChild(); + if (child != null) { + this.context.setNextNode(child); + removeStructuredDocumentRegion(oldStructuredDocumentRegion); + return; + } + + if (!element.hasEndTag()) { + // implicit element + removeNode(element); + removeStructuredDocumentRegion(oldStructuredDocumentRegion); + return; + } + + IStructuredDocumentRegion flatNode = element.getEndStructuredDocumentRegion(); + if (flatNode != oldStructuredDocumentRegion) { + throw new StructuredDocumentRegionManagementException(); + } + removeNode(element); + } + return; + } + + Node parent = this.context.getParentNode(); + if (parent == null || parent.getNodeType() != Node.ELEMENT_NODE) { + throw new StructuredDocumentRegionManagementException(); + } + + ElementImpl end = (ElementImpl) parent; + if (end.hasEndTag()) { + IStructuredDocumentRegion flatNode = end.getEndStructuredDocumentRegion(); + if (flatNode != oldStructuredDocumentRegion) { + throw new StructuredDocumentRegionManagementException(); + } + if (!end.hasStartTag() && !end.hasChildNodes()) { + this.context.setNextNode(end); + removeNode(end); + } else { + end.setEndStructuredDocumentRegion(null); + removeEndTag(end); + } + return; + } + + next = (NodeImpl) end.getNextSibling(); + if (next != null) { + this.context.setNextNode(next); + removeStructuredDocumentRegion(oldStructuredDocumentRegion); + return; + } + + parent = (NodeImpl) end.getParentNode(); + if (parent != null) { + this.context.setParentNode(parent); + removeStructuredDocumentRegion(oldStructuredDocumentRegion); + return; + } + } + + /** + * replaceRegions method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + * @param newRegions + * java.util.Vector + * @param oldRegions + * java.util.Vector + */ + void replaceRegions(IStructuredDocumentRegion flatNode, ITextRegionList newRegions, ITextRegionList oldRegions) { + if (flatNode == null) + return; + if (this.document == null) + return; + this.context = new XMLModelContext(this.document); + + // optimize typical cases + String regionType = StructuredDocumentRegionUtil.getFirstRegionType(flatNode); + if (regionType == XMLRegionContext.XML_TAG_OPEN) { + changeStartTag(flatNode, newRegions, oldRegions); + } else if (regionType == XMLRegionContext.XML_END_TAG_OPEN) { + changeEndTag(flatNode, newRegions, oldRegions); + } else { + changeStructuredDocumentRegion(flatNode); + } + } + + /** + * replaceStructuredDocumentRegions method + * + * @param newStructuredDocumentRegions + * com.ibm.sed.structuredDocument.IStructuredDocumentRegionList + * @param oldStructuredDocumentRegions + * com.ibm.sed.structuredDocument.IStructuredDocumentRegionList + */ + void replaceStructuredDocumentRegions(IStructuredDocumentRegionList newStructuredDocumentRegions, IStructuredDocumentRegionList oldStructuredDocumentRegions) { + if (this.document == null) + return; + this.context = new XMLModelContext(this.document); + + int newCount = (newStructuredDocumentRegions != null ? newStructuredDocumentRegions.getLength() : 0); + int oldCount = (oldStructuredDocumentRegions != null ? oldStructuredDocumentRegions.getLength() : 0); + + if (oldCount > 0) { + setupContext(oldStructuredDocumentRegions.item(0)); + // Node startParent = this.context.getParentNode(); + + Enumeration e = oldStructuredDocumentRegions.elements(); + while (e.hasMoreElements()) { + IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) e.nextElement(); + if (flatNode == null) + continue; + removeStructuredDocumentRegion(flatNode); + } + } else { + if (newCount == 0) + return; + setupContext(newStructuredDocumentRegions.item(0)); + } + // make sure the parent is set to deepest level + // when end tag has been removed + this.context.setLast(); + + if (newCount > 0) { + Enumeration e = newStructuredDocumentRegions.elements(); + while (e.hasMoreElements()) { + IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) e.nextElement(); + if (flatNode == null) + continue; + insertStructuredDocumentRegion(flatNode); + } + } + + cleanupText(); + cleanupEndTag(); + } + + /** + * setupContext method + * + * @param flatNode + * com.ibm.sed.structuredDocument.IStructuredDocumentRegion + */ + private void setupContext(IStructuredDocumentRegion startStructuredDocumentRegion) { + int offset = startStructuredDocumentRegion.getStart(); + if (offset < 0) + return; + NodeImpl root = (NodeImpl) this.context.getRootNode(); + if (root == null) + return; + + if (offset == 0) { + // at the beggining of document + Node child = root.getFirstChild(); + if (child != null) + this.context.setNextNode(child); + else + this.context.setParentNode(root); + return; + } + + NodeImpl node = (NodeImpl) root.getNodeAt(offset); + if (node == null) { + // might be at the end of document + this.context.setParentNode(root); + this.context.setLast(); + return; + } + + if (offset == node.getStartOffset()) { + this.context.setNextNode(node); + return; + } + + if (node.getNodeType() == Node.TEXT_NODE) { + TextImpl text = (TextImpl) node; + Text nextText = text.splitText(startStructuredDocumentRegion); + // notify the change + text.notifyValueChanged(); + if (nextText == null) + return; // error + this.context.setNextNode(nextText); + return; + } + + for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { + if (offset >= ((NodeImpl) child).getEndOffset()) + continue; + this.context.setNextNode(child); + return; + } + this.context.setParentNode(node); + this.context.setLast(); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelUpdater.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelUpdater.java new file mode 100644 index 0000000000..c9cab984c8 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/document/XMLModelUpdater.java @@ -0,0 +1,1647 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.document; + + + +import java.util.Enumeration; +import java.util.Iterator; + +import org.eclipse.wst.sse.core.text.IStructuredDocument; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.xml.core.document.JSPTag; +import org.eclipse.wst.xml.core.document.XMLElement; +import org.eclipse.wst.xml.core.document.XMLGenerator; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.eclipse.wst.xml.core.jsp.model.parser.temp.XMLJSPRegionContexts; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; +import org.w3c.dom.Attr; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.Text; + + +/** + * XMLModelUpdater class + */ +public class XMLModelUpdater implements XMLJSPRegionContexts { + private int diff = 0; + private int gapLength = 0; + private int gapOffset = 0; + private IStructuredDocumentRegion gapStructuredDocumentRegion = null; + private XMLGenerator generator = null; + private XMLModelImpl model = null; + private NodeImpl nextNode = null; + private NodeImpl parentNode = null; + + protected XMLModelUpdater(XMLModelImpl model) { + super(); + + if (model != null) { + this.model = model; + this.generator = model.getGenerator(); + } + } + + /** + * changeAttrValue method + * + * @param attrNode + * org.w3c.dom.Attr + */ + private void changeAttrName(Attr attrNode) { + if (attrNode == null) + return; + + AttrImpl attr = (AttrImpl) attrNode; + ElementImpl element = (ElementImpl) attr.getOwnerElement(); + if (element == null) + return; + + if (element.isCommentTag()) { + changeStartTag(element); + return; + } + + int offset = element.getStartOffset(); + int start = offset; + int end = offset; + + String name = attr.getName(); + if (name == null) + name = new String(); + ITextRegion nameRegion = attr.getNameRegion(); + if (nameRegion == null) + return; // error + start += nameRegion.getStart(); + // use getTextEnd() because getEnd() may include the tailing spaces + end += nameRegion.getTextEnd(); + + replaceSource(name, start, end); + } + + /** + * changeAttrValue method + * + * @param attrNode + * org.w3c.dom.Attr + */ + private void changeAttrValue(Attr attrNode) { + if (attrNode == null) + return; + + AttrImpl attr = (AttrImpl) attrNode; + ElementImpl element = (ElementImpl) attr.getOwnerElement(); + if (element == null) + return; + + if (element.isCommentTag()) { + changeStartTag(element); + return; + } + + int offset = element.getStartOffset(); + int start = offset; + int end = offset; + + String value = null; + ITextRegion valueRegion = attr.getValueRegion(); + if (valueRegion != null) { + char quote = 0; // no quote preference + // DW: 4/16/2003 due to change in structuredDocument ... we need a + // flatnode to + // get at region values. For now I'll assume this is always the + // first + // flatnode .. may need to make smarter later (e.g. to search for + // the flatnode that this.valueRegion belongs to. + IStructuredDocumentRegion documentRegion = element.getFirstStructuredDocumentRegion(); + String oldValue = documentRegion.getText(valueRegion); + if (oldValue != null && oldValue.length() > 0) { + char firstChar = oldValue.charAt(0); + if (firstChar == '"' || firstChar == '\'') { + quote = firstChar; + } + } + + ITextRegion startRegion = valueRegion; + + value = this.generator.generateAttrValue(attr, quote); + if (value == null) { + value = new String(); + // remove equal too + ITextRegion equalRegion = attr.getEqualRegion(); + if (equalRegion != null) + startRegion = equalRegion; + } + attr.setValueRegion(valueRegion); // reset value + + start += startRegion.getStart(); + // use getTextEnd() because getEnd() may include the tailing + // spaces + end += valueRegion.getTextEnd(); + } else { + ITextRegion equalRegion = attr.getEqualRegion(); + + value = this.generator.generateAttrValue(attr); + if (value == null) { + if (equalRegion == null) + return; // nothng to do + value = new String(); + // remove equal + start += equalRegion.getStart(); + end += equalRegion.getTextEnd(); + } else { + if (equalRegion != null) { + // use getTextEnd() because getEnd() may include the + // tailing spaces + start += equalRegion.getTextEnd(); + } else { + ITextRegion nameRegion = attr.getNameRegion(); + if (nameRegion == null) + return; // must never happen + // use getTextEnd() because getEnd() may include the + // tailing spaces + start += nameRegion.getTextEnd(); + value = '=' + value; + } + end = start; + } + } + + replaceSource(value, start, end); + } + + /** + */ + void changeEndTag(Element element) { + String source = this.generator.generateEndTag(element); + if (source == null) + return; + int length = source.length(); + if (length == 0) + return; + + ElementImpl impl = (ElementImpl) element; + int offset = impl.getEndStartOffset(); + int start = offset; + int end = offset; + if (impl.hasEndTag()) { + end = impl.getEndOffset(); + this.gapStructuredDocumentRegion = impl.getEndStructuredDocumentRegion(); + impl.setEndStructuredDocumentRegion(new StructuredDocumentRegionProxy(offset, length)); + } + + replaceSource(source, start, end); + } + + /** + * changeName method + * + * @param node + * org.w3c.dom.Node + */ + void changeName(Node node) { + if (node == null) + return; + if (getStructuredDocument() == null) + return; + + // support changing name of attribute for setPrefix() + short nodeType = node.getNodeType(); + if (nodeType == Node.ATTRIBUTE_NODE) { + changeAttrName((Attr) node); + return; + } + + // not supported + return; + } + + void changeRegion(IStructuredDocumentRegion flatNode, ITextRegion region) { + // future_TODO: optimize + + NodeImpl root = (NodeImpl) this.model.getDocument(); + this.parentNode = root; + this.nextNode = (NodeImpl) root.getFirstChild(); + + removeGapStructuredDocumentRegion(flatNode); + insertGapStructuredDocumentRegionBefore(flatNode.getStart()); + changeStructuredDocumentRegion(flatNode); + insertGapStructuredDocumentRegionAfter(flatNode.getEnd()); + } + + /** + * This is a fallback method to regenerate the start tag. + */ + void changeStartTag(Element element) { + if (element == null) + return; + ElementImpl impl = (ElementImpl) element; + + if (!impl.hasStartTag() && !impl.hasEndTag()) { + // need to generate the start and the end tags + Node parent = element.getParentNode(); + if (parent != null) { + replaceChild(parent, element, element); + return; + } + // else error + } + + String source = this.generator.generateStartTag(element); + if (source == null) + return; + int length = source.length(); + if (length == 0) + return; + + int offset = impl.getStartOffset(); + int start = offset; + int end = offset; + if (impl.hasStartTag()) { + end = impl.getStartEndOffset(); + this.gapStructuredDocumentRegion = impl.getStartStructuredDocumentRegion(); + } + impl.setStartStructuredDocumentRegion(new StructuredDocumentRegionProxy(offset, length)); + + replaceSource(source, start, end); + } + + private void changeStructuredDocumentRegion(IStructuredDocumentRegion oldStructuredDocumentRegion) { + if (oldStructuredDocumentRegion == null) + return; // error + if (this.parentNode == null) + return; // error + + int oldOffset = oldStructuredDocumentRegion.getStart(); + int oldEnd = oldStructuredDocumentRegion.getEnd(); + boolean isEndTag = false; + + // find owner node + NodeImpl ownerNode = null; + while (this.parentNode != null) { + if (this.nextNode != null) { + IStructuredDocumentRegion nextStructuredDocumentRegion = this.nextNode.getStructuredDocumentRegion(); + if (nextStructuredDocumentRegion != null) { + if (nextStructuredDocumentRegion == oldStructuredDocumentRegion) { + ownerNode = this.nextNode; + break; + } + int nextOffset = nextStructuredDocumentRegion.getStart(); + if (nextOffset == oldOffset) { // found + ownerNode = this.nextNode; + break; + } + if (this.nextNode.getNodeType() == Node.TEXT_NODE) { + TextImpl text = (TextImpl) this.nextNode; + if (text.hasStructuredDocumentRegion(oldStructuredDocumentRegion)) { + ownerNode = this.nextNode; + break; + } + int nextEnd = nextStructuredDocumentRegion.getEnd(); + if (nextOffset < oldEnd && nextEnd > oldOffset) { + ownerNode = this.nextNode; + break; + } + } + } + + Node child = this.nextNode.getFirstChild(); + if (child != null) { + this.parentNode = this.nextNode; + this.nextNode = (NodeImpl) child; + continue; + } + + if (this.nextNode.getNodeType() == Node.ELEMENT_NODE) { + this.parentNode = this.nextNode; + this.nextNode = null; + continue; + } + + this.nextNode = (NodeImpl) this.nextNode.getNextSibling(); + if (this.nextNode != null) + continue; + } + + if (this.parentNode.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) this.parentNode; + IStructuredDocumentRegion endStructuredDocumentRegion = element.getEndStructuredDocumentRegion(); + if (endStructuredDocumentRegion != null) { + if (endStructuredDocumentRegion == oldStructuredDocumentRegion) { + ownerNode = this.parentNode; + isEndTag = true; + break; + } + int endOffset = endStructuredDocumentRegion.getStart(); + if (endOffset == oldOffset) { // found + ownerNode = this.parentNode; + isEndTag = true; + break; + } + } + } + + this.nextNode = (NodeImpl) this.parentNode.getNextSibling(); + this.parentNode = (NodeImpl) this.parentNode.getParentNode(); + } + if (ownerNode == null) + throw new StructuredDocumentRegionManagementException(); + + short nodeType = ownerNode.getNodeType(); + if (nodeType == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) ownerNode; + if (isEndTag) { + element.setEndStructuredDocumentRegion(oldStructuredDocumentRegion); + } else { + element.setStartStructuredDocumentRegion(oldStructuredDocumentRegion); + updateAttrRegions(element, oldStructuredDocumentRegion); + } + } else if (nodeType == Node.TEXT_NODE) { + TextImpl text = (TextImpl) ownerNode; + + IStructuredDocumentRegion flatNode = text.getStructuredDocumentRegion(); + if (flatNode == oldStructuredDocumentRegion) { + int newOffset = oldOffset; + int newEnd = oldEnd; + if (oldOffset == this.gapOffset) { + newOffset += this.diff; + } else { + newEnd = this.gapOffset; + } + int newLength = newEnd - newOffset; + IStructuredDocumentRegion newStructuredDocumentRegion = new StructuredDocumentRegionProxy(newOffset, newLength, oldStructuredDocumentRegion); + text.setStructuredDocumentRegion(newStructuredDocumentRegion); + + if (oldEnd > newEnd) { + this.nextNode = (NodeImpl) text.getNextSibling(); + changeStructuredDocumentRegion(oldStructuredDocumentRegion); + } + return; + } + + if (flatNode instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) flatNode; + int offset = proxy.getOffset(); + int end = offset + proxy.getLength(); + if (proxy.getStructuredDocumentRegion() == null) { + if (offset == oldOffset && end == oldEnd) { + text.setStructuredDocumentRegion(oldStructuredDocumentRegion); + } else { + if (end > oldEnd) { + StructuredDocumentRegionContainer container = new StructuredDocumentRegionContainer(); + container.appendStructuredDocumentRegion(oldStructuredDocumentRegion); + proxy.setOffset(oldEnd); + proxy.setLength(end - oldEnd); + container.appendStructuredDocumentRegion(proxy); + text.setStructuredDocumentRegion(container); + } else { + proxy.setStructuredDocumentRegion(oldStructuredDocumentRegion); + + if (end < oldEnd) { // to be shared + this.nextNode = (NodeImpl) text.getNextSibling(); + changeStructuredDocumentRegion(oldStructuredDocumentRegion); + } + } + } + return; + } + + if (offset >= this.gapOffset) { + proxy.setOffset(offset + this.diff); + end += this.diff; + } + if (end < oldEnd) { // to be shared + this.nextNode = (NodeImpl) text.getNextSibling(); + changeStructuredDocumentRegion(oldStructuredDocumentRegion); + return; + } + } else if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (content == null) + continue; // error + if (content == oldStructuredDocumentRegion) { + int newOffset = oldOffset; + int newEnd = oldEnd; + if (oldOffset == this.gapOffset) { + newOffset += this.diff; + } else { + newEnd = this.gapOffset; + } + int newLength = newEnd - newOffset; + IStructuredDocumentRegion newStructuredDocumentRegion = new StructuredDocumentRegionProxy(newOffset, newLength, oldStructuredDocumentRegion); + container.replaceStructuredDocumentRegion(newStructuredDocumentRegion, i); + + if (oldEnd > newEnd) { // to be shared + this.nextNode = (NodeImpl) text.getNextSibling(); + changeStructuredDocumentRegion(oldStructuredDocumentRegion); + } + return; + } + + if (content instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) content; + int offset = proxy.getOffset(); + int end = offset + proxy.getLength(); + if (end <= oldOffset) + continue; + if (proxy.getStructuredDocumentRegion() == null) { + if (offset == oldOffset && end == oldEnd) { + container.replaceStructuredDocumentRegion(oldStructuredDocumentRegion, i); + } else { + if (end > oldEnd) { + container.insertStructuredDocumentRegion(oldStructuredDocumentRegion, i); + proxy.setOffset(oldEnd); + proxy.setLength(end - oldEnd); + } else { + proxy.setStructuredDocumentRegion(oldStructuredDocumentRegion); + + if (end < oldEnd) { // to be shared + this.nextNode = (NodeImpl) text.getNextSibling(); + changeStructuredDocumentRegion(oldStructuredDocumentRegion); + } + } + } + return; + } + + if (offset >= this.gapOffset) { + proxy.setOffset(offset + this.diff); + end += this.diff; + } + if (end < oldEnd) { // to be shared + this.nextNode = (NodeImpl) text.getNextSibling(); + changeStructuredDocumentRegion(oldStructuredDocumentRegion); + return; + } + } + } + } else { + throw new StructuredDocumentRegionManagementException(); + } + } else { + ownerNode.setStructuredDocumentRegion(oldStructuredDocumentRegion); + } + } + + /** + */ + private void changeTextData(Text text) { + if (text == null) + return; + + String source = this.generator.generateSource(text); + if (source == null) + source = new String(); + int length = source.length(); + + TextImpl impl = (TextImpl) text; + int start = impl.getStartOffset(); + int end = impl.getEndOffset(); + int offset = start; + + // make sure previous tag is closed + Node prev = text.getPreviousSibling(); + if (prev != null) { + String preTag = getCloseTag((XMLNode) prev); + if (preTag != null && preTag.length() > 0) { + offset += preTag.length(); + source = preTag + source; + } + } else { + Node parent = text.getParentNode(); + if (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) parent; + String preTag = getStartCloseTag(element); + if (preTag != null && preTag.length() > 0) { + offset += preTag.length(); + StringBuffer buffer = new StringBuffer(); + buffer.append(preTag); + buffer.append(source); + if (text.getNextSibling() == null && !element.hasEndTag() && (element.isJSPContainer() || element.isCDATAContainer())) { + // need to generate the end tag + String postTag = this.generator.generateEndTag(element); + if (postTag != null) { + int postLength = postTag.length(); + if (postLength > 0) { + buffer.append(postTag); + int postOffset = offset + length; + IStructuredDocumentRegion flatNode = new StructuredDocumentRegionProxy(postOffset, postLength); + element.setEndStructuredDocumentRegion(flatNode); + } + } + } + source = buffer.toString(); + } + } + } + + this.gapStructuredDocumentRegion = impl.getStructuredDocumentRegion(); + IStructuredDocumentRegion newStructuredDocumentRegion = null; + if (length > 0) + newStructuredDocumentRegion = new StructuredDocumentRegionProxy(offset, length); + impl.setStructuredDocumentRegion(newStructuredDocumentRegion); + + replaceSource(source, start, end); + } + + /** + * changeValue method + * + * @param node + * org.w3c.dom.Node + */ + void changeValue(Node node) { + if (node == null) + return; + if (getStructuredDocument() == null) + return; + + short nodeType = node.getNodeType(); + if (nodeType == Node.TEXT_NODE) { + changeTextData((Text) node); + return; + } + if (nodeType == Node.ATTRIBUTE_NODE) { + changeAttrValue((Attr) node); + return; + } + if (nodeType == Node.ELEMENT_NODE) { + changeStartTag((Element) node); + return; + } + + String source = this.generator.generateSource(node); + if (source == null) + source = new String(); + int length = source.length(); + + NodeImpl impl = (NodeImpl) node; + int start = impl.getStartOffset(); + int end = impl.getEndOffset(); + + this.gapStructuredDocumentRegion = impl.getStructuredDocumentRegion(); + IStructuredDocumentRegion flatNode = null; + if (length > 0) + flatNode = new StructuredDocumentRegionProxy(start, length); + impl.setStructuredDocumentRegion(flatNode); + + replaceSource(source, start, end); + } + + /** + */ + private String getAttrValueClose(XMLElement element) { + if (element == null) + return null; + + IStructuredDocumentRegion flatNode = element.getStartStructuredDocumentRegion(); + if (flatNode == null) + return null; + ITextRegion region = StructuredDocumentRegionUtil.getLastRegion(flatNode); + if (region == null || region.getType() != XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) + return null; + String value = flatNode.getText(region); + if (value == null) + return null; + int length = value.length(); + if (length == 0) + return null; + + // check open JSP tag + boolean closeJSPTag = false; + int offset = value.indexOf(JSPTag.TAG_OPEN); + while (offset >= 0) { + offset = value.indexOf(JSPTag.TAG_CLOSE, offset + 2); + if (offset < 0) { + closeJSPTag = true; + break; + } + offset = value.indexOf(JSPTag.TAG_OPEN, offset + 2); + } + + // check quote + boolean closeQuote = false; + char firstChar = value.charAt(0); + if (firstChar == '"' || firstChar == '\'') { + if (closeJSPTag || length == 1 || value.charAt(length - 1) != firstChar) { + closeQuote = true; + } + } + + if (!closeJSPTag && !closeQuote) + return null; + + StringBuffer buffer = new StringBuffer(); + if (closeJSPTag) + buffer.append(JSPTag.TAG_CLOSE); + if (closeQuote) + buffer.append(firstChar); + return buffer.toString(); + } + + /** + * Gather close tags recursively. + */ + private String getCloseTag(XMLNode node) { + if (node == null || node.isClosed()) + return null; + + if (node.getNodeType() != Node.ELEMENT_NODE) { + return this.generator.generateCloseTag(node); + } + + ElementImpl element = (ElementImpl) node; + if (element.hasEndTag()) { + // end tag is not closed + return this.generator.generateCloseTag(element); + } + + // no end tag + int offset = element.getEndOffset(); + StringBuffer buffer = new StringBuffer(); + + XMLNode lastChild = (XMLNode) element.getLastChild(); + if (lastChild == null) { + if (!element.isStartTagClosed()) { + if (element.preferEmptyTag()) + element.setEmptyTag(true); + String closeTag = getStartCloseTag(element); + if (closeTag != null) { + int length = closeTag.length(); + if (length > 0) { + buffer.append(closeTag); + offset += length; + } + } + } + } else { + String closeTag = getCloseTag(lastChild); + if (closeTag != null) { + int length = closeTag.length(); + if (length > 0) { + buffer.append(closeTag); + offset += length; + } + } + } + + String endTag = this.generator.generateEndTag(element); + if (endTag != null) { + int length = endTag.length(); + if (length > 0) { + buffer.append(endTag); + IStructuredDocumentRegion flatNode = new StructuredDocumentRegionProxy(offset, length); + element.setEndStructuredDocumentRegion(flatNode); + } + } + + return buffer.toString(); + } + + /** + */ + private String getStartCloseTag(XMLElement element) { + if (element == null || element.isStartTagClosed()) + return null; + + StringBuffer buffer = new StringBuffer(); + String attrValueClose = getAttrValueClose(element); + if (attrValueClose != null) + buffer.append(attrValueClose); + String closeTag = this.generator.generateCloseTag(element); + if (closeTag != null) + buffer.append(closeTag); + return buffer.toString(); + } + + private IStructuredDocument getStructuredDocument() { + if (model == null) + return null; + return model.getStructuredDocument(); + } + + /** + */ + void initialize() { + this.gapStructuredDocumentRegion = null; + this.gapOffset = 0; + this.gapLength = 0; + this.diff = 0; + this.parentNode = null; + this.nextNode = null; + } + + private void insertGapStructuredDocumentRegionAfter(int endOffset) { + if (this.gapStructuredDocumentRegion == null) + return; + + if (this.gapStructuredDocumentRegion instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) this.gapStructuredDocumentRegion; + IStructuredDocumentRegion flatNode = proxy.getStructuredDocumentRegion(); + if (flatNode != null) + insertStructuredDocumentRegion(flatNode); + } else if (this.gapStructuredDocumentRegion instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) this.gapStructuredDocumentRegion; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (content == null) + continue; + if (content.getStart() < endOffset) + continue; + if (content instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) content; + IStructuredDocumentRegion flatNode = proxy.getStructuredDocumentRegion(); + if (flatNode != null) + insertStructuredDocumentRegion(flatNode); + } else { + insertStructuredDocumentRegion(content); + } + } + } else { + insertStructuredDocumentRegion(this.gapStructuredDocumentRegion); + } + } + + private void insertGapStructuredDocumentRegionBefore(int startOffset) { + if (this.gapStructuredDocumentRegion == null) + return; + + if (this.gapStructuredDocumentRegion instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) this.gapStructuredDocumentRegion; + IStructuredDocumentRegion flatNode = proxy.getStructuredDocumentRegion(); + if (flatNode != null) + insertStructuredDocumentRegion(flatNode); + } else if (this.gapStructuredDocumentRegion instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) this.gapStructuredDocumentRegion; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (content == null) + continue; + if (content.getStart() >= startOffset) + return; + if (content instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) content; + IStructuredDocumentRegion flatNode = proxy.getStructuredDocumentRegion(); + if (flatNode != null) + insertStructuredDocumentRegion(flatNode); + } else { + insertStructuredDocumentRegion(content); + } + } + } else { + insertStructuredDocumentRegion(this.gapStructuredDocumentRegion); + } + } + + /** + */ + private void insertStructuredDocumentRegion(IStructuredDocumentRegion newStructuredDocumentRegion) { + if (newStructuredDocumentRegion == null) + return; // error + if (this.parentNode == null) + return; // error + + int newOffset = newStructuredDocumentRegion.getStart(); + int newEnd = newStructuredDocumentRegion.getEnd(); + boolean isEndTag = false; + + // find owner node + NodeImpl ownerNode = null; + while (this.parentNode != null) { + if (this.nextNode != null) { + IStructuredDocumentRegion nextStructuredDocumentRegion = this.nextNode.getStructuredDocumentRegion(); + if (nextStructuredDocumentRegion != null) { + int nextOffset = nextStructuredDocumentRegion.getStart(); + if (nextOffset == newOffset) { // found + ownerNode = this.nextNode; + break; + } + if (this.nextNode.getNodeType() == Node.TEXT_NODE) { + int nextEnd = nextStructuredDocumentRegion.getEnd(); + if (nextOffset < newEnd && nextEnd > newOffset) { + ownerNode = this.nextNode; + break; + } + } + } + + Node child = this.nextNode.getFirstChild(); + if (child != null) { + this.parentNode = this.nextNode; + this.nextNode = (NodeImpl) child; + continue; + } + + if (this.nextNode.getNodeType() == Node.ELEMENT_NODE) { + this.parentNode = this.nextNode; + this.nextNode = null; + continue; + } + + this.nextNode = (NodeImpl) this.nextNode.getNextSibling(); + if (this.nextNode != null) + continue; + } + + if (this.parentNode.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) this.parentNode; + IStructuredDocumentRegion endStructuredDocumentRegion = element.getEndStructuredDocumentRegion(); + if (endStructuredDocumentRegion != null) { + int endOffset = endStructuredDocumentRegion.getStart(); + if (endOffset == newOffset) { // found + ownerNode = this.parentNode; + isEndTag = true; + break; + } + } + } + + this.nextNode = (NodeImpl) this.parentNode.getNextSibling(); + this.parentNode = (NodeImpl) this.parentNode.getParentNode(); + } + if (ownerNode == null) + throw new StructuredDocumentRegionManagementException(); + + short nodeType = ownerNode.getNodeType(); + if (nodeType == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) ownerNode; + if (isEndTag) { + element.setEndStructuredDocumentRegion(newStructuredDocumentRegion); + } else { + element.setStartStructuredDocumentRegion(newStructuredDocumentRegion); + updateAttrRegions(element, newStructuredDocumentRegion); + } + } else if (nodeType == Node.TEXT_NODE) { + TextImpl text = (TextImpl) ownerNode; + IStructuredDocumentRegion oldStructuredDocumentRegion = text.getStructuredDocumentRegion(); + if (oldStructuredDocumentRegion == null) { + throw new StructuredDocumentRegionManagementException(); + } + int oldOffset = oldStructuredDocumentRegion.getStart(); + int oldEnd = oldStructuredDocumentRegion.getEnd(); + if (oldOffset == newOffset && oldEnd == newEnd) { + text.setStructuredDocumentRegion(newStructuredDocumentRegion); + return; + } + + if (oldStructuredDocumentRegion instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) oldStructuredDocumentRegion; + if (oldEnd > newEnd) { + StructuredDocumentRegionContainer container = new StructuredDocumentRegionContainer(); + if (oldOffset == newOffset) { + container.appendStructuredDocumentRegion(newStructuredDocumentRegion); + } else { + StructuredDocumentRegionProxy newProxy = new StructuredDocumentRegionProxy(); + newProxy.setOffset(oldOffset); + newProxy.setLength(newEnd - oldOffset); + newProxy.setStructuredDocumentRegion(newStructuredDocumentRegion); + container.appendStructuredDocumentRegion(newProxy); + } + proxy.setOffset(newEnd); + proxy.setLength(oldEnd - newEnd); + container.appendStructuredDocumentRegion(proxy); + text.setStructuredDocumentRegion(container); + } else { + proxy.setStructuredDocumentRegion(newStructuredDocumentRegion); + + if (oldEnd < newEnd) { // to be shared + this.nextNode = (NodeImpl) text.getNextSibling(); + insertStructuredDocumentRegion(newStructuredDocumentRegion); + } + } + return; + } + + if (oldStructuredDocumentRegion instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) oldStructuredDocumentRegion; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (content == null) + continue; // error + int offset = content.getStart(); + int end = content.getEnd(); + if (end <= newOffset) + continue; + if (offset == newOffset && end == newEnd) { + container.replaceStructuredDocumentRegion(newStructuredDocumentRegion, i); + return; + } + + if (content instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) content; + if (end > newEnd) { + if (offset == newOffset) { + container.insertStructuredDocumentRegion(newStructuredDocumentRegion, i); + } else { + StructuredDocumentRegionProxy newProxy = new StructuredDocumentRegionProxy(); + newProxy.setOffset(offset); + newProxy.setLength(newEnd - offset); + newProxy.setStructuredDocumentRegion(newStructuredDocumentRegion); + container.insertStructuredDocumentRegion(newProxy, i); + } + proxy.setOffset(newEnd); + proxy.setLength(end - newEnd); + return; + } else { + proxy.setStructuredDocumentRegion(newStructuredDocumentRegion); + if (end == newEnd) + return; + } + } + } + + if (oldEnd < newEnd) { // to be shared + this.nextNode = (NodeImpl) text.getNextSibling(); + insertStructuredDocumentRegion(newStructuredDocumentRegion); + } + return; + } else { + throw new StructuredDocumentRegionManagementException(); + } + } else { + ownerNode.setStructuredDocumentRegion(newStructuredDocumentRegion); + } + } + + private void removeGapStructuredDocumentRegion(IStructuredDocumentRegion oldStructuredDocumentRegion) { + if (this.gapStructuredDocumentRegion == null) + return; + + if (this.gapStructuredDocumentRegion == oldStructuredDocumentRegion) { + this.gapStructuredDocumentRegion = null; + return; + } + + if (this.gapStructuredDocumentRegion instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) this.gapStructuredDocumentRegion; + IStructuredDocumentRegion flatNode = proxy.getStructuredDocumentRegion(); + if (flatNode == oldStructuredDocumentRegion) + this.gapStructuredDocumentRegion = null; + } else if (this.gapStructuredDocumentRegion instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) this.gapStructuredDocumentRegion; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (content == null) + continue; + if (content == oldStructuredDocumentRegion) { + if (count > 1) + container.removeStructuredDocumentRegion(i); + else + this.gapStructuredDocumentRegion = null; + return; + } + if (content instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) content; + if (proxy.getStructuredDocumentRegion() == oldStructuredDocumentRegion) { + if (count > 1) + container.removeStructuredDocumentRegion(i); + else + this.gapStructuredDocumentRegion = null; + return; + } + } + } + } + } + + private void removeStructuredDocumentRegion(IStructuredDocumentRegion oldStructuredDocumentRegion) { + if (oldStructuredDocumentRegion == null) + return; // error + if (this.parentNode == null) + return; // error + + int gapEnd = this.gapOffset + this.gapLength; + int oldOffset = oldStructuredDocumentRegion.getStart(); + int oldEnd = oldStructuredDocumentRegion.getEnd(); + if (oldOffset >= this.gapOffset && oldEnd <= gapEnd) + return; // do nothing + int oldLength = oldEnd - oldOffset; + if (oldOffset >= gapEnd) + oldOffset += this.diff; + + // find owner node + NodeImpl ownerNode = null; + ElementImpl ownerEndTag = null; + TextImpl ownerText = null; + while (this.parentNode != null) { + if (this.nextNode != null) { + if (this.nextNode.getStructuredDocumentRegion() == oldStructuredDocumentRegion) { + ownerNode = this.nextNode; + break; + } + if (this.nextNode.getNodeType() == Node.TEXT_NODE) { + TextImpl text = (TextImpl) this.nextNode; + if (text.hasStructuredDocumentRegion(oldStructuredDocumentRegion)) { + ownerNode = this.nextNode; + ownerText = text; + break; + } + } + + Node child = this.nextNode.getFirstChild(); + if (child != null) { + this.parentNode = this.nextNode; + this.nextNode = (NodeImpl) child; + continue; + } + + if (this.nextNode.getNodeType() == Node.ELEMENT_NODE) { + this.parentNode = this.nextNode; + this.nextNode = null; + continue; + } + + this.nextNode = (NodeImpl) this.nextNode.getNextSibling(); + if (this.nextNode != null) + continue; + } + + if (this.parentNode.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) this.parentNode; + if (element.getEndStructuredDocumentRegion() == oldStructuredDocumentRegion) { + ownerNode = this.parentNode; + ownerEndTag = element; + break; + } + } + + this.nextNode = (NodeImpl) this.parentNode.getNextSibling(); + this.parentNode = (NodeImpl) this.parentNode.getParentNode(); + } + if (ownerNode == null) + throw new StructuredDocumentRegionManagementException(); + + if (ownerText != null) { + IStructuredDocumentRegion flatNode = ownerText.getStructuredDocumentRegion(); + if (flatNode == oldStructuredDocumentRegion) { + IStructuredDocumentRegion newStructuredDocumentRegion = new StructuredDocumentRegionProxy(oldOffset, oldLength); + ownerText.setStructuredDocumentRegion(newStructuredDocumentRegion); + return; + } + + if (flatNode instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) flatNode; + if (proxy.getStructuredDocumentRegion() != oldStructuredDocumentRegion) { + throw new StructuredDocumentRegionManagementException(); + } + int offset = proxy.getOffset(); + int end = offset + proxy.getLength(); + if (offset >= this.gapOffset) { + proxy.setOffset(offset + this.diff); + } + proxy.setStructuredDocumentRegion(null); + if (end < oldEnd && (end < this.gapOffset || oldEnd > gapEnd)) { // has + // shared + removeStructuredDocumentRegion(oldStructuredDocumentRegion); + return; + } + } else if (flatNode instanceof StructuredDocumentRegionContainer) { + StructuredDocumentRegionContainer container = (StructuredDocumentRegionContainer) flatNode; + int count = container.getStructuredDocumentRegionCount(); + for (int i = 0; i < count; i++) { + IStructuredDocumentRegion content = container.getStructuredDocumentRegion(i); + if (content == null) + continue; // error + if (content == oldStructuredDocumentRegion) { + IStructuredDocumentRegion newStructuredDocumentRegion = new StructuredDocumentRegionProxy(oldOffset, oldLength); + container.replaceStructuredDocumentRegion(newStructuredDocumentRegion, i); + return; + } + + if (content instanceof StructuredDocumentRegionProxy) { + StructuredDocumentRegionProxy proxy = (StructuredDocumentRegionProxy) content; + if (proxy.getStructuredDocumentRegion() == oldStructuredDocumentRegion) { + int offset = proxy.getOffset(); + int end = offset + proxy.getLength(); + if (offset >= this.gapOffset) { + proxy.setOffset(offset + this.diff); + } + proxy.setStructuredDocumentRegion(null); + if (end < oldEnd && (end < this.gapOffset || oldEnd > gapEnd)) { // has + // shared + removeStructuredDocumentRegion(oldStructuredDocumentRegion); + return; + } + } + } + } + } else { + throw new StructuredDocumentRegionManagementException(); + } + } else { + IStructuredDocumentRegion newStructuredDocumentRegion = new StructuredDocumentRegionProxy(oldOffset, oldLength); + if (ownerEndTag != null) { + ownerEndTag.setEndStructuredDocumentRegion(newStructuredDocumentRegion); + } else { + ownerNode.setStructuredDocumentRegion(newStructuredDocumentRegion); + } + } + } + + /** + * replaceAttr method + * + * @param ownerElement + * org.w3c.dom.Element + * @param newAttr + * org.w3c.dom.Attr + * @param oldAttr + * org.w3c.dom.Attr + */ + void replaceAttr(Element ownerElement, Attr newAttr, Attr oldAttr) { + if (ownerElement == null) + return; + if (getStructuredDocument() == null) + return; + + ElementImpl element = (ElementImpl) ownerElement; + if (!element.hasStartTag()) { + changeStartTag(element); + return; + } + if (element.isCommentTag()) { + changeStartTag(element); + return; + } + + int offset = element.getStartOffset(); + int start = offset; + int end = offset; + + boolean insertSpace = false; + String attrValueClose = null; + if (oldAttr != null) { + AttrImpl impl = (AttrImpl) oldAttr; + ITextRegion nameRegion = impl.getNameRegion(); + if (nameRegion == null) + return; // must never happen + ITextRegion lastRegion = impl.getValueRegion(); + if (lastRegion != null) { + end += lastRegion.getEnd(); + } else { + lastRegion = impl.getEqualRegion(); + if (lastRegion != null) { + end += lastRegion.getEnd(); + } else { + end += nameRegion.getEnd(); + lastRegion = nameRegion; + } + } + // check there are extra space before the last attribute + IStructuredDocumentRegion flatNode = element.getStartStructuredDocumentRegion(); + if (flatNode == null) + return; // must never happen + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; // must never happen + ITextRegion prevRegion = null; + ITextRegion nextRegion = null; + for (int i = 0; i < regions.size(); i++) { + ITextRegion region = regions.get(i); + if (region == nameRegion) { + if (i > 0) { + prevRegion = regions.get(i - 1); + } + } + if (region == lastRegion) { + if (i + 1 < regions.size()) { + nextRegion = regions.get(i + 1); + } + break; + } + } + boolean isLastAttr = false; + if (nextRegion != null) { + String regionType = nextRegion.getType(); + if (regionType == XMLRegionContext.XML_TAG_CLOSE || regionType == XMLRegionContext.XML_EMPTY_TAG_CLOSE || regionType == JSP_CLOSE || regionType == JSP_DIRECTIVE_CLOSE) { + isLastAttr = true; + } + } + if (isLastAttr && prevRegion != null) { + start += prevRegion.getTextEnd(); + } else { + start += nameRegion.getStart(); + } + + // impl.resetRegions(ownerElement); + impl.resetRegions(element); + } else { // append attribute + IStructuredDocumentRegion flatNode = element.getStartStructuredDocumentRegion(); + if (flatNode == null) + return; // must never happen + + attrValueClose = getAttrValueClose(element); + if (attrValueClose != null && attrValueClose.length() > 0) { + insertSpace = true; + start = flatNode.getEndOffset(); + end = start; + } else { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; // must never happen + int attrStart = 0; + for (int i = regions.size() - 1; i >= 0; i--) { + ITextRegion region = regions.get(i); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_TAG_CLOSE || regionType == XMLRegionContext.XML_EMPTY_TAG_CLOSE || regionType == JSP_CLOSE || regionType == JSP_DIRECTIVE_CLOSE) + continue; + int regionEnd = region.getEnd(); + if (regionEnd == region.getTextEnd()) + insertSpace = true; + attrStart = regionEnd; + break; + } + if (attrStart == 0) + return; // not found, must never happen + start += attrStart; + end = start; + } + } + + String source = null; + if (newAttr != null) { + int size = 2; + if (attrValueClose != null) + size += attrValueClose.length(); + String name = this.generator.generateAttrName(newAttr); + if (name != null) + size += name.length(); + String value = this.generator.generateAttrValue(newAttr); + if (value != null) + size += value.length(); + StringBuffer buffer = new StringBuffer(size); + if (attrValueClose != null) + buffer.append(attrValueClose); + if (insertSpace) + buffer.append(' '); + buffer.append(name); + if (value != null) { + buffer.append('='); + buffer.append(value); + } + source = buffer.toString(); + } + + replaceSource(source, start, end); + } + + /** + * replaceChild method + * + * @param parentNode + * org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + * @param oldChild + * org.w3c.dom.Node + */ + void replaceChild(Node parentNode, Node newChild, Node oldChild) { + if (parentNode == null) + return; + if (newChild == null && oldChild == null) + return; + if (getStructuredDocument() == null) + return; + + int start = 0; + int end = 0; + String preTag = null; + String postTag = null; + ElementImpl postElement = null; + if (oldChild != null) { + NodeImpl node = (NodeImpl) oldChild; + start = node.getStartOffset(); + end = node.getEndOffset(); + if (oldChild.getNodeType() == Node.TEXT_NODE) { + this.gapStructuredDocumentRegion = node.getStructuredDocumentRegion(); + } + node.resetStructuredDocumentRegions(); // reset values from + // IStructuredDocumentRegion + } else { + NodeImpl prev = (NodeImpl) newChild.getPreviousSibling(); + if (prev != null) { + start = prev.getEndOffset(); + end = start; + preTag = getCloseTag(prev); + } else { + // first child + NodeImpl next = (NodeImpl) newChild.getNextSibling(); + if (next != null) { + start = next.getStartOffset(); + end = start; + if (parentNode.getNodeType() == Node.ELEMENT_NODE) { + preTag = getStartCloseTag((XMLElement) parentNode); + } + } else { + // newly having a child + if (parentNode.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) parentNode; + if (element.isEmptyTag()) { // empty tag format + // need to generate the start and the end tags + end = element.getEndOffset(); + start = end - 2; // for "/>" + element.setEmptyTag(false); + preTag = this.generator.generateCloseTag(element); + postTag = this.generator.generateEndTag(element); + postElement = element; + } else if (!element.hasStartTag()) { + start = element.getStartOffset(); + end = start; + // invalid end tag or implicit tag + // need to generate the start tag + preTag = this.generator.generateStartTag(element); + if (preTag != null) { + int length = preTag.length(); + if (length > 0) { + IStructuredDocumentRegion flatNode = new StructuredDocumentRegionProxy(start, length); + element.setStartStructuredDocumentRegion(flatNode); + } + } + if (!element.hasEndTag()) { + // implicit tag + // need to generate the end tags + postTag = this.generator.generateEndTag(element); + postElement = element; + } + } else { + start = element.getStartEndOffset(); + end = start; + preTag = getStartCloseTag(element); + if (preTag != null && preTag.length() > 0) { + if (!element.hasEndTag() && (element.isJSPContainer() || element.isCDATAContainer())) { + // need to generate the end tag + postTag = this.generator.generateEndTag(element); + postElement = element; + } + } + } + } + // else might DOCUMENT_NODE, start and end are 0 + } + } + } + + String source = null; + if (newChild != null) { + StringBuffer buffer = new StringBuffer(); + int offset = start; + if (preTag != null) { + int length = preTag.length(); + if (length > 0) { + offset += length; + buffer.append(preTag); + } + } + + NodeImpl node = (NodeImpl) newChild; + while (node != null) { + if (node.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) node; + if (element.preferEmptyTag()) + element.setEmptyTag(true); + IStructuredDocumentRegion flatNode = null; + String startTag = this.generator.generateStartTag(element); + if (startTag != null) { + int length = startTag.length(); + if (length > 0) { + buffer.append(startTag); + flatNode = new StructuredDocumentRegionProxy(offset, length); + offset += length; + } + } + element.setStartStructuredDocumentRegion(flatNode); + } else { + String content = this.generator.generateSource(node); + if (content == null) + content = new String(); + int length = content.length(); + IStructuredDocumentRegion flatNode = null; + if (length > 0) { + buffer.append(content); + flatNode = new StructuredDocumentRegionProxy(offset, length); + offset += length; + } + node.setStructuredDocumentRegion(flatNode); + } + + NodeImpl child = (NodeImpl) node.getFirstChild(); + if (child != null) { + node = child; + continue; + } + + if (node.getNodeType() == Node.ELEMENT_NODE) { + ElementImpl element = (ElementImpl) node; + IStructuredDocumentRegion flatNode = null; + String endTag = this.generator.generateEndTag(element); + if (endTag != null) { + int length = endTag.length(); + if (length > 0) { + buffer.append(endTag); + flatNode = new StructuredDocumentRegionProxy(offset, length); + offset += length; + } + } + element.setEndStructuredDocumentRegion(flatNode); + } + + while (node != null) { + if (node == newChild) { + node = null; + break; + } + NodeImpl next = (NodeImpl) node.getNextSibling(); + if (next != null) { + node = next; + break; + } + + node = (NodeImpl) node.getParentNode(); + if (node.getNodeType() != Node.ELEMENT_NODE) + continue; + ElementImpl element = (ElementImpl) node; + IStructuredDocumentRegion flatNode = null; + String endTag = this.generator.generateEndTag(element); + if (endTag != null) { + int length = endTag.length(); + if (length > 0) { + buffer.append(endTag); + flatNode = new StructuredDocumentRegionProxy(offset, length); + offset += length; + } + } + element.setEndStructuredDocumentRegion(flatNode); + } + } + + if (postTag != null) { + int length = postTag.length(); + if (length > 0) { + buffer.append(postTag); + if (postElement != null) { + IStructuredDocumentRegion flatNode = new StructuredDocumentRegionProxy(offset, length); + postElement.setEndStructuredDocumentRegion(flatNode); + } + } + } + source = buffer.toString(); + } + + if (start == end && (source == null || source.length() == 0)) { + // no thing changed + return; + } + + replaceSource(source, start, end); + } + + void replaceRegions(IStructuredDocumentRegion flatNode, ITextRegionList newRegions, ITextRegionList oldRegions) { + // future_TODO: optimize + + NodeImpl root = (NodeImpl) this.model.getDocument(); + this.parentNode = root; + this.nextNode = (NodeImpl) root.getFirstChild(); + + removeGapStructuredDocumentRegion(flatNode); + insertGapStructuredDocumentRegionBefore(flatNode.getStart()); + changeStructuredDocumentRegion(flatNode); + insertGapStructuredDocumentRegionAfter(flatNode.getEnd()); + } + + /** + * Wraps IStructuredDocumentRegion.replaceText() and sets contextual + * information. + */ + private void replaceSource(String source, int start, int end) { + int inserted = 0; + if (source == null) + source = new String(); + else + inserted = source.length(); + int removed = end - start; + if (inserted == 0 && removed == 0) + return; + + this.gapOffset = start; + this.gapLength = removed; + this.diff = inserted - removed; + // Note: due to bug + // https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=3619 + // for now assume "ignore readonly" region is ok -- assume DOM itself + // checks if + // ok to insert or not. In reality, we may have to make or "contains" + // method more + // better. Or, we may have to "perculate up" the parameter for clients + // to tell us programatically + // that its ok to insert/format in a read-only region. + getStructuredDocument().replaceText(this.model, this.gapOffset, this.gapLength, source, true); + } + + void replaceStructuredDocumentRegions(IStructuredDocumentRegionList newStructuredDocumentRegions, IStructuredDocumentRegionList oldStructuredDocumentRegions) { + NodeImpl root = (NodeImpl) this.model.getDocument(); + + if (oldStructuredDocumentRegions != null) { + this.parentNode = root; + this.nextNode = (NodeImpl) root.getFirstChild(); + + Enumeration e = oldStructuredDocumentRegions.elements(); + while (e.hasMoreElements()) { + IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) e.nextElement(); + if (flatNode == null) + continue; + removeStructuredDocumentRegion(flatNode); + removeGapStructuredDocumentRegion(flatNode); + } + } + + if (newStructuredDocumentRegions != null) { + this.parentNode = root; + this.nextNode = (NodeImpl) root.getFirstChild(); + + IStructuredDocumentRegion lastStructuredDocumentRegion = null; + Enumeration e = newStructuredDocumentRegions.elements(); + while (e.hasMoreElements()) { + IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) e.nextElement(); + if (flatNode == null) + continue; + if (lastStructuredDocumentRegion == null) + insertGapStructuredDocumentRegionBefore(flatNode.getStart()); + insertStructuredDocumentRegion(flatNode); + lastStructuredDocumentRegion = flatNode; + } + if (lastStructuredDocumentRegion != null) { + insertGapStructuredDocumentRegionAfter(lastStructuredDocumentRegion.getEnd()); + } else { + insertGapStructuredDocumentRegionBefore(this.gapOffset); + // make sure to restore all backuped StructuredDocumentRegions + insertGapStructuredDocumentRegionAfter(this.gapOffset); + } + } else { + this.parentNode = root; + this.nextNode = (NodeImpl) root.getFirstChild(); + + insertGapStructuredDocumentRegionBefore(this.gapOffset); + // make sure to restore all backuped StructuredDocumentRegions + insertGapStructuredDocumentRegionAfter(this.gapOffset); + } + } + + /** + */ + private void updateAttrRegions(Element element, IStructuredDocumentRegion flatNode) { + + // update attributes + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + NamedNodeMap attributes = element.getAttributes(); + if (attributes == null) + return; + int index = -1; + AttrImpl attr = null; + Iterator e = regions.iterator(); + while (e.hasNext()) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME) { + attr = (AttrImpl) attributes.item(++index); + if (attr != null) { + attr.setNameRegion(region); + // reset other regions + attr.setEqualRegion(null); + attr.setValueRegion(null); + } + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { + if (attr != null) + attr.setEqualRegion(region); + } else if (regionType == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) { + if (attr != null) { + attr.setValueRegion(region); + attr = null; + } + } + } + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/filebuffers/DocumentFactoryForXML.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/filebuffers/DocumentFactoryForXML.java new file mode 100644 index 0000000000..549752a77f --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/filebuffers/DocumentFactoryForXML.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.filebuffers; + +import org.eclipse.core.filebuffers.IDocumentFactory; +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.sse.core.document.StructuredDocumentFactory; +import org.eclipse.wst.sse.core.text.IStructuredDocument; +import org.eclipse.wst.xml.core.internal.parser.XMLSourceParser; + + + +public class DocumentFactoryForXML implements IDocumentFactory { + + public DocumentFactoryForXML() { + super(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.filebuffers.IDocumentFactory#createDocument() + */ + public IDocument createDocument() { + IStructuredDocument structuredDocument = StructuredDocumentFactory.getNewStructuredDocumentInstance(new XMLSourceParser()); + return structuredDocument; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/filebuffers/SetupParticipantForXML.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/filebuffers/SetupParticipantForXML.java new file mode 100644 index 0000000000..1390a1f0fb --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/filebuffers/SetupParticipantForXML.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.filebuffers; + +import org.eclipse.core.filebuffers.IDocumentSetupParticipant; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentPartitioner; +import org.eclipse.wst.xml.core.text.rules.StructuredTextPartitionerForXML; + + + +public class SetupParticipantForXML implements IDocumentSetupParticipant { + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.filebuffers.IDocumentSetupParticipant#setup(org.eclipse.jface.text.IDocument) + */ + public void setup(IDocument document) { + if (document != null) { + IDocumentPartitioner partitioner = new StructuredTextPartitionerForXML(); + document.setDocumentPartitioner(partitioner); + partitioner.connect(document); + + // setup empty model here? coordinated via model manager? + + } + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/BlockStructuredDocumentRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/BlockStructuredDocumentRegion.java new file mode 100644 index 0000000000..5442a10f0e --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/BlockStructuredDocumentRegion.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser; + + + +import org.eclipse.wst.sse.core.internal.text.BasicStructuredDocumentRegion; +import org.eclipse.wst.sse.core.parser.IBlockedStructuredDocumentRegion; + + +public class BlockStructuredDocumentRegion extends BasicStructuredDocumentRegion implements IBlockedStructuredDocumentRegion { + + private String partitionType; + + /** + * A BlockStructuredDocumentRegion is like a IStructuredDocumentRegion, + * but is the result of a "block scan". + */ + public BlockStructuredDocumentRegion() { + super(); + } + + public String getPartitionType() { + if (partitionType == null) { + // eventually can look up surroundingTag name + // but this field is primarily entended for future + // extensibility. This may change. + //partitionType = "org.eclipse.wst.sse.core." + tagname; + } + return partitionType; + } + + public void setPartitionType(String partitionType) { + this.partitionType = partitionType; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/ContextRegionContainer.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/ContextRegionContainer.java new file mode 100644 index 0000000000..e238281ff4 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/ContextRegionContainer.java @@ -0,0 +1,386 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser; + + + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.text.TextRegionListImpl; +import org.eclipse.wst.sse.core.text.IStructuredDocument; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionCollection; +import org.eclipse.wst.sse.core.text.ITextRegionContainer; +import org.eclipse.wst.sse.core.text.ITextRegionList; + + +public class ContextRegionContainer implements ITextRegionContainer { + protected int length; + protected ITextRegionCollection parent; + protected ITextRegionList regions; + protected int start; + protected int textLength; + protected String type; + + public ContextRegionContainer() { + super(); + regions = new TextRegionListImpl(); + + } + + /** + * these "deep" parenting is not normal, but just in case. + */ + private IStructuredDocument _getParentDocument() { + // go up enough parents to get to document + ITextRegionCollection parent = getParent(); + while (!(parent instanceof IStructuredDocumentRegion)) { + // would be an error not to be container, but + // won't check for it now + parent = ((ITextRegionContainer) parent).getParent(); + } + return ((IStructuredDocumentRegion) parent).getParentDocument(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjust(int) + */ + public void adjust(int i) { + + start += i; + // I erroneously added length and textLength + // may want to rename this method to adjustStart + //length += i; + //textLength += i; + + } + + public void adjustLengthWith(int i) { + length += i; + } + + public void adjustStart(int i) { + start += i; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + textLength += i; + + } + + public boolean containsOffset(int i) { + + return getStartOffset() <= i && i < getEndOffset(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#containsOffset(com.ibm.sed.structured.text.ITextRegion, + * int) + */ + public boolean containsOffset(ITextRegion containedRegion, int offset) { + return getStartOffset(containedRegion) <= offset && offset < getEndOffset(containedRegion); + } + + /** + * This method is just to equate positions. clients may (will probably) + * still need to make calls to equate regions, parent, etc. + */ + public void equatePositions(ITextRegion region) { + start = region.getStart(); + length = region.getLength(); + textLength = region.getTextLength(); + } + + public int getEnd() { + return start + length; + } + + public int getEndOffset() { + // our startOffset take into account our parent, and our start + return getStartOffset() + getLength(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getEndOffset(com.ibm.sed.structured.text.ITextRegion) + */ + public int getEndOffset(ITextRegion containedRegion) { + return getStartOffset(containedRegion) + containedRegion.getLength(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getFirstRegion() + */ + public ITextRegion getFirstRegion() { + return getRegions().get(0); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getFullText() + */ + public String getFullText() { + // CMVC > 252430, 245586 + // unit test > com.ibm.sed.tests.other.UnitTests.testDeepEmbeddedJSP3 + // this code modified on 6/25/03 (pa) + + // String result = null; + // try { + // result = _getParentDocument().get(start, length); + // } catch (BadLocationException e) { + // Logger.logException("program error: unreachable exception", e); + // //$NON-NLS-1$ + // } + // return result; + return getParent().getFullText(this); + } + + public String getFullText(org.eclipse.wst.sse.core.text.ITextRegion aRegion) { + // Must be proxied here since aRegion should always be a child of + // *this* container and indexed from + // this container's offset + // try { + return parent.getFullText().substring(start + aRegion.getStart(), start + aRegion.getEnd()); + //} catch (Exception e) { + //if (com.ibm.sed.util.Debug.debugStructuredDocument) + //e.printStackTrace(); + //} + //return "";//$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getLastRegion() + */ + public ITextRegion getLastRegion() { + return getRegions().get(getRegions().size() - 1); + } + + public int getLength() { + return length; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getNumberOfRegions() + */ + public int getNumberOfRegions() { + return getRegions().size(); + } + + public ITextRegionCollection getParent() { + return parent; + } + + /** + * The parameter offset refers to the overall offset in the document. + */ + public ITextRegion getRegionAtCharacterOffset(int offset) { + ITextRegion result = null; + if (regions != null) { + // transform the requested offset to the "scale" that + // regions are stored in, which are all relative to the + // start point. + //int transformedOffset = offset - getStartOffset(); + // + int length = getRegions().size(); + for (int i = 0; i < length; i++) { + ITextRegion region = getRegions().get(i); + if (org.eclipse.wst.sse.core.util.Debug.debugStructuredDocument) { + System.out.println("region(s) in IStructuredDocumentRegion::getRegionAtCharacterOffset: " + region); //$NON-NLS-1$ + System.out.println(" requested offset: " + offset); //$NON-NLS-1$ + //System.out.println(" transformedOffset: " + + // transformedOffset); //$NON-NLS-1$ + System.out.println(" region start: " + region.getStart()); //$NON-NLS-1$ + System.out.println(" region end: " + region.getEnd()); //$NON-NLS-1$ + System.out.println(" region type: " + region.getType()); //$NON-NLS-1$ + System.out.println(" region class: " + region.getClass()); //$NON-NLS-1$ + + } + if ((getStartOffset(region) <= offset) && (offset < getEndOffset(region))) { + result = region; + break; + } + } + } + return result; + } + + public ITextRegionList getRegions() { + return regions; + } + + public int getStart() { + return start; + } + + public int getStartOffset() { + return getParent().getStartOffset() + getStart(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getStartOffset(com.ibm.sed.structured.text.ITextRegion) + */ + public int getStartOffset(ITextRegion containedRegion) { + return getStartOffset() + containedRegion.getStart(); + } + + /** + * same as getFullText for this region type ... do we need to take white + * space off? + */ + + public String getText() { + String result = null; + try { + IStructuredDocument parentDocument = _getParentDocument(); + result = parentDocument.get(start, length); + } catch (BadLocationException e) { + Logger.logException("program error: unreachable exception", e); //$NON-NLS-1$ + } + return result; + } + + public String getText(org.eclipse.wst.sse.core.text.ITextRegion aRegion) { + // Must be proxied here since aRegion should always be a child of + // *this* container and indexed from + // this container's offset + // com.ibm.sed.util.Assert.isTrue(regions.contains(aRegion)); + //try { + return parent.getText().substring(start + aRegion.getStart(), start + aRegion.getTextEnd()); + //} catch (Exception e) { + //if (com.ibm.sed.util.Debug.debugStructuredDocument) + //com.ibm.sed.util.Logger.log(e); + //} + // return ""; //$NON-NLS-1$ + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#getTextEnd() + */ + public int getTextEnd() { + // int result = 0; + // ITextRegion lastRegion = (ITextRegion) regions.get(regions.size() - + // 1); + // result = getStartOffset(lastRegion) + lastRegion.getTextLength(); + // return result; + return start + textLength; + } + + public int getTextEndOffset() { + ITextRegion region = regions.get(regions.size() - 1); + // our startOffset take into account our parent, and our start + // (pa) 10/4 changed to be based on text end + // it used to return incorrect value for embedded region containers + // + + // TODO CRITICAL -- need to re-work this work around, so doesn't + // depend on XMLRegionContext + // // this is a workaround for 226823/////////// + // for (int i = regions.size() - 1; i >= 0 && region.getType() == + // XMLRegionContext.WHITE_SPACE; i--) + // region = (ITextRegion) regions.get(i); + // ///////////////////////////////////////////// + + return getStartOffset() + region.getTextEnd(); + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegionCollection#getTextEndOffset(com.ibm.sed.structured.text.ITextRegion) + */ + public int getTextEndOffset(ITextRegion containedRegion) { + int result = 0; + if (regions != null) { + int length = getRegions().size(); + for (int i = 0; i < length; i++) { + ITextRegion region = getRegions().get(i); + if (region == containedRegion) { + result = getStartOffset(region) + region.getTextEnd(); + break; + } + } + } + return result; + } + + public int getTextLength() { + return textLength; + } + + public String getType() { + return type; + } + + public void setLength(int i) { + length = i; + } + + public void setParent(ITextRegionCollection parentRegion) { + parent = parentRegion; + } + + public void setRegions(ITextRegionList containedRegions) { + regions = containedRegions; + } + + public void setStart(int i) { + start = i; + } + + public void setTextLength(int i) { + textLength = i; + } + + public void setType(String string) { + type = string; + } + + public String toString() { + String className = getClass().getName(); + String shortClassName = className.substring(className.lastIndexOf(".") + 1); //$NON-NLS-1$ + String result = "Container!!! " + shortClassName + "--> " + getType() + ": " + getStart() + "-" + getTextEnd() + (getTextEnd() != getEnd() ? ("/" + getEnd()) : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + return result; + } + + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + org.eclipse.wst.sse.core.events.RegionChangedEvent result = null; + // FUTURE_TO_DO: need to implement region level parsing in + // ITextRegionContainer::updateModel + // never being called? + return result; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/IntStack.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/IntStack.java new file mode 100644 index 0000000000..b871545643 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/IntStack.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser; + + + +/* + * + * A non-resizable class implementing the behavior of java.util.Stack, but + * directly for the <code> integer </code> primitive. + */ +import java.util.EmptyStackException; + +public class IntStack { + private int[] list = null; + + private int size = 0; + + public IntStack() { + this(100); + } + + public IntStack(int maxdepth) { + super(); + list = new int[maxdepth]; + initialize(); + } + + public boolean empty() { + return size == 0; + } + + public int get(int slot) { + return list[slot]; + } + + void initialize() { + for (int i = 0; i < list.length; i++) + list[i] = -1; + } + + /** + * Returns the int at the top of the stack without removing it + * + * @return int at the top of this stack. + * @exception EmptyStackException + * when empty. + */ + public int peek() { + if (size == 0) + throw new EmptyStackException(); + return list[size - 1]; + } + + /** + * Removes and returns the int at the top of the stack + * + * @return int at the top of this stack. + * @exception EmptyStackException + * when empty. + */ + public int pop() { + int value = peek(); + list[size - 1] = -1; + size--; + return value; + } + + /** + * Pushes an item onto the top of this stack. + * + * @param newValue - + * the int to be pushed onto this stack. + * @return the <code>newValue</code> argument. + */ + public int push(int newValue) { + if (size == list.length) { + throw new StackOverflowError(); + } + list[size++] = newValue; + return newValue; + } + + public int size() { + return list.length; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/RegionFactory.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/RegionFactory.java new file mode 100644 index 0000000000..f9c4ab9e08 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/RegionFactory.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser; + + + +import org.eclipse.wst.sse.core.internal.parser.ContextRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionContainer; + + +public class RegionFactory { + + public RegionFactory() { + super(); + } + + public ITextRegion createToken(ITextRegionContainer parent, String context, int start, int textLength, int length) { + return this.createToken(parent, context, start, textLength, length, null, null); + } + + public ITextRegion createToken(ITextRegionContainer parent, String context, int start, int textLength, int length, String lang, String surroundingTag) { + ITextRegion newRegion = createToken(context, start, textLength, length); + // DW, 4/16/2003 token regions no longer have parents + //newRegion.setParent(parent); + return newRegion; + } + + public ITextRegion createToken(String context, int start, int textLength, int length) { + return this.createToken(context, start, textLength, length, null, null); + } + + public ITextRegion createToken(String context, int start, int textLength, int length, String lang, String surroundingTag) { + ITextRegion newRegion = new ContextRegion(context, start, textLength, length); + return newRegion; + + + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XML10Names.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XML10Names.java new file mode 100644 index 0000000000..1ff05a0a8b --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XML10Names.java @@ -0,0 +1,541 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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 + * + *******************************************************************************/ +/* The following code was generated by JFlex 1.4 on 7/17/04 3:43 AM */ + +/*nlsXXX*/ +package org.eclipse.wst.xml.core.internal.parser; + + + +/** + * This class is a scanner generated by <a href="http://www.jflex.de/">JFlex + * </a> 1.4 on 7/17/04 3:43 AM from the specification file + * <tt>XML10Names.jflex</tt> + */ +public final class XML10Names { + + /** This character denotes the end of file */ + private static final int YYEOF = -1; + + /** initial size of the lookahead buffer */ + private static final int ZZ_BUFFERSIZE = 2048; + + /** lexical states */ + private static final int YYINITIAL = 0; + + /** + * Translates characters to character classes + */ + private static final String ZZ_CMAP_PACKED = "\11\0\1\1\1\2\2\0\1\1\22\0\1\1\14\0\1\0\2\0" + "\12\0\1\3\6\0\32\3\4\0\1\3\1\0\32\3\74\0\1\0" + "\10\0\27\3\1\0\37\3\1\0\72\3\2\0\13\3\2\0\10\3" + "\1\0\65\3\1\0\104\3\11\0\44\3\3\0\2\3\4\0\36\3" + "\70\0\131\3\22\0\7\3\16\0\2\0\56\0\106\0\32\0\2\0" + "\44\0\1\3\1\0\3\3\1\0\1\3\1\0\24\3\1\0\54\3" + "\1\0\7\3\3\0\1\3\1\0\1\3\1\0\1\3\1\0\1\3" + "\1\0\22\3\15\0\14\3\1\0\102\3\1\0\14\3\1\0\44\3" + "\1\0\4\0\11\0\65\3\2\0\2\3\2\0\2\3\3\0\34\3" + "\2\0\10\3\2\0\2\3\67\0\46\3\2\0\1\3\7\0\46\3" + "\12\0\21\0\1\0\27\0\1\0\3\0\1\0\1\0\1\0\2\0" + "\1\0\1\0\13\0\33\3\5\0\3\3\56\0\32\3\5\0\1\0" + "\12\3\10\0\15\0\12\0\6\0\1\0\107\3\2\0\5\3\1\0" + "\17\3\1\0\4\3\1\0\1\3\17\0\2\3\2\0\1\0\4\0" + "\2\0\12\0\u0207\0\3\0\1\0\65\3\2\0\1\0\1\3\20\0" + "\3\0\4\0\3\0\12\3\2\0\2\0\12\0\21\0\3\0\1\0" + "\10\3\2\0\2\3\2\0\26\3\1\0\7\3\1\0\1\3\3\0" + "\4\3\2\0\1\0\1\0\7\0\2\0\2\0\2\0\3\0\11\0" + "\1\0\4\0\2\3\1\0\3\3\2\0\2\0\12\0\2\3\20\0" + + "\1\0\2\0\6\3\4\0\2\3\2\0\26\3\1\0\7\3\1\0" + "\2\3\1\0\2\3\1\0\2\3\2\0\1\0\1\0\5\0\4\0" + "\2\0\2\0\3\0\13\0\4\3\1\0\1\3\7\0\12\0\2\0" + "\3\3\14\0\3\0\1\0\7\3\1\0\1\3\1\0\3\3\1\0" + "\26\3\1\0\7\3\1\0\2\3\1\0\5\3\2\0\1\0\1\3" + "\10\0\1\0\3\0\1\0\3\0\22\0\1\3\5\0\12\0\21\0" + "\3\0\1\0\10\3\2\0\2\3\2\0\26\3\1\0\7\3\1\0" + "\2\3\2\0\4\3\2\0\1\0\1\3\6\0\3\0\2\0\2\0" + "\3\0\10\0\2\0\4\0\2\3\1\0\3\3\4\0\12\0\22\0" + "\2\0\1\0\6\3\3\0\3\3\1\0\4\3\3\0\2\3\1\0" + "\1\3\1\0\2\3\3\0\2\3\3\0\3\3\3\0\10\3\1\0" + "\3\3\4\0\5\0\3\0\3\0\1\0\4\0\11\0\1\0\17\0" + "\11\0\21\0\3\0\1\0\10\3\1\0\3\3\1\0\27\3\1\0" + "\12\3\1\0\5\3\4\0\7\0\1\0\3\0\1\0\4\0\7\0" + "\2\0\11\0\2\3\4\0\12\0\22\0\2\0\1\0\10\3\1\0" + "\3\3\1\0\27\3\1\0\12\3\1\0\5\3\4\0\7\0\1\0" + "\3\0\1\0\4\0\7\0\2\0\7\0\1\3\1\0\2\3\4\0" + "\12\0\22\0\2\0\1\0\10\3\1\0\3\3\1\0\27\3\1\0" + "\20\3\4\0\6\0\2\0\3\0\1\0\4\0\11\0\1\0\10\0" + "\2\3\4\0\12\0\221\0\56\3\1\0\1\3\1\0\2\3\7\0" + + "\5\0\6\3\1\0\10\0\1\0\12\0\47\0\2\3\1\0\1\3" + "\2\0\2\3\1\0\1\3\2\0\1\3\6\0\4\3\1\0\7\3" + "\1\0\3\3\1\0\1\3\1\0\1\3\2\0\2\3\1\0\2\3" + "\1\0\1\3\1\0\2\3\6\0\1\0\2\0\1\3\2\0\5\3" + "\1\0\1\0\1\0\6\0\2\0\12\0\76\0\2\0\6\0\12\0" + "\13\0\1\0\1\0\1\0\1\0\1\0\4\0\2\0\10\3\1\0" + "\41\3\7\0\24\0\1\0\6\0\4\0\6\0\1\0\1\0\1\0" + "\25\0\3\0\7\0\1\0\1\0\346\0\46\3\12\0\47\3\11\0" + "\1\3\1\0\2\3\1\0\3\3\1\0\1\3\1\0\2\3\1\0" + "\5\3\51\0\1\3\1\0\1\3\1\0\1\3\13\0\1\3\1\0" + "\1\3\1\0\1\3\3\0\2\3\3\0\1\3\5\0\3\3\1\0" + "\1\3\1\0\1\3\1\0\1\3\1\0\1\3\3\0\2\3\3\0" + "\2\3\1\0\1\3\50\0\1\3\11\0\1\3\2\0\1\3\2\0" + "\2\3\7\0\2\3\1\0\1\3\1\0\7\3\50\0\1\3\4\0" + "\1\3\10\0\1\3\u0c06\0\234\3\4\0\132\3\6\0\26\3\2\0" + "\6\3\2\0\46\3\2\0\6\3\2\0\10\3\1\0\1\3\1\0" + "\1\3\1\0\1\3\1\0\37\3\2\0\65\3\1\0\7\3\1\0" + "\1\3\3\0\3\3\1\0\7\3\3\0\4\3\2\0\6\3\4\0" + "\15\3\5\0\3\3\1\0\7\3\323\0\15\0\4\0\1\0\104\0" + "\1\3\3\0\2\3\2\0\1\3\121\0\3\3\u0e82\0\1\0\1\0" + + "\1\3\31\0\11\3\6\0\1\0\5\0\13\0\124\3\4\0\2\0" + "\2\0\2\0\2\0\132\3\1\0\3\0\6\0\50\3\u1cd3\0\u51a6\3" + "\u0c5a\0\u2ba4\3\u285c\0"; + + /** + * Translates characters to character classes + */ + private static final char[] ZZ_CMAP = zzUnpackCMap(ZZ_CMAP_PACKED); + + /** + * Translates DFA states to action switch labels. + */ + private static final int[] ZZ_ACTION = zzUnpackAction(); + + private static final String ZZ_ACTION_PACKED_0 = "\1\0\1\1\1\2\4\1"; + + private static int[] zzUnpackAction() { + int[] result = new int[7]; + int offset = 0; + offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAction(String packed, int offset, int[] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do + result[j++] = value; + while (--count > 0); + } + return j; + } + + + /** + * Translates a state to a row index in the transition table + */ + private static final int[] ZZ_ROWMAP = zzUnpackRowMap(); + + private static final String ZZ_ROWMAP_PACKED_0 = "\0\0\0\4\0\10\0\14\0\20\0\24\0\30"; + + private static int[] zzUnpackRowMap() { + int[] result = new int[7]; + int offset = 0; + offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackRowMap(String packed, int offset, int[] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int high = packed.charAt(i++) << 16; + result[j++] = high | packed.charAt(i++); + } + return j; + } + + /** + * The transition table of the DFA + */ + private static final int ZZ_TRANS[] = {1, 1, -1, 2, -1, -1, -1, -1, 2, 3, 4, 2, 2, 3, 4, 5, -1, 4, 4, 6, 5, 5, 4, 5, 6, 6, -1, 6,}; + + /* error codes */ + private static final int ZZ_UNKNOWN_ERROR = 0; + private static final int ZZ_NO_MATCH = 1; + private static final int ZZ_PUSHBACK_2BIG = 2; + + /* error messages for the codes above */ + private static final String ZZ_ERROR_MSG[] = {"Unkown internal scanner error", "Error: could not match input", "Error: pushback value was too large"}; + + /** + * ZZ_ATTRIBUTE[aState] contains the attributes of state + * <code>aState</code> + */ + private static final int[] ZZ_ATTRIBUTE = zzUnpackAttribute(); + + private static final String ZZ_ATTRIBUTE_PACKED_0 = "\1\0\1\11\5\1"; + + private static int[] zzUnpackAttribute() { + int[] result = new int[7]; + int offset = 0; + offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); + return result; + } + + private static int zzUnpackAttribute(String packed, int offset, int[] result) { + int i = 0; /* index in packed string */ + int j = offset; /* index in unpacked array */ + int l = packed.length(); + while (i < l) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + do + result[j++] = value; + while (--count > 0); + } + return j; + } + + /** the input device */ + private java.io.Reader zzReader; + + /** the current state of the DFA */ + private int zzState; + + /** the current lexical state */ + private int zzLexicalState = YYINITIAL; + + /** + * this buffer contains the current text to be matched and is the source + * of the yytext() string + */ + private char zzBuffer[] = new char[ZZ_BUFFERSIZE]; + + /** the textposition at the last accepting state */ + private int zzMarkedPos; + + /** the textposition at the last state to be included in yytext */ + private int zzPushbackPos; + + /** the current text position in the buffer */ + private int zzCurrentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int zzStartRead; + + /** + * endRead marks the last character in the buffer, that has been read from + * input + */ + private int zzEndRead; + + /** number of newlines encountered up to the start of the matched text */ + int yyline; + + /** the number of characters up to the start of the matched text */ + int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + int yycolumn; + + /** + * zzAtBOL == true <=>the scanner is currently at the beginning of a line + */ + boolean zzAtBOL = true; + + /** zzAtEOF == true <=>the scanner is at the EOF */ + private boolean zzAtEOF; + + /* user code: */ + + /** + * Creates a new scanner + */ + public XML10Names() { + this.zzReader = null; + } + + public boolean isValidXML10Name(String stringToCheck) { + boolean result = false; + yyreset(new java.io.StringReader(stringToCheck)); + try { + result = isValidXML10Name(); + } catch (java.io.IOException e) { + // should be impossible with strings, but if occurs, just means + // "not" + result = false; + } + return result; + } + + + + /** + * Creates a new scanner There is also a java.io.InputStream version of + * this constructor. + * + * @param in + * the java.io.Reader to read input from. + */ + public XML10Names(java.io.Reader in) { + this.zzReader = in; + } + + /** + * Creates a new scanner. There is also java.io.Reader version of this + * constructor. + * + * @param in + * the java.io.Inputstream to read input from. + */ + public XML10Names(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed + * the packed character translation table + * @return the unpacked character translation table + */ + private static char[] zzUnpackCMap(String packed) { + char[] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 1226) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do + map[j++] = value; + while (--count > 0); + } + return map; + } + + + /** + * Refills the input buffer. + * + * @return <code>false</code>, iff there was new input. + * + * @exception java.io.IOException + * if any I/O-Error occurs + */ + private boolean zzRefill() throws java.io.IOException { + + /* first: make room (if you can) */ + if (zzStartRead > 0) { + System.arraycopy(zzBuffer, zzStartRead, zzBuffer, 0, zzEndRead - zzStartRead); + + /* translate stored positions */ + zzEndRead -= zzStartRead; + zzCurrentPos -= zzStartRead; + zzMarkedPos -= zzStartRead; + zzPushbackPos -= zzStartRead; + zzStartRead = 0; + } + + /* is the buffer big enough? */ + if (zzCurrentPos >= zzBuffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[zzCurrentPos * 2]; + System.arraycopy(zzBuffer, 0, newBuffer, 0, zzBuffer.length); + zzBuffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = zzReader.read(zzBuffer, zzEndRead, zzBuffer.length - zzEndRead); + + if (numRead < 0) { + return true; + } else { + zzEndRead += numRead; + return false; + } + } + + + /** + * Closes the input stream. + */ + final void yyclose() throws java.io.IOException { + zzAtEOF = true; /* indicate end of file */ + zzEndRead = zzStartRead; /* invalidate buffer */ + + if (zzReader != null) + zzReader.close(); + } + + + /** + * Resets the scanner to read from a new input stream. Does not close the + * old reader. + * + * All internal variables are reset, the old input stream <b>cannot </b> + * be reused (internal buffer is discarded and lost). Lexical state is set + * to <tt>ZZ_INITIAL</tt>. + * + * @param reader + * the new input stream + */ + private final void yyreset(java.io.Reader reader) { + zzReader = reader; + //zzAtBOL = true; + zzAtEOF = false; + zzEndRead = zzStartRead = 0; + zzCurrentPos = zzMarkedPos = zzPushbackPos = 0; + yyline = yychar = yycolumn = 0; + zzLexicalState = YYINITIAL; + } + + + /** + * Returns the current lexical state. + */ + final int yystate() { + return zzLexicalState; + } + + + /** + * Enters a new lexical state + * + * @param newState + * the new lexical state + */ + final void yybegin(int newState) { + zzLexicalState = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + final String yytext() { + return new String(zzBuffer, zzStartRead, zzMarkedPos - zzStartRead); + } + + + /** + * Returns the character at position <tt>pos</tt> from the matched text. + * + * It is equivalent to yytext().charAt(pos), but faster + * + * @param pos + * the position of the character to fetch. A value from 0 to + * yylength()-1. + * + * @return the character at position pos + */ + final char yycharat(int pos) { + return zzBuffer[zzStartRead + pos]; + } + + + /** + * Returns the length of the matched text region. + */ + private final int yylength() { + return zzMarkedPos - zzStartRead; + } + + + /** + * Reports an error that occured while scanning. + * + * In a wellformed scanner (no or only correct usage of yypushback(int) + * and a match-all fallback rule) this method will only be called with + * things that "Can't Possibly Happen". If this method is called, + * something is seriously wrong (e.g. a JFlex bug producing a faulty + * scanner etc.). + * + * Usual syntax/scanner level error handling should be done in error + * fallback rules. + * + * @param errorCode + * the code of the errormessage to display + */ + private void zzScanError(int errorCode) { + String message; + try { + message = ZZ_ERROR_MSG[errorCode]; + } catch (ArrayIndexOutOfBoundsException e) { + message = ZZ_ERROR_MSG[ZZ_UNKNOWN_ERROR]; + } + + throw new Error(message); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number + * the number of characters to be read again. This number must + * not be greater than yylength()! + */ + void yypushback(int number) { + if (number > yylength()) + zzScanError(ZZ_PUSHBACK_2BIG); + + zzMarkedPos -= number; + } + + + /** + * Resumes scanning until the next regular expression is matched, the end + * of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception java.io.IOException + * if any I/O-Error occurs + */ + private boolean isValidXML10Name() throws java.io.IOException { + int zzInput; + int zzAction; + + // cached fields: + int zzCurrentPosL; + int zzMarkedPosL; + int zzEndReadL = zzEndRead; + char[] zzBufferL = zzBuffer; + char[] zzCMapL = ZZ_CMAP; + + int[] zzTransL = ZZ_TRANS; + int[] zzRowMapL = ZZ_ROWMAP; + int[] zzAttrL = ZZ_ATTRIBUTE; + + while (true) { + zzMarkedPosL = zzMarkedPos; + + zzAction = -1; + + zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL; + + zzState = zzLexicalState; + + + zzForAction : { + while (true) { + + if (zzCurrentPosL < zzEndReadL) + zzInput = zzBufferL[zzCurrentPosL++]; + else if (zzAtEOF) { + zzInput = YYEOF; + break zzForAction; + } else { + // store back cached positions + zzCurrentPos = zzCurrentPosL; + zzMarkedPos = zzMarkedPosL; + boolean eof = zzRefill(); + // get translated positions and possibly new buffer + zzCurrentPosL = zzCurrentPos; + zzMarkedPosL = zzMarkedPos; + zzBufferL = zzBuffer; + zzEndReadL = zzEndRead; + if (eof) { + zzInput = YYEOF; + break zzForAction; + } else { + zzInput = zzBufferL[zzCurrentPosL++]; + } + } + int zzNext = zzTransL[zzRowMapL[zzState] + zzCMapL[zzInput]]; + if (zzNext == -1) + break zzForAction; + zzState = zzNext; + + int zzAttributes = zzAttrL[zzState]; + if ((zzAttributes & 1) == 1) { + zzAction = zzState; + zzMarkedPosL = zzCurrentPosL; + if ((zzAttributes & 8) == 8) + break zzForAction; + } + + } + } + + // store back cached position + zzMarkedPos = zzMarkedPosL; + + switch (zzAction < 0 ? zzAction : ZZ_ACTION[zzAction]) { + case 1 : { + return false; + } + case 3 : + break; + case 2 : { + return true; + } + case 4 : + break; + default : + if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { + zzAtEOF = true; + { + { + return false; + } + } + } else { + zzScanError(ZZ_NO_MATCH); + } + } + } + } + + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLRegionContexts.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLRegionContexts.java new file mode 100644 index 0000000000..5c622ab3c0 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLRegionContexts.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser; + +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + +/** + * @deprecated - use org.eclipse.wst.xml.core.parser.XMLRegionContext + */ +public interface XMLRegionContexts extends XMLRegionContext { +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLSourceParser.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLSourceParser.java new file mode 100644 index 0000000000..63711818d9 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLSourceParser.java @@ -0,0 +1,595 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser; + + + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.sse.core.document.DocumentReader; +import org.eclipse.wst.sse.core.internal.text.CharSequenceReader; +import org.eclipse.wst.sse.core.internal.text.IRegionComparible; +import org.eclipse.wst.sse.core.parser.BlockMarker; +import org.eclipse.wst.sse.core.parser.BlockTagParser; +import org.eclipse.wst.sse.core.parser.BlockTokenizer; +import org.eclipse.wst.sse.core.parser.RegionParser; +import org.eclipse.wst.sse.core.parser.StructuredDocumentRegionHandler; +import org.eclipse.wst.sse.core.parser.StructuredDocumentRegionParser; +import org.eclipse.wst.sse.core.parser.StructuredDocumentRegionParserExtension; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionContainer; +import org.eclipse.wst.sse.core.text.ITextRegionList; +import org.eclipse.wst.sse.core.util.Debug; +import org.eclipse.wst.xml.core.Logger; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +/** + * Takes input from the HTMLTokenizer and creates a tag list + */ + +public class XMLSourceParser implements RegionParser, BlockTagParser, StructuredDocumentRegionParser, IRegionComparible, StructuredDocumentRegionParserExtension { + // made public to aid access from inner classes in hierarchy. + // TODO: in future, figure out how to solve without exposing data. + public CharSequence fCharSequenceSource = null; + private IDocument fDocumentInput; + protected int fOffset = 0; + // DMW: 2/12/03. Removed some state data, since not really needed, + // and since it added a lot to overhead (since so many regions are + // created. + // protected IStructuredDocumentRegion fCurrentNode = null; + // protected IStructuredDocumentRegion fNodes = null; + // protected List fRegions = null; + // protected Object fInput = null; + protected String fStringInput = null; + protected List fStructuredDocumentRegionHandlers; + + protected BlockTokenizer fTokenizer = null; + protected long startTime; + protected long stopTime; + + /** + * HTMLSourceParser constructor comment. + */ + public XMLSourceParser() { + super(); + fStructuredDocumentRegionHandlers = new ArrayList(); + } + + /** + * This is a simple utility to count nodes. Used only for debug + * statements. + */ + protected int _countNodes(IStructuredDocumentRegion nodes) { + int result = 0; + IStructuredDocumentRegion countNode = nodes; + while (countNode != null) { + result++; + countNode = countNode.getNext(); + } + return result; + } + + public void addBlockMarker(BlockMarker marker) { + getTokenizer().addBlockMarker(marker); + } + + public void addStructuredDocumentRegionHandler(StructuredDocumentRegionHandler handler) { + if (fStructuredDocumentRegionHandlers == null) + fStructuredDocumentRegionHandlers = new ArrayList(); + fStructuredDocumentRegionHandlers.add(handler); + } + + public void beginBlockScan(String newTagName) { + getTokenizer().beginBlockTagScan(newTagName); + } + + /** + * @return IStructuredDocumentRegion + */ + protected IStructuredDocumentRegion createStructuredDocumentRegion(String type) { + IStructuredDocumentRegion newNode = null; + if (type == XMLRegionContext.BLOCK_TEXT) + newNode = XMLStructuredRegionFactory.createRegion(XMLStructuredRegionFactory.XML_BLOCK); + else + newNode = XMLStructuredRegionFactory.createRegion(XMLStructuredRegionFactory.XML); + return newNode; + } + + protected void fireNodeParsed(IStructuredDocumentRegion fCurrentNode) { + if (fCurrentNode != null && fStructuredDocumentRegionHandlers != null) { + for (int i = 0; i < fStructuredDocumentRegionHandlers.size(); i++) + ((StructuredDocumentRegionHandler) fStructuredDocumentRegionHandlers.get(i)).nodeParsed(fCurrentNode); + } + } + + public BlockMarker getBlockMarker(String tagName) { + List markers = getTokenizer().getBlockMarkers(); + for (int i = 0; i < markers.size(); i++) { + BlockMarker marker = (BlockMarker) markers.get(i); + if (marker.isCaseSensitive()) { + if (marker.getTagName().equals(tagName)) + return marker; + } else { + if (marker.getTagName().equalsIgnoreCase(tagName)) + return marker; + } + } + return null; + } + + public List getBlockMarkers() { + return getTokenizer().getBlockMarkers(); + } + + /** + * @return IStructuredDocumentRegion + */ + public IStructuredDocumentRegion getDocumentRegions() { + IStructuredDocumentRegion headnode = null; + if (headnode == null) { + if (Debug.perfTest) { + startTime = System.currentTimeMillis(); + } + headnode = parseNodes(); + if (Debug.perfTest) { + stopTime = System.currentTimeMillis(); + System.out.println(" -- creating nodes of IStructuredDocument -- "); //$NON-NLS-1$ + System.out.println(" Time parse and init all regions: " + (stopTime - startTime) + " (msecs)"); //$NON-NLS-2$//$NON-NLS-1$ + //System.out.println(" for " + fRegions.size() + " + // Regions");//$NON-NLS-2$//$NON-NLS-1$ + System.out.println(" and " + _countNodes(headnode) + " Nodes"); //$NON-NLS-2$//$NON-NLS-1$ + } + } + return headnode; + } + + protected ITextRegion getNextRegion() { + ITextRegion region = null; + try { + region = getTokenizer().getNextToken(); + // DMW: 2/12/03 Removed state + // if (region != null) { + // fRegions.add(region); + // } + return region; + } catch (StackOverflowError e) { + Logger.logException(getClass().getName() + ": input could not be parsed correctly at position " + getTokenizer().getOffset(), e); //$NON-NLS-1$ + throw e; + } catch (Exception e) { + Logger.logException(getClass().getName() + ": input could not be parsed correctly at position " + getTokenizer().getOffset() + " (" + e.getLocalizedMessage() + ")", e); //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ + } + return null; + } + + /** + * Return the full list of known regions. Typically getNodes should be + * used instead of this method. + */ + public List getRegions() { + IStructuredDocumentRegion headNode = null; + if (!getTokenizer().isEOF()) { + headNode = getDocumentRegions(); + // throw new IllegalStateException("parsing has not finished"); + } + // for memory recovery, we assume if someone + // requests all regions, we can reset our big + // memory consuming objects + // but the new "getRegions" method is then more expensive. + // I don't think its used much, though. + List localRegionsList = getRegions(headNode); + primReset(); + return localRegionsList; + } + + /** + * Method getRegions. + * + * @param headNode + * @return List + */ + protected List getRegions(IStructuredDocumentRegion headNode) { + List allRegions = new ArrayList(); + IStructuredDocumentRegion currentNode = headNode; + while (currentNode != null) { + ITextRegionList nodeRegions = currentNode.getRegions(); + for (int i = 0; i < nodeRegions.size(); i++) { + allRegions.add(nodeRegions.get(i)); + } + currentNode = currentNode.getNext(); + } + return allRegions; + } + + /** + * + * @return java.util.List + */ + public List getStructuredDocumentRegionHandlers() { + if (fStructuredDocumentRegionHandlers == null) { + fStructuredDocumentRegionHandlers = new ArrayList(0); + } + return fStructuredDocumentRegionHandlers; + } + + /** + * Returns text from the current input. Text is only valid before + * getNodes() has been called and only when a raw String or DocumentReader + * is given as the input. + */ + public String getText(int offset, int length) { + String text = null; + if (fCharSequenceSource != null) { + int start = fOffset + offset; + int end = start + length; + text = fCharSequenceSource.subSequence(start, end).toString(); + } else if (fDocumentInput != null) { + try { + text = fDocumentInput.get(offset, length); + } catch (BadLocationException e) { + text = ""; + } + } else { + if (fStringInput == null || fStringInput.length() == 0 || offset + length > fStringInput.length() || offset < 0) { + text = ""; //$NON-NLS-1$ + } else { + // offset is entirely valid during parsing as the parse + // numbers haven't been adjusted. + text = fStringInput.substring(offset, offset + length); + } + } + return text; + } + + protected BlockTokenizer getTokenizer() { + if (fTokenizer == null) { + fTokenizer = new XMLTokenizer(); + } + return fTokenizer; + } + + /** + * @see com.ibm.sed.parser.RegionParser#newInstance() + */ + public RegionParser newInstance() { + XMLSourceParser newInstance = new XMLSourceParser(); + newInstance.setTokenizer(getTokenizer().newInstance()); + return newInstance; + } + + protected IStructuredDocumentRegion parseNodes() { + // regions are initially reported as complete offsets within the + // scanned input + // they are adjusted here to be indexes from the currentNode's start + // offset + IStructuredDocumentRegion headNode = null; + IStructuredDocumentRegion lastNode = null; + ITextRegion region = null; + IStructuredDocumentRegion currentNode = null; + String type = null; + + while ((region = getNextRegion()) != null) { + type = region.getType(); + // these types (might) demand a IStructuredDocumentRegion for each + // of them + if (type == XMLRegionContext.BLOCK_TEXT) { + if (currentNode != null && currentNode.getLastRegion().getType() == XMLRegionContext.BLOCK_TEXT) { + // multiple block texts indicated embedded containers; no + // new IStructuredDocumentRegion + currentNode.addRegion(region); + currentNode.setLength(region.getStart() + region.getLength() - currentNode.getStart()); + region.adjustStart(-currentNode.getStart()); + // DW 4/16/2003 regions no longer have parents + //region.setParent(currentNode); + } else { + // not continuing a IStructuredDocumentRegion + if (currentNode != null) { + // ensure that any existing node is at least + // terminated + if (!currentNode.isEnded()) { + currentNode.setLength(region.getStart() - currentNode.getStart()); + // fCurrentNode.setTextLength(region.getStart() - + // fCurrentNode.getStart()); + } + lastNode = currentNode; + } + fireNodeParsed(currentNode); + currentNode = createStructuredDocumentRegion(type); + if (lastNode != null) { + lastNode.setNext(currentNode); + } + currentNode.setPrevious(lastNode); + currentNode.setStart(region.getStart()); + currentNode.setLength(region.getStart() + region.getLength() - currentNode.getStart()); + currentNode.setEnded(true); + region.adjustStart(-currentNode.getStart()); + currentNode.addRegion(region); + // DW 4/16/2003 regions no longer have parents + //region.setParent(currentNode); + } + } + // the following contexts OPEN new StructuredDocumentRegions + else if ((currentNode != null && currentNode.isEnded()) || (type == XMLRegionContext.XML_CONTENT) || (type == XMLRegionContext.XML_CHAR_REFERENCE) || (type == XMLRegionContext.XML_ENTITY_REFERENCE) || (type == XMLRegionContext.XML_PI_OPEN) || (type == XMLRegionContext.XML_TAG_OPEN) || (type == XMLRegionContext.XML_END_TAG_OPEN) || (type == XMLRegionContext.XML_COMMENT_OPEN) || (type == XMLRegionContext.XML_CDATA_OPEN) || (type == XMLRegionContext.XML_DECLARATION_OPEN)) { + if (currentNode != null) { + // ensure that any existing node is at least terminated + if (!currentNode.isEnded()) { + currentNode.setLength(region.getStart() - currentNode.getStart()); + // fCurrentNode.setTextLength(region.getStart() - + // fCurrentNode.getStart()); + } + lastNode = currentNode; + } + fireNodeParsed(currentNode); + currentNode = createStructuredDocumentRegion(type); + if (lastNode != null) { + lastNode.setNext(currentNode); + } + currentNode.setPrevious(lastNode); + currentNode.setStart(region.getStart()); + currentNode.addRegion(region); + currentNode.setLength(region.getStart() + region.getLength() - currentNode.getStart()); + region.adjustStart(-currentNode.getStart()); + // DW 4/16/2003 regions no longer have parents + //region.setParent(currentNode); + } + // the following contexts neither open nor close + // StructuredDocumentRegions; just add to them + else if ((type == XMLRegionContext.XML_TAG_NAME) || (type == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME) || (type == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS) || (type == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) || (type == XMLRegionContext.XML_COMMENT_TEXT) || (type == XMLRegionContext.XML_PI_CONTENT) || (type == XMLRegionContext.XML_DOCTYPE_INTERNAL_SUBSET)) { + currentNode.addRegion(region); + currentNode.setLength(region.getStart() + region.getLength() - currentNode.getStart()); + region.adjustStart(-currentNode.getStart()); + // DW 4/16/2003 regions no longer have parents + //region.setParent(currentNode); + } + // the following contexts close off StructuredDocumentRegions + // cleanly + else if ((type == XMLRegionContext.XML_PI_CLOSE) || (type == XMLRegionContext.XML_TAG_CLOSE) || (type == XMLRegionContext.XML_EMPTY_TAG_CLOSE) || (type == XMLRegionContext.XML_COMMENT_CLOSE) || (type == XMLRegionContext.XML_DECLARATION_CLOSE) || (type == XMLRegionContext.XML_CDATA_CLOSE)) { + currentNode.setEnded(true); + currentNode.setLength(region.getStart() + region.getLength() - currentNode.getStart()); + currentNode.addRegion(region); + region.adjustStart(-currentNode.getStart()); + // DW 4/16/2003 regions no longer have parents + //region.setParent(currentNode); + } + // this is extremely rare, but valid + else if (type == XMLRegionContext.WHITE_SPACE) { + ITextRegion lastRegion = currentNode.getLastRegion(); + // pack the embedded container with this region + if (lastRegion instanceof ITextRegionContainer) { + ITextRegionContainer container = (ITextRegionContainer) lastRegion; + container.getRegions().add(region); + // containers must have parent set ... + // setting for EACH subregion is redundent, but not sure + // where else to do, so will do here for now. + container.setParent(currentNode); + // DW 4/16/2003 regions no longer have parents + //region.setParent(container); + region.adjustStart(container.getLength() - region.getStart()); + } + currentNode.getLastRegion().adjustLengthWith(region.getLength()); + currentNode.adjustLengthWith(region.getLength()); + } else if (type == XMLRegionContext.UNDEFINED && currentNode != null) { + // skip on a very-first region situation as the default + // behavior is good enough + // combine with previous if also undefined + if (currentNode.getLastRegion() != null && currentNode.getLastRegion().getType() == XMLRegionContext.UNDEFINED) { + currentNode.getLastRegion().adjustLengthWith(region.getLength()); + currentNode.adjustLengthWith(region.getLength()); + } + // previous wasn't undefined + else { + currentNode.addRegion(region); + currentNode.setLength(region.getStart() + region.getLength() - currentNode.getStart()); + region.adjustStart(-currentNode.getStart()); + } + } else { + // if an unknown type is the first region in the document, + // ensure that a node exists + if (currentNode == null) { + currentNode = createStructuredDocumentRegion(type); + currentNode.setStart(region.getStart()); + } + currentNode.addRegion(region); + currentNode.setLength(region.getStart() + region.getLength() - currentNode.getStart()); + region.adjustStart(-currentNode.getStart()); + // DW 4/16/2003 regions no longer have parents + //region.setParent(currentNode); + if (Debug.debugTokenizer) + System.out.println(getClass().getName() + " found region of not specifically handled type " + region.getType() + " @ " + region.getStart() + "[" + region.getLength() + "]"); //$NON-NLS-4$//$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ + //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ + } + + // these regions also get their own node, so close them cleanly + // NOTE: these regions have new StructuredDocumentRegions created + // for them above; it may + // be more readable if that is handled here as well, but the + // current layout + // ensures that they open StructuredDocumentRegions the same way + if ((type == XMLRegionContext.XML_CONTENT) || (type == XMLRegionContext.XML_CHAR_REFERENCE) || (type == XMLRegionContext.XML_ENTITY_REFERENCE)) { + currentNode.setEnded(true); + } + if (headNode == null && currentNode != null) { + headNode = currentNode; + } + } + if (currentNode != null) { + fireNodeParsed(currentNode); + currentNode.setPrevious(lastNode); + } + //fStringInput = null; + primReset(); + return headNode; + } + + protected void primReset() { + //fNodes = null; + //fRegions = null; + //fInput = null; + fStringInput = null; + fCharSequenceSource = null; + fDocumentInput = null; + fOffset = 0; + //fCurrentNode = null; + // DMW: also reset tokenizer so it doesn't hold on + // to large arrays + getTokenizer().reset(new char[0]); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.text.IRegionComparible#regionMatches(int, + * int, java.lang.String) + */ + public boolean regionMatches(int offset, int length, String stringToCompare) { + // by definition + if (stringToCompare == null) + return false; + + boolean result = false; + if (fCharSequenceSource != null && fCharSequenceSource instanceof IRegionComparible) { + result = ((IRegionComparible) fCharSequenceSource).regionMatches(offset, length, stringToCompare); + } else { + // old fashioned ways + String test = null; + if (fCharSequenceSource != null) { + test = fCharSequenceSource.subSequence(offset, offset + length).toString(); + } else if (fStringInput != null) { + test = fStringInput.substring(offset, offset + length); + } + result = stringToCompare.equals(test); + } + return result; + } + + public boolean regionMatchesIgnoreCase(int offset, int length, String stringToCompare) { + // by definition + if (stringToCompare == null) + return false; + + boolean result = false; + if (fCharSequenceSource != null && fCharSequenceSource instanceof IRegionComparible) { + result = ((IRegionComparible) fCharSequenceSource).regionMatchesIgnoreCase(offset, length, stringToCompare); + } else { + // old fashioned ways + String test = null; + if (fCharSequenceSource != null) { + test = fCharSequenceSource.subSequence(offset, offset + length).toString(); + } else if (fStringInput != null) { + test = fStringInput.substring(offset, offset + length); + } + result = stringToCompare.equalsIgnoreCase(test); + } + return result; + } + + public void removeBlockMarker(BlockMarker marker) { + getTokenizer().removeBlockMarker(marker); + } + + public void removeBlockMarker(String tagName) { + getTokenizer().removeBlockMarker(tagName); + } + + public void removeStructuredDocumentRegionHandler(StructuredDocumentRegionHandler handler) { + if (fStructuredDocumentRegionHandlers == null) + return; + if (fStructuredDocumentRegionHandlers.contains(handler)) + fStructuredDocumentRegionHandlers.remove(handler); + } + + /** + * Resets the input. + */ + public void reset(java.io.FileInputStream instream) { + primReset(); + //fInput = instream; + getTokenizer().reset(instream); + } + + /** + * Resets the input. + */ + public void reset(java.io.Reader reader) { + reset(reader, 0); + } + + /** + * Resets the input. + */ + public void reset(java.io.Reader reader, int position) { + primReset(); + fOffset = position; + getTokenizer().reset(reader, position); + if (reader instanceof DocumentReader) { + IDocument doc = ((DocumentReader) reader).getDocument(); + if (doc instanceof CharSequence) { + fCharSequenceSource = (CharSequence) doc; + } else { + // old fashioned IDocument + fDocumentInput = ((DocumentReader) reader).getDocument(); + } + + } else if (reader instanceof CharSequenceReader) { + fCharSequenceSource = ((CharSequenceReader) reader).getOriginalSource(); + } + } + + /** + * Resets the input. Use this version to allow text to be retrieved + * <em>during</em> parsing, such as by the + * StructuredDocumentRegionHandler. + */ + public void reset(String sourceString) { + reset(new StringReader(sourceString)); + fStringInput = sourceString; + } + + /** + * Resets the input. Use this version to allow text to be retrieved + * <em>during</em> parsing, such as by the + * StructuredDocumentRegionHandler. + */ + public void reset(String sourceString, int position) { + StringReader reader = new StringReader(sourceString); + reset(reader, position); + fStringInput = sourceString; + } + + public void resetHandlers() { + if (fStructuredDocumentRegionHandlers != null) { + int size = fStructuredDocumentRegionHandlers.size(); + for (int i = 0; i < size; i++) + ((StructuredDocumentRegionHandler) fStructuredDocumentRegionHandlers.get(i)).resetNodes(); + } + } + + /** + * + * @param List + */ + public void setStructuredDocumentRegionHandlers(List newStructuredDocumentRegionHandlers) { + fStructuredDocumentRegionHandlers = newStructuredDocumentRegionHandlers; + } + + protected void setTokenizer(BlockTokenizer newTokenizer) { + // DMW: changed from private to protected, so subclass could use in + // creation of 'newInstance'. + fTokenizer = newTokenizer; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLStructuredDocumentReParser.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLStructuredDocumentReParser.java new file mode 100644 index 0000000000..518d4f63c6 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLStructuredDocumentReParser.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser; + +import org.eclipse.wst.sse.core.internal.text.StructuredDocumentReParser; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.IStructuredTextReParser; +import org.eclipse.wst.xml.core.jsp.model.parser.temp.XMLJSPRegionContexts; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +public class XMLStructuredDocumentReParser extends StructuredDocumentReParser { + + public XMLStructuredDocumentReParser() { + super(); + } + + protected IStructuredDocumentRegion findDirtyEnd(int end) { + // Caution: here's one place we have to cast + IStructuredDocumentRegion result = fStructuredDocument.getRegionAtCharacterOffset(end); + // if not well formed, get one past, if there is something there + if ((result != null) && (!result.isEnded())) { + if (result.getNext() != null) { + result = result.getNext(); + } + } + // also, get one past if exactly equal to the end (this was needed + // as a simple fix to when a whole exact region is deleted. + // there's probably a better way. + if ((result != null) && (end == result.getEnd())) { + if (result.getNext() != null) { + result = result.getNext(); + } + } + + // 12/6/2001 - Since we've changed the parser/scanner to allow a lone + // '<' without + // always interpretting it as start of a tag name, we need to be a + // little fancier, in order + // to "skip" over any plain 'ol content between the lone '<' and any + // potential meating + // regions past plain 'ol content. + if (isLoneOpenFollowedByContent(result) && (result.getNext() != null)) { + result = result.getNext(); + } + + if (result != null) + fStructuredDocument.setCachedDocumentRegion(result); + dirtyEnd = result; + + return dirtyEnd; + } + + protected void findDirtyStart(int start) { + IStructuredDocumentRegion result = fStructuredDocument.getRegionAtCharacterOffset(start); + // heuristic: if the postion is exactly equal to the start, then + // go back one more, if it exists. This prevents problems with + // insertions + // of text that should be merged with the previous node instead of + // simply hung + // off of it as a separate node (ex.: XML content inserted right + // before an open + // bracket should become part of the previous content node) + if (result != null) { + IStructuredDocumentRegion previous = result.getPrevious(); + if ((previous != null) && ((!(previous.isEnded())) || (start == result.getStart()))) { + result = previous; + } + // If we are now at the end of a "tag dependent" content area (or + // JSP area) + // then we need to back up all the way to the beginning of that. + IStructuredDocumentRegion potential = result; + while (isPartOfBlockRegion(potential)) { + potential = potential.getPrevious(); + } + if (potential != null) { + result = potential; + fStructuredDocument.setCachedDocumentRegion(result); + } + } + dirtyStart = result; + } + + /** + * The rule is, that is we are content, preceded by lone <, then we need + * to advance one more for dirty end. + */ + protected boolean isLoneOpenFollowedByContent(IStructuredDocumentRegion flatNode) { + boolean result = false; + String type = flatNode.getType(); + if (type == XMLRegionContext.XML_CONTENT) { + IStructuredDocumentRegion previous = flatNode.getPrevious(); + String previousType = null; + if (previous != null) { + previousType = previous.getType(); + } + if (previousType != null) { + result = (previousType == XMLRegionContext.XML_TAG_OPEN); + } + } + return result; + } + + protected boolean isPartOfBlockRegion(IStructuredDocumentRegion flatNode) { + boolean result = false; + String type = flatNode.getType(); + result = (type == XMLRegionContext.BLOCK_TEXT || type == XMLJSPRegionContexts.JSP_CONTENT); + return result; + } + + public IStructuredTextReParser newInstance() { + return new XMLStructuredDocumentReParser(); + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLStructuredRegionFactory.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLStructuredRegionFactory.java new file mode 100644 index 0000000000..0411d664e2 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLStructuredRegionFactory.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser; + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.xml.core.internal.text.XMLStructuredDocumentRegion; + + +/** + * A simple class to generate instances of StructuredRegions. + */ +public class XMLStructuredRegionFactory { + public final static int JSP_DIRECTIVE = 1003; + public final static int XML = 1001; + public final static int XML_BLOCK = 1002; + + public static IStructuredDocumentRegion createRegion(int type) { + IStructuredDocumentRegion instance = null; + switch (type) { + case XML : + instance = new XMLStructuredDocumentRegion(); + break; + case XML_BLOCK : + instance = new BlockStructuredDocumentRegion(); + break; + default : + throw new IllegalArgumentException("AbstractRegion::createRegion. Invalid type."); //$NON-NLS-1$ + } + return instance; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLTokenizer.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLTokenizer.java new file mode 100644 index 0000000000..5f1101f1f8 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/XMLTokenizer.java @@ -0,0 +1,1699 @@ +/******************************************************************************* + * Copyright (c) 2004 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 + *******************************************************************************/ +/* The following code was generated by JFlex 1.2.2 on 9/23/04 1:31 AM */ + +/*nlsXXX*/ +package org.eclipse.wst.xml.core.internal.parser; + +import java.io.CharArrayReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.wst.sse.core.parser.BlockMarker; +import org.eclipse.wst.sse.core.parser.BlockTokenizer; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.util.Debug; +import org.eclipse.wst.sse.core.util.StringUtils; +import org.eclipse.wst.xml.core.Logger; +import org.eclipse.wst.xml.core.internal.parser.regions.XMLParserRegionFactory; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + + +/** + * This class is a scanner generated by <a + * href="http://www.informatik.tu-muenchen.de/~kleing/jflex/">JFlex </a> 1.2.2 + * on 9/23/04 1:31 AM from the specification file + * <tt>file:/D:/eclipse.rad/workspace.rad/org.eclipse.wst.sse.core/DevTimeSupport/SedModel/HTMLTokenizer/devel/XMLTokenizer.jflex</tt> + */ +public class XMLTokenizer implements BlockTokenizer, XMLRegionContext { + + /** this character denotes the end of file */ + final public static int YYEOF = -1; + + /** lexical states */ + final public static int ST_XML_DOCTYPE_EXTERNAL_ID = 23; + final public static int ST_XML_ELEMENT_DECLARATION_CONTENT = 27; + final public static int ST_DHTML_ATTRIBUTE_NAME = 12; + final public static int ST_XML_PI_TAG_CLOSE = 11; + final public static int ST_XML_DECLARATION_CLOSE = 21; + final public static int ST_XML_PI_ATTRIBUTE_VALUE = 10; + final public static int ST_DHTML_EQUALS = 13; + final public static int ST_XML_TAG_NAME = 16; + final public static int ST_XML_ATTRIBUTE_VALUE = 19; + final public static int ST_DHTML_ATTRIBUTE_VALUE = 14; + final public static int ST_XML_DOCTYPE_ID_SYSTEM = 25; + final public static int ST_XML_ATTRIBUTE_NAME = 17; + final public static int ST_XML_ELEMENT_DECLARATION = 26; + final public static int ST_XML_DOCTYPE_DECLARATION = 22; + final public static int ST_XML_ATTLIST_DECLARATION = 28; + final public static int ST_XML_COMMENT_END = 4; + final public static int ST_CDATA_TEXT = 1; + final public static int ST_DHTML_TAG_CLOSE = 15; + final public static int ST_XML_COMMENT = 3; + final public static int ST_PI_CONTENT = 7; + final public static int ST_PI_WS = 6; + final public static int ST_CDATA_END = 2; + final public static int ST_XML_ATTLIST_DECLARATION_CONTENT = 29; + final public static int ST_BLOCK_TAG_SCAN = 30; + final public static int ST_XML_PI_EQUALS = 9; + final public static int ST_XML_DECLARATION = 20; + final public static int YYINITIAL = 0; + final public static int ST_XML_DOCTYPE_ID_PUBLIC = 24; + final public static int ST_XML_EQUALS = 18; + final public static int ST_PI = 5; + final public static int ST_XML_PI_ATTRIBUTE_NAME = 8; + + /** + * Translates characters to character classes + */ + final private static String yycmap_packed = "\11\0\1\5\1\22\2\0\1\14\22\0\1\14\1\21\1\11\1\51" + "\1\16\1\17\1\12\1\13\1\16\1\16\1\16\1\16\1\16\1\7" + "\1\6\1\3\12\15\1\10\1\54\1\1\1\43\1\2\1\4\1\16" + "\1\32\1\55\1\30\1\31\1\35\1\52\1\34\1\34\1\40\1\34" + "\1\34\1\26\1\25\1\42\1\41\1\45\1\34\1\36\1\37\1\33" + "\1\53\2\34\1\23\1\44\1\34\1\27\1\0\1\20\1\0\1\10" + "\1\0\1\47\1\55\1\56\1\50\1\35\1\52\1\34\1\34\1\40" + "\2\34\1\26\1\25\1\42\1\41\1\45\1\34\1\36\1\37\1\46" + "\1\53\1\34\1\34\1\24\1\44\1\34\1\0\1\0\72\0\1\60" + "\10\0\27\57\1\0\37\57\1\0\72\57\2\0\13\57\2\0\10\57" + "\1\0\65\57\1\0\104\57\11\0\44\57\3\0\2\57\4\0\36\57" + "\70\0\131\57\22\0\7\57\16\0\2\60\56\0\106\60\32\0\2\60" + "\44\0\1\57\1\60\3\57\1\0\1\57\1\0\24\57\1\0\54\57" + "\1\0\7\57\3\0\1\57\1\0\1\57\1\0\1\57\1\0\1\57" + "\1\0\22\57\15\0\14\57\1\0\102\57\1\0\14\57\1\0\44\57" + "\1\0\4\60\11\0\65\57\2\0\2\57\2\0\2\57\3\0\34\57" + "\2\0\10\57\2\0\2\57\67\0\46\57\2\0\1\57\7\0\46\57" + + "\12\0\21\60\1\0\27\60\1\0\3\60\1\0\1\60\1\0\2\60" + "\1\0\1\60\13\0\33\57\5\0\3\57\56\0\32\57\5\0\1\60" + "\12\57\10\60\15\0\12\60\6\0\1\60\107\57\2\0\5\57\1\0" + "\17\57\1\0\4\57\1\0\1\57\17\60\2\57\2\60\1\0\4\60" + "\2\0\12\60\u0207\0\3\60\1\0\65\57\2\0\1\60\1\57\20\60" + "\3\0\4\60\3\0\12\57\2\60\2\0\12\60\21\0\3\60\1\0" + "\10\57\2\0\2\57\2\0\26\57\1\0\7\57\1\0\1\57\3\0" + "\4\57\2\0\1\60\1\0\7\60\2\0\2\60\2\0\3\60\11\0" + "\1\60\4\0\2\57\1\0\3\57\2\60\2\0\12\60\2\57\20\0" + "\1\60\2\0\6\57\4\0\2\57\2\0\26\57\1\0\7\57\1\0" + "\2\57\1\0\2\57\1\0\2\57\2\0\1\60\1\0\5\60\4\0" + "\2\60\2\0\3\60\13\0\4\57\1\0\1\57\7\0\12\60\2\60" + "\3\57\14\0\3\60\1\0\7\57\1\0\1\57\1\0\3\57\1\0" + "\26\57\1\0\7\57\1\0\2\57\1\0\5\57\2\0\1\60\1\57" + "\10\60\1\0\3\60\1\0\3\60\22\0\1\57\5\0\12\60\21\0" + "\3\60\1\0\10\57\2\0\2\57\2\0\26\57\1\0\7\57\1\0" + "\2\57\2\0\4\57\2\0\1\60\1\57\6\60\3\0\2\60\2\0" + "\3\60\10\0\2\60\4\0\2\57\1\0\3\57\4\0\12\60\22\0" + + "\2\60\1\0\6\57\3\0\3\57\1\0\4\57\3\0\2\57\1\0" + "\1\57\1\0\2\57\3\0\2\57\3\0\3\57\3\0\10\57\1\0" + "\3\57\4\0\5\60\3\0\3\60\1\0\4\60\11\0\1\60\17\0" + "\11\60\21\0\3\60\1\0\10\57\1\0\3\57\1\0\27\57\1\0" + "\12\57\1\0\5\57\4\0\7\60\1\0\3\60\1\0\4\60\7\0" + "\2\60\11\0\2\57\4\0\12\60\22\0\2\60\1\0\10\57\1\0" + "\3\57\1\0\27\57\1\0\12\57\1\0\5\57\4\0\7\60\1\0" + "\3\60\1\0\4\60\7\0\2\60\7\0\1\57\1\0\2\57\4\0" + "\12\60\22\0\2\60\1\0\10\57\1\0\3\57\1\0\27\57\1\0" + "\20\57\4\0\6\60\2\0\3\60\1\0\4\60\11\0\1\60\10\0" + "\2\57\4\0\12\60\221\0\56\57\1\0\1\57\1\60\2\57\7\60" + "\5\0\6\57\1\60\10\60\1\0\12\60\47\0\2\57\1\0\1\57" + "\2\0\2\57\1\0\1\57\2\0\1\57\6\0\4\57\1\0\7\57" + "\1\0\3\57\1\0\1\57\1\0\1\57\2\0\2\57\1\0\2\57" + "\1\0\1\57\1\60\2\57\6\60\1\0\2\60\1\57\2\0\5\57" + "\1\0\1\60\1\0\6\60\2\0\12\60\76\0\2\60\6\0\12\60" + "\13\0\1\60\1\0\1\60\1\0\1\60\4\0\2\60\10\57\1\0" + "\41\57\7\0\24\60\1\0\6\60\4\0\6\60\1\0\1\60\1\0" + + "\25\60\3\0\7\60\1\0\1\60\346\0\46\57\12\0\47\57\11\0" + "\1\57\1\0\2\57\1\0\3\57\1\0\1\57\1\0\2\57\1\0" + "\5\57\51\0\1\57\1\0\1\57\1\0\1\57\13\0\1\57\1\0" + "\1\57\1\0\1\57\3\0\2\57\3\0\1\57\5\0\3\57\1\0" + "\1\57\1\0\1\57\1\0\1\57\1\0\1\57\3\0\2\57\3\0" + "\2\57\1\0\1\57\50\0\1\57\11\0\1\57\2\0\1\57\2\0" + "\2\57\7\0\2\57\1\0\1\57\1\0\7\57\50\0\1\57\4\0" + "\1\57\10\0\1\57\u0c06\0\234\57\4\0\132\57\6\0\26\57\2\0" + "\6\57\2\0\46\57\2\0\6\57\2\0\10\57\1\0\1\57\1\0" + "\1\57\1\0\1\57\1\0\37\57\2\0\65\57\1\0\7\57\1\0" + "\1\57\3\0\3\57\1\0\7\57\3\0\4\57\2\0\6\57\4\0" + "\15\57\5\0\3\57\1\0\7\57\323\0\15\60\4\0\1\60\104\0" + "\1\57\3\0\2\57\2\0\1\57\121\0\3\57\u0e82\0\1\60\1\0" + "\1\57\31\0\11\57\6\60\1\0\5\60\13\0\124\57\4\0\2\60" + "\2\0\2\60\2\0\132\57\1\0\3\60\6\0\50\57\u1cd3\0\u51a6\57" + "\u0c5a\0\u2ba4\57\134\0\u0800\0\u1ffe\0\2\0"; + + /** + * Translates characters to character classes + */ + final private static char[] yycmap = yy_unpack_cmap(yycmap_packed); + + /** + * Translates a state to a row index in the transition table + */ + final private static int yy_rowMap[] = {0, 49, 98, 147, 196, 245, 294, 343, 392, 441, 490, 539, 588, 637, 686, 735, 784, 833, 882, 931, 980, 1029, 1078, 1127, 1176, 1225, 1274, 1323, 1372, 1421, 1470, 1519, 1568, 1617, 1666, 1715, 1764, 1715, 1764, 1813, 1715, 1715, 1764, 1862, 1911, 1960, 2009, 2058, 2107, 2156, 1715, 1764, 2205, 2254, 2303, 1715, 2352, 2352, 2401, 2450, 2499, 2205, 1715, 2548, 2597, 1715, 2646, 2695, 2744, 2793, 2842, 2891, 1715, 2940, 2989, 3038, 3087, 1715, 3136, 3185, 3234, 3283, 3332, 1715, 3381, 3430, 3479, 3528, 3577, 3626, 3675, 3724, 3724, 3773, 3822, 3871, 3920, 3920, 3969, 4018, 4067, 4116, 4116, 4165, 4214, 4263, 4312, 1715, 4361, 4361, 4410, 4459, 4508, 4557, 1715, 1715, 1764, 1715, 1715, 4606, 4655, 4704, 4753, 4802, 4851, 4900, 4949, 1715, 4998, 5047, 1715, 1715, 2352, 5096, 2450, 1715, 5145, 2499, 2548, 2646, 2695, 5194, 2744, 1715, 5243, 2793, 1715, 3136, 5292, 3234, 1715, 5341, 3283, 4606, 5390, 5439, 5488, 3528, 1715, 5537, 5586, 3724, 5635, + 3773, 1715, 5684, 5733, 5782, 5782, 5831, 5880, 3871, 3724, 3920, 5929, 3969, 1715, 5978, 4018, 4067, 3920, 4116, 6027, 4165, 1715, 6076, 6125, 6174, 6174, 6223, 6272, 6321, 4361, 6370, 4410, 1715, 6419, 6468, 6517, 6517, 6566, 6615, 6664, 6713, 6762, 6811, 6860, 1715, 6909, 6958, 1715, 1715, 1715, 2009, 7007, 7056, 7105, 7154, 7203, 7252, 5684, 7301, 7301, 6076, 7350, 7350, 7399, 6419, 7448, 7448, 7497, 1715, 7546, 7595, 1715, 7644, 7693, 7742, 7791, 7840, 7889, 7938, 5831, 6223, 7987, 6566, 8036, 8085, 8134, 8183, 8232, 8281, 8330, 8379, 8428, 8477, 8526, 2009, 8575, 8624, 8673, 1715, 1715, 8722, 8771, 8820, 1715, 1715, 1715, 8869, 8918, 8967, 9016, 9065, 1715, 4263, 4508}; + + /** + * The packed transition table of the DFA + */ + final private static String yy_packed = "\1\40\1\41\10\40\1\42\4\40\1\43\41\40\1\44" + "\1\45\57\44\1\46\1\47\16\46\1\50\1\46\1\51" + "\36\46\1\52\1\53\57\52\1\46\1\47\5\46\1\54" + "\12\46\1\51\37\46\1\47\2\46\1\55\1\56\2\46" + "\1\57\3\46\1\56\5\46\1\56\2\60\2\57\1\46" + "\10\57\1\61\2\57\1\46\5\57\1\46\2\57\1\46" + "\3\57\2\46\1\47\2\46\1\55\1\62\6\46\1\62" + "\5\46\1\62\36\46\1\63\1\64\2\63\1\65\15\63" + "\1\51\36\63\1\46\1\47\2\46\1\66\1\56\2\46" + "\1\67\3\46\1\56\5\46\1\56\4\67\1\46\13\67" + "\1\46\5\67\1\46\2\67\1\46\3\67\2\46\1\47" + "\2\46\1\66\1\56\2\46\1\67\3\46\1\56\5\46" + "\1\56\4\67\1\46\13\67\1\70\5\67\1\46\2\67" + "\1\46\3\67\1\46\1\71\1\47\1\46\1\72\1\73" + "\1\56\3\71\1\74\1\71\1\75\1\56\5\71\1\56" + "\36\71\1\46\1\47\2\46\1\76\15\46\1\51\37\46" + "\1\47\1\77\1\100\1\46\1\56\2\46\1\101\3\46" + "\1\56\5\46\1\56\4\101\1\46\13\101\1\46\5\101" + "\1\46\2\101\1\46\3\101\2\46\1\47\1\77\1\100" + "\1\46\1\56\2\46\1\101\3\46\1\56\5\46\1\56" + + "\4\101\1\46\13\101\1\102\5\101\1\46\2\101\1\46" + "\3\101\1\46\1\103\1\47\1\77\1\104\1\103\1\56" + "\3\103\1\105\1\103\1\106\1\56\5\103\1\56\36\103" + "\1\46\1\47\3\46\1\56\6\46\1\56\5\46\1\56" + "\36\46\1\107\1\110\1\111\1\112\4\107\1\113\12\107" + "\4\114\1\107\13\114\1\107\5\114\1\107\2\114\1\107" + "\3\114\1\107\1\46\1\110\1\111\1\112\1\46\1\56" + "\2\46\1\115\3\46\1\56\5\46\1\56\4\115\1\46" + "\13\115\1\46\5\115\1\46\2\115\1\46\3\115\2\46" + "\1\110\1\111\1\112\1\46\1\56\2\46\1\115\3\46" + "\1\56\5\46\1\56\4\115\1\46\13\115\1\116\5\115" + "\1\46\2\115\1\46\3\115\1\46\1\117\1\110\1\111" + "\1\120\1\117\1\56\3\117\1\121\1\117\1\122\1\56" + "\5\117\1\56\36\117\1\46\1\123\1\124\2\46\1\56" + "\6\46\1\56\5\46\1\56\6\46\1\125\1\126\2\46" + "\1\127\11\46\1\126\1\125\11\46\1\47\1\124\2\46" + "\1\56\6\46\1\56\5\46\1\56\4\46\1\130\32\46" + "\1\47\1\124\2\46\1\56\2\46\1\131\3\46\1\56" + "\5\46\1\56\4\131\1\130\13\131\1\46\5\131\1\46" + + "\2\131\1\46\3\131\2\46\1\47\1\124\2\46\1\56" + "\6\46\1\56\5\46\1\56\4\46\1\130\7\46\1\132" + "\5\46\1\133\13\46\1\134\1\47\1\124\1\135\1\134" + "\1\56\3\134\1\136\1\134\1\137\1\56\5\134\1\56" + "\4\134\1\140\31\134\1\141\1\47\1\124\1\142\1\141" + "\1\56\3\141\1\143\1\141\1\144\1\56\5\141\1\56" + "\4\141\1\145\31\141\1\146\1\47\1\124\1\147\1\146" + "\1\56\3\146\1\150\1\146\1\151\1\56\5\146\1\56" + "\36\146\1\152\1\153\1\154\56\152\1\155\1\47\1\124" + "\1\156\1\155\1\56\3\155\1\157\1\155\1\160\1\56" + "\5\155\1\56\36\155\1\161\1\162\1\163\56\161\1\164" + "\1\165\57\164\1\40\1\0\10\40\1\0\4\40\1\0" + "\41\40\3\0\1\166\1\167\14\0\1\170\44\0\1\171" + "\2\0\1\172\3\0\1\171\5\0\1\171\4\172\1\0" + "\13\172\1\0\5\172\1\173\2\172\1\0\3\172\6\0" + "\1\171\2\0\1\174\3\0\1\171\5\0\1\171\4\174" + "\1\0\13\174\1\0\5\174\1\0\2\174\1\0\3\174" + "\103\0\1\175\57\0\1\176\47\0\1\177\53\0\1\200" + "\63\0\1\56\6\0\1\56\5\0\1\56\44\0\3\57" + "\4\0\1\57\5\0\4\57\1\0\13\57\1\0\5\57" + + "\1\0\2\57\1\0\4\57\6\0\3\57\4\0\1\57" + "\5\0\2\57\1\201\1\57\1\0\13\57\1\0\5\57" + "\1\0\2\57\1\0\4\57\6\0\3\57\4\0\1\57" + "\5\0\2\57\1\202\1\57\1\0\13\57\1\0\5\57" + "\1\0\2\57\1\0\4\57\5\0\1\62\6\0\1\62" + "\5\0\1\62\40\0\1\203\60\0\1\204\64\0\3\67" + "\4\0\1\67\5\0\4\67\1\0\13\67\1\0\5\67" + "\1\0\2\67\1\0\4\67\1\71\2\0\1\205\1\71" + "\1\0\3\71\1\0\1\71\2\0\5\71\1\0\37\71" + "\1\0\1\204\1\205\1\71\1\0\3\71\1\0\1\71" + "\2\0\5\71\1\0\36\71\1\74\1\0\1\206\1\207" + "\1\74\1\206\3\74\1\210\1\74\2\206\5\74\1\206" + "\36\74\1\75\1\0\1\211\1\212\1\75\1\211\3\75" + "\1\211\1\75\1\210\1\211\5\75\1\211\36\75\2\0" + "\1\77\1\213\63\0\3\101\4\0\1\101\5\0\4\101" + "\1\0\13\101\1\0\5\101\1\0\2\101\1\0\4\101" + "\1\103\2\0\1\214\1\103\1\0\3\103\1\0\1\103" + "\2\0\5\103\1\0\37\103\1\0\1\77\1\215\1\103" + "\1\0\3\103\1\0\1\103\2\0\5\103\1\0\36\103" + "\1\105\1\0\1\216\1\217\1\105\1\216\3\105\1\220" + "\1\105\2\216\5\105\1\216\36\105\1\106\1\0\1\221" + + "\1\222\1\106\1\221\3\106\1\221\1\106\1\220\1\221" + "\5\106\1\221\36\106\1\107\3\0\17\107\4\0\1\107" + "\13\0\1\107\5\0\1\107\2\0\1\107\3\0\1\107" + "\3\0\1\166\15\0\1\170\41\0\1\223\56\0\1\107" + "\3\0\2\107\3\113\4\107\1\113\5\107\4\114\1\107" + "\13\114\1\107\5\114\1\107\2\114\1\107\3\114\1\113" + "\6\0\3\114\4\0\1\114\5\0\4\114\1\0\13\114" + "\1\0\5\114\1\0\2\114\1\0\4\114\6\0\3\115" + "\4\0\1\115\5\0\4\115\1\0\13\115\1\0\5\115" + "\1\0\2\115\1\0\4\115\1\117\2\0\1\224\1\117" + "\1\0\3\117\1\0\1\117\2\0\5\117\1\0\37\117" + "\1\0\1\223\1\224\1\117\1\0\3\117\1\0\1\117" + "\2\0\5\117\1\0\36\117\1\121\1\0\1\225\1\226" + "\1\121\1\225\3\121\1\227\1\121\2\225\5\121\1\225" + "\36\121\1\122\1\0\1\230\1\231\1\122\1\230\3\122" + "\1\230\1\122\1\227\1\230\5\122\1\230\36\122\3\0" + "\1\166\15\0\1\232\100\0\1\233\52\0\1\234\12\0" + "\1\234\40\0\1\235\32\0\20\236\1\237\40\236\6\0" + "\3\131\4\0\1\131\5\0\4\131\1\0\13\131\1\0" + "\5\131\1\0\2\131\1\0\4\131\44\0\1\240\67\0" + + "\1\241\5\0\1\134\2\0\1\242\1\134\1\0\3\134" + "\1\0\1\134\2\0\5\134\1\0\36\134\1\136\1\0" + "\1\243\1\244\1\136\1\243\3\136\1\245\1\136\2\243" + "\5\136\1\243\36\136\1\246\1\0\1\247\1\250\1\251" + "\1\247\3\251\1\247\1\246\1\252\1\253\3\251\1\246" + "\1\251\1\253\4\251\1\246\27\251\2\246\1\140\2\236" + "\1\254\1\140\1\236\3\140\1\236\1\140\2\236\3\140" + "\1\255\1\140\1\236\36\140\1\141\2\0\1\256\1\141" + "\1\0\3\141\1\0\1\141\2\0\5\141\1\0\36\141" + "\1\143\2\257\1\260\1\143\1\257\3\143\1\261\1\143" + "\2\257\5\143\1\257\36\143\1\144\2\262\1\263\1\144" + "\1\262\3\144\1\262\1\144\1\261\1\262\5\144\1\262" + "\36\144\1\145\2\236\1\264\1\145\1\236\3\145\1\236" + "\1\145\2\236\3\145\1\265\1\145\1\236\36\145\1\146" + "\2\0\1\266\1\146\1\0\3\146\1\0\1\146\2\0" + "\5\146\1\0\36\146\1\150\1\0\1\267\1\270\1\150" + "\1\267\3\150\1\271\1\150\2\267\5\150\1\267\36\150" + "\1\272\1\0\1\273\1\274\1\275\1\273\3\275\1\273" + "\1\272\1\276\1\277\3\275\1\272\1\275\1\277\4\275" + + "\1\272\27\275\2\272\2\152\1\0\60\152\1\0\16\152" + "\1\300\37\152\1\155\2\0\1\301\1\155\1\0\3\155" + "\1\0\1\155\2\0\5\155\1\0\36\155\1\157\1\0" + "\1\302\1\303\1\157\1\302\3\157\1\304\1\157\2\302" + "\5\157\1\302\36\157\1\305\1\0\1\306\1\307\1\310" + "\1\306\3\310\1\306\1\305\1\311\1\312\3\310\1\305" + "\1\310\1\312\4\310\1\305\27\310\2\305\2\161\1\0" + "\60\161\1\0\16\161\1\313\37\161\7\0\1\314\17\0" + "\1\315\36\0\1\171\2\0\1\40\3\0\1\171\5\0" + "\1\171\4\40\1\0\13\40\1\0\5\40\1\0\2\40" + "\1\0\3\40\1\0\1\316\1\0\3\316\1\317\3\172" + "\1\316\1\0\1\316\1\317\1\172\1\316\1\0\2\316" + "\1\317\4\172\1\316\13\172\1\316\5\172\1\316\2\172" + "\1\320\4\172\15\0\1\321\6\0\1\322\34\0\1\316" + "\1\0\3\316\1\317\3\174\1\316\1\0\1\316\1\317" + "\1\174\1\316\1\0\2\316\1\317\4\174\1\316\13\174" + "\1\316\5\174\1\316\2\174\1\323\4\174\27\0\1\315" + "\33\0\1\324\60\0\1\325\64\0\3\57\4\0\1\57" + "\5\0\3\57\1\326\1\0\13\57\1\0\5\57\1\0" + "\2\57\1\0\4\57\6\0\3\57\4\0\1\57\5\0" + + "\4\57\1\0\13\57\1\0\1\57\1\327\3\57\1\0" + "\2\57\1\0\4\57\1\206\1\0\7\206\1\210\47\206" + "\1\211\1\0\11\211\1\210\45\211\1\216\1\0\7\216" + "\1\220\47\216\1\221\1\0\11\221\1\220\45\221\1\225" + "\1\0\7\225\1\227\47\225\1\230\1\0\11\230\1\227" + "\45\230\30\0\1\330\25\0\1\330\35\0\1\331\12\0" + "\1\331\47\0\1\332\62\0\1\333\76\0\1\334\3\0" + "\1\243\1\0\7\243\1\245\47\243\1\246\1\0\1\247" + "\1\335\1\246\1\247\3\246\1\247\1\246\1\245\1\247" + "\5\246\1\247\36\246\1\247\1\0\11\247\1\245\45\247" + "\1\246\1\0\1\247\1\335\1\246\1\247\3\246\1\247" + "\1\246\1\336\1\247\5\246\1\247\36\246\13\0\1\337" + "\45\0\1\247\1\0\11\247\1\336\45\247\11\257\1\261" + "\47\257\13\262\1\261\45\262\1\267\1\0\7\267\1\271" + "\47\267\1\272\1\0\1\273\1\340\1\272\1\273\3\272" + "\1\273\1\272\1\271\1\273\5\272\1\273\36\272\1\273" + "\1\0\11\273\1\271\45\273\1\272\1\0\1\273\1\340" + "\1\272\1\273\3\272\1\273\1\272\1\341\1\273\5\272" + "\1\273\36\272\13\0\1\342\45\0\1\273\1\0\11\273" + + "\1\341\45\273\2\152\1\0\24\152\1\343\31\152\1\302" + "\1\0\7\302\1\304\47\302\1\305\1\0\1\306\1\344" + "\1\305\1\306\3\305\1\306\1\305\1\304\1\306\5\305" + "\1\306\36\305\1\306\1\0\11\306\1\304\45\306\1\305" + "\1\0\1\306\1\344\1\305\1\306\3\305\1\306\1\305" + "\1\345\1\306\5\305\1\306\36\305\13\0\1\346\45\0" + "\1\306\1\0\11\306\1\345\45\306\2\161\1\0\24\161" + "\1\347\31\161\7\0\1\350\101\0\1\351\30\0\1\316" + "\1\0\10\316\1\0\4\316\1\0\34\316\1\0\5\316" + "\1\0\3\316\1\317\4\316\1\0\1\316\1\317\2\316" + "\1\0\2\316\1\317\31\316\1\352\4\316\15\0\1\321" + "\36\0\1\353\21\0\1\354\12\0\3\354\2\0\1\354" + "\11\0\2\354\1\0\1\354\2\0\2\354\10\0\3\57" + "\4\0\1\57\5\0\4\57\1\0\11\57\1\355\1\57" + "\1\0\5\57\1\0\2\57\1\0\4\57\33\0\1\356" + "\12\0\1\356\40\0\1\357\57\0\1\360\66\0\1\361" + "\12\0\1\361\40\0\1\362\35\0\2\363\1\0\3\363" + "\2\0\1\252\4\363\1\0\6\363\1\0\27\363\5\0" + "\2\364\1\0\3\364\2\0\1\276\4\364\1\0\6\364" + + "\1\0\27\364\2\0\2\152\1\0\25\152\1\365\30\152" + "\3\0\2\366\1\0\3\366\2\0\1\311\4\366\1\0" + "\6\366\1\0\27\366\2\0\2\161\1\0\25\161\1\367" + "\30\161\31\0\1\370\103\0\1\352\21\0\1\354\12\0" + "\3\354\2\0\1\354\11\0\2\354\1\0\1\354\1\0" + "\1\353\2\354\10\0\3\57\4\0\1\57\5\0\4\57" + "\1\0\6\57\1\371\4\57\1\0\5\57\1\0\2\57" + "\1\0\4\57\44\0\1\372\54\0\1\373\55\0\1\374" + "\60\0\1\375\63\0\1\376\20\0\2\152\1\0\26\152" + "\1\377\27\152\2\161\1\0\26\161\1\u0100\27\161\32\0" + "\1\u0101\34\0\3\57\4\0\1\57\5\0\4\57\1\0" + "\3\57\1\u0102\7\57\1\0\2\57\1\u0102\2\57\1\0" + "\2\57\1\0\4\57\45\0\1\u0103\52\0\1\u0104\63\0" + "\1\u0105\43\0\1\u0106\63\0\1\u0107\25\0\1\u0107\2\0" + "\2\152\1\0\27\152\1\u0108\26\152\2\161\1\0\27\161" + "\1\u0109\26\161\33\0\1\u010a\62\0\1\u010b\56\0\1\u010c" + "\12\0\1\u010c\45\0\1\u010d\12\0\1\u010d\12\0\2\152" + "\1\0\30\152\1\u010e\25\152\2\161\1\0\30\161\1\u010f" + "\25\161\32\0\1\u0110\26\0\2\152\1\0\27\152\1\u0111" + + "\26\152\2\161\1\0\27\161\1\u0112\26\161\27\0\1\u0113" + "\31\0\2\152\1\0\24\152\1\u0114\31\152\2\161\1\0" + "\24\161\1\u0115\31\161"; + + /** + * The transition table of the DFA + */ + final private static int yytrans[] = yy_unpack(yy_packed); + + + /* error codes */ + final private static int YY_UNKNOWN_ERROR = 0; + // final private static int YY_ILLEGAL_STATE = 1; + final private static int YY_NO_MATCH = 2; + final private static int YY_PUSHBACK_2BIG = 3; + + /* error messages for the codes above */ + final private static String YY_ERROR_MSG[] = {"Unkown internal scanner error", //$NON-NLS-1$ + "Internal error: unknown state", //$NON-NLS-1$ + "Error: could not match input", //$NON-NLS-1$ + "Error: pushback value was too large" //$NON-NLS-1$ + }; + + /** + * YY_ATTRIBUTE[aState] contains the attributes of state + * <code>aState</code> + */ + private final static byte YY_ATTRIBUTE[] = {1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 9, 1, 9, 1, 1, 9, 9, 1, 1, 1, 1, 1, 1, 1, 1, 9, 1, 1, 1, 1, 9, 1, 1, 1, 1, 1, 1, 9, 1, 1, 9, 1, 1, 1, 1, 1, 1, 9, 1, 1, 1, 1, 9, 1, 1, 1, 1, 1, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 1, 1, 1, 1, 1, 1, 9, 9, 1, 9, 9, 1, 0, 1, 0, 1, 0, 0, 0, 9, 1, 1, 9, 9, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 9, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 9, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 9, 0, 0, 0, 1, 0, 0, 0, 9, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 9, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 9, 0, 0, 9, 9, 9, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 9, 0, 1, 9, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 9, 9, 1, 1, 0, 9, 9, 9, 1, 1, 0, 1, 1, 9, 1, 1}; + + /** the input device */ + private java.io.Reader yy_reader; + + /** the current state of the DFA */ + private int yy_state; + + /** the current lexical state */ + private int yy_lexical_state = YYINITIAL; + + /** + * this buffer contains the current text to be matched and is the source + * of the yytext() string + */ + private char yy_buffer[] = new char[16384]; + + /** the textposition at the last accepting state */ + private int yy_markedPos; + + /** the textposition at the last state to be included in yytext */ + private int yy_pushbackPos; + + /** the current text position in the buffer */ + private int yy_currentPos; + + /** startRead marks the beginning of the yytext() string in the buffer */ + private int yy_startRead; + + /** + * endRead marks the last character in the buffer, that has been read from + * input + */ + private int yy_endRead; + + /** number of newlines encountered up to the start of the matched text */ + private int yyline; + + /** the number of characters up to the start of the matched text */ + private int yychar; + + /** + * the number of characters from the last newline up to the start of the + * matched text + */ + // private int yycolumn; + /** + * yy_atBOL == true <=>the scanner is currently at the beginning of a line + */ + // private boolean yy_atBOL; + /** yy_atEOF == true <=>the scanner has returned a value for EOF */ + private boolean yy_atEOF; + + /** denotes if the user-EOF-code has already been executed */ + private boolean yy_eof_done; + + /* user code: */ + private int fTokenCount = 0; + + // required holders for white-space compacting + private boolean fShouldLoadBuffered = false; + private String fBufferedContext = null; + private int fBufferedStart = 1; + private int fBufferedLength = 0; + private ContextRegionContainer fBufferedEmbeddedContainer = null; + private String f_context = null; + + // state stack for handling embedded regions + private IntStack fStateStack = new IntStack(); + // a "hint" as to what an embedded region should be evaluated + String fEmbeddedHint = UNDEFINED; + // a "hint" as to what state to enter once an embedded region has + // been completed + int fEmbeddedPostState = YYINITIAL; + // the container used to create embedded regions + private ContextRegionContainer fEmbeddedContainer = null; + private static final String PROXY_CONTEXT = "PROXY_CONTEXT"; + + private String context = null; + private int start = 0; + private int textLength = 0; + private int length = 0; + + // offset for tracking position specific block tags + private int fOffset = 0; + + // the name of the current tag being opened + private String fCurrentTagName = null; + + // the list of tag name BlockMarkers + private List fBlockMarkers = new ArrayList(); + + // required to not seek text blocks on an end tag + private boolean fIsBlockingEnabled = false; + private boolean fIsCaseSensitiveBlocking = true; + + private XMLParserRegionFactory fRegionFactory = new XMLParserRegionFactory(); + + static final String rcsver = "$Id: XMLTokenizer.java,v 1.3 2004/11/11 09:05:07 david_williams Exp $";//$NON-NLS-1$ + + /** + * user method + */ + public final void addBlockMarker(BlockMarker marker) { + if (containsTagName(marker.getTagName())) + return; + fBlockMarkers.add(marker); + } + + /** + * user method + */ + public final void removeBlockMarker(BlockMarker marker) { + fBlockMarkers.remove(marker); + } + + /** + * user method + */ + public final void removeBlockMarker(String tagname) { + if (fBlockMarkers != null) { + Iterator blocks = fBlockMarkers.iterator(); + while (blocks.hasNext()) { + if (((BlockMarker) blocks.next()).getTagName().equals(tagname)) + blocks.remove(); + } + } + } + + /* user method */ + public final boolean isCaseSensitiveBlocking() { + return fIsCaseSensitiveBlocking; + } + + /* user method */ + public final void setCaseSensitiveBlocking(boolean newValue) { + fIsCaseSensitiveBlocking = newValue; + } + + /* user method */ + public boolean getBlockMarkerCaseSensitivity() { + return getBlockMarkerCaseSensitivity(fCurrentTagName); + } + + /* user method */ + public boolean getBlockMarkerCaseSensitivity(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while (iterator.hasNext()) { + BlockMarker marker = (BlockMarker) iterator.next(); + boolean casesensitive = marker.isCaseSensitive(); + if (casesensitive && marker.getTagName().equals(name)) + return casesensitive; + else if (!casesensitive && marker.getTagName().equalsIgnoreCase(name)) + return casesensitive; + } + return true; + } + + /* user method */ + public String getBlockMarkerContext() { + return getBlockMarkerContext(fCurrentTagName); + } + + /* user method */ + public String getBlockMarkerContext(String name) { + Iterator iterator = fBlockMarkers.iterator(); + while (iterator.hasNext()) { + BlockMarker marker = (BlockMarker) iterator.next(); + if (marker.getTagName().equals(name)) + return marker.getContext(); + } + return BLOCK_TEXT; + } + + /* user method */ + public List getBlockMarkers() { + return fBlockMarkers; + } + + /* user method */ + public final int getOffset() { + return fOffset + yychar; + } + + private final boolean isBlockMarker() { + return isBlockMarker(fCurrentTagName); + } + + private final boolean isBlockMarker(String tagName) { + if (!fIsBlockingEnabled) + return false; + return containsTagName(tagName); + } + + /** + * user method + */ + public final void beginBlockTagScan(String newTagName) { + beginBlockMarkerScan(newTagName, BLOCK_TEXT); + } + + /** + * user method + * + * Special tokenizer setup. Allows tokenization to be initiated at the + * start of a text block within a "newTagName" tag. + * + * Example: Tokenizer toker = new Tokenizer(); + * toker.setCaseSensitiveBlocking(false); toker.reset(new + * java.io.StringReader("afiuhqwkejhtasihgalkwhtq </scripter> </scr> + * </script>asgdasga")); toker.beginBlockMarkerScan("script", BLOCK_TEXT); + * toker.getRegions(); + * + * Returns: BLOCK_TEXT: 0-40 XML_END_TAG_OPEN: 41-42 XML_TAG_NAME: 43-48 + * XML_TAG_CLOSE: 49-49 XML_CONTENT: 50-57 + * + */ + public final void beginBlockMarkerScan(String newTagName, String blockcontext) { + yybegin(ST_BLOCK_TAG_SCAN); + fCurrentTagName = newTagName; + } + + /** + * Method doScan. + * + * Returns a context region for all of the text from the current position + * upto the end of input or to right *before* the first occurence of + * searchString + * + * @param searchString - + * target string to search for ex.: "-->", " </tagname" + * @param requireTailSeparator - + * whether the target must be immediately followed by + * whitespace or '>' + * @param context - + * the context of the scanned region if non-zero length + * @param exitState - + * the state to go to if the region was of non-zero length + * @param abortState - + * the state to go to if the searchString was found immediately + * @return String - the context found: the desired context on a non-zero + * length match, the abortContext on immediate success + * @throws IOException + */ + private final String doScan(String searchString, boolean requireTailSeparator, String searchContext, int exitState, int immediateFallbackState) throws IOException { + boolean stillSearching = true; + // Disable further block (probably) + fIsBlockingEnabled = false; + int searchStringLength = searchString.length(); + int n = 0; + char lastCheckChar; + int i; + boolean same = false; + while (stillSearching) { + n = 0; + // Ensure that enough data from the input exists to compare + // against the search String. + n = yy_advance(); + while (n != YYEOF && yy_currentPos < searchStringLength) + n = yy_advance(); + // If the input was too short or we've exhausted the input, stop + // immediately. + if (n == YYEOF) { + stillSearching = false; + } else { + same = true; + // Ensure that we've not encountered a complete block (<%%>) + // that was *shorter* than the closeTagString and + // thus found twice at current-targetLength [since the first + // scan would have come out this far anyway]. + // Check the characters in the target versus the last + // targetLength characters read from the buffer + // and see if it matches + + // safety check for array accesses (yy_currentPos is the + // *last* character we can check against) + if (yy_currentPos >= searchStringLength && yy_currentPos <= yy_buffer.length) { + for (i = 0; i < searchStringLength; i++) { + if (same && fIsCaseSensitiveBlocking) + same = yy_buffer[i + yy_currentPos - searchStringLength] == searchString.charAt(i); + else if (same && !fIsCaseSensitiveBlocking) + same = Character.toLowerCase(yy_buffer[i + yy_currentPos - searchStringLength]) == Character.toLowerCase(searchString.charAt(i)); + } + } + // safety check failed; no match is possible right now + else { + same = false; + } + if (same && requireTailSeparator && yy_currentPos < yy_buffer.length) { + // Additional check for close tags to ensure that + // targetString="</script" doesn't match + // "</scriptS" + lastCheckChar = yy_buffer[yy_currentPos]; + // Succeed on "</script>" and "</script " + if (lastCheckChar == '>' || Character.isWhitespace(lastCheckChar)) + stillSearching = false; + } else { + stillSearching = !same || (yy_currentPos < yy_startRead + searchStringLength); + } + } + } + if (n != YYEOF || same) { + // We've stopped short of the end or definitely found a match + yy_markedPos = yy_currentPos - searchStringLength; + yy_currentPos = yy_markedPos + 1; + // If the searchString occurs at the very beginning of what would + // have + // been a Block, resume scanning normally immediately + if (yy_markedPos == yy_startRead) { + yybegin(immediateFallbackState); + return primGetNextToken(); + } + } else { + // We ran through the rest of the input + yy_markedPos = yy_currentPos; + yy_currentPos++; + } + yybegin(exitState); + // If the ending occurs at the very beginning of what would have + // been a Block, resume scanning normally immediately + if (yy_markedPos == yy_startRead) + return primGetNextToken(); + return searchContext; + } + + /** + * user method + * + * A generic lookahead-like operation + */ + private final String doBlockScan(String target, String targetContext, int immediateFallbackState) throws IOException { + return doScan(target, false, targetContext, immediateFallbackState, immediateFallbackState); + } + + /** + * user method does a lookahead for the current tag name + */ + private final String doBlockTagScan() throws IOException { + fIsCaseSensitiveBlocking = getBlockMarkerCaseSensitivity(); + return doScan("</" + fCurrentTagName, true, getBlockMarkerContext(fCurrentTagName), YYINITIAL, YYINITIAL); + } + + /** + * user method + * + * Converts the raw context String returned by the primGetNextToken() + * method into a full ITextRegion by pulling in values for the current + * offset within the scanning text. + * + * Returns null when EOF is encountered and attaches intermittently + * discovered whitespace onto the end of useful regions. + * + * Note that this algorithm caches the token following the one being + * returned so that whitespace can be collapsed. + */ + public final ITextRegion getNextToken() throws IOException { + fEmbeddedContainer = null; + // load the starting non-whitespace token (assume that it is so) + if (fShouldLoadBuffered) { + if (fBufferedEmbeddedContainer != null) { + ITextRegion container = fBufferedEmbeddedContainer; + fBufferedEmbeddedContainer = null; + fShouldLoadBuffered = false; + return container; + } + context = fBufferedContext; + start = fBufferedStart; + textLength = length = fBufferedLength; + fShouldLoadBuffered = false; + } else { + context = primGetNextToken(); + if (context == PROXY_CONTEXT) { + return fEmbeddedContainer; + } else if (context == XML_TAG_NAME) { + if (containsTagName(yy_buffer, yy_startRead, yy_markedPos - yy_startRead)) + fCurrentTagName = yytext(); + else + fCurrentTagName = null; + } else if (context == XML_TAG_OPEN) { + fIsBlockingEnabled = true; + } else if (context == XML_END_TAG_OPEN) { + fIsBlockingEnabled = false; + } + start = yychar; + textLength = length = yylength(); + if (yy_atEOF) { + fTokenCount++; + return null; + } + } + // store the next token + f_context = primGetNextToken(); + if (f_context == PROXY_CONTEXT) { + fBufferedEmbeddedContainer = fEmbeddedContainer; + fShouldLoadBuffered = true; + } else if (f_context == XML_TAG_NAME) { + if (containsTagName(yy_buffer, yy_startRead, yy_markedPos - yy_startRead)) + fCurrentTagName = yytext(); + else + fCurrentTagName = null; + } else if (f_context == XML_TAG_OPEN) { + fIsBlockingEnabled = true; + } else if (f_context == XML_END_TAG_OPEN) { + fIsBlockingEnabled = false; + } + fBufferedContext = f_context; + fBufferedStart = yychar; + fBufferedLength = yylength(); + fShouldLoadBuffered = true; + if (fBufferedContext == WHITE_SPACE) { + fShouldLoadBuffered = false; + length += fBufferedLength; + } + if (context == null) { + // EOF + if (Debug.debugTokenizer) { + System.out.println(getClass().getName() + " discovered " + fTokenCount + " tokens."); //$NON-NLS-2$//$NON-NLS-1$ + } + return null; + } + fTokenCount++; + return fRegionFactory.createToken(context, start, textLength, length, null, fCurrentTagName); + } + + /* user method */ + public XMLTokenizer() { + super(); + } + + /* user method */ + public XMLTokenizer(char[] charArray) { + this(new CharArrayReader(charArray)); + } + + /* user method */ + public void reset(char[] charArray) { + reset(new CharArrayReader(charArray), 0); + } + + /* user method */ + public void reset(char[] charArray, int newOffset) { + reset(new CharArrayReader(charArray), newOffset); + } + + /* user method */ + public void reset(java.io.InputStream in) { + reset(new java.io.InputStreamReader(in), 0); + } + + /* user method */ + public void reset(java.io.InputStream in, int newOffset) { + reset(new java.io.InputStreamReader(in), newOffset); + } + + /* user method */ + public void reset(java.io.Reader in) { + reset(in, 0); + } + + /** + * user method * + * + * Reset internal counters and vars to "newly created" values, in the + * hopes that resetting a pre-existing tokenizer is faster than creating a + * new one. + * + * This method contains code blocks that were essentially duplicated from + * the <em>generated</em> output of this specification before this + * method was added. Those code blocks were under the above copyright. + */ + public void reset(java.io.Reader in, int newOffset) { + if (Debug.debugTokenizer) { + System.out.println("resetting tokenizer");//$NON-NLS-1$ + } + fOffset = newOffset; + + /* the input device */ + yy_reader = in; + + /* the current state of the DFA */ + yy_state = 0; + + /* the current lexical state */ + yy_lexical_state = YYINITIAL; + + /* + * this buffer contains the current text to be matched and is the + * source of the yytext() string + */ + java.util.Arrays.fill(yy_buffer, (char) 0); + + /* the textposition at the last accepting state */ + yy_markedPos = 0; + + /* the textposition at the last state to be included in yytext */ + yy_pushbackPos = 0; + + /* the current text position in the buffer */ + yy_currentPos = 0; + + /* startRead marks the beginning of the yytext() string in the buffer */ + yy_startRead = 0; + + /** + * endRead marks the last character in the buffer, that has been read + * from input + */ + yy_endRead = 0; + + /* number of newlines encountered up to the start of the matched text */ + yyline = 0; + + /* the number of characters up to the start of the matched text */ + yychar = 0; + + /* yy_atEOF == true <=> the scanner has returned a value for EOF */ + yy_atEOF = false; + + /* denotes if the user-EOF-code has already been executed */ + yy_eof_done = false; + + + /* user vars: */ + fTokenCount = 0; + + fShouldLoadBuffered = false; + fBufferedContext = null; + fBufferedStart = 1; + fBufferedLength = 0; + fStateStack = new IntStack(); + + context = null; + start = 0; + textLength = 0; + length = 0; + + fEmbeddedContainer = null; + } + + /** + * user method + * + * @see com.ibm.sed.parser.BlockTokenizer#newInstance() + */ + public BlockTokenizer newInstance() { + XMLTokenizer newInstance = new XMLTokenizer(); + // global tagmarkers can be shared; they have no state and + // are never destroyed (e.g. 'release') + for (int i = 0; i < fBlockMarkers.size(); i++) { + BlockMarker blockMarker = (BlockMarker) fBlockMarkers.get(i); + if (blockMarker.isGlobal()) + newInstance.addBlockMarker(blockMarker); + } + return newInstance; + } + + /* user method */ + private final String scanXMLCommentText() throws IOException { + // Scan for '-->' and return the text up to that point as + // XML_COMMENT_TEXT unless the string occurs IMMEDIATELY, in which + // case change to the ST_XML_COMMENT_END state and return the next + // context as usual. + return doScan("-->", false, XML_COMMENT_TEXT, ST_XML_COMMENT_END, ST_XML_COMMENT_END); + } + + + /** + * Creates a new scanner There is also a java.io.InputStream version of + * this constructor. + * + * @param in + * the java.io.Reader to read input from. + */ + public XMLTokenizer(java.io.Reader in) { + this.yy_reader = in; + } + + /** + * Creates a new scanner. There is also java.io.Reader version of this + * constructor. + * + * @param in + * the java.io.Inputstream to read input from. + */ + public XMLTokenizer(java.io.InputStream in) { + this(new java.io.InputStreamReader(in)); + } + + /** + * Unpacks the compressed DFA transition table. + * + * @param packed + * the packed transition table + * @return the unpacked transition table + */ + private static int[] yy_unpack(String packed) { + int[] trans = new int[9114]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 3174) { + int count = packed.charAt(i++); + int value = packed.charAt(i++); + value--; + do + trans[j++] = value; + while (--count > 0); + } + return trans; + } + + /** + * Unpacks the compressed character translation table. + * + * @param packed + * the packed character translation table + * @return the unpacked character translation table + */ + private static char[] yy_unpack_cmap(String packed) { + char[] map = new char[0x10000]; + int i = 0; /* index in packed string */ + int j = 0; /* index in unpacked array */ + while (i < 1372) { + int count = packed.charAt(i++); + char value = packed.charAt(i++); + do + map[j++] = value; + while (--count > 0); + } + return map; + } + + + /** + * Gets the next input character. + * + * @return the next character of the input stream, EOF if the end of the + * stream is reached. + * @exception IOException + * if any I/O-Error occurs + */ + private int yy_advance() throws java.io.IOException { + + /* standard case */ + if (yy_currentPos < yy_endRead) + return yy_buffer[yy_currentPos++]; + + /* if the eof is reached, we don't need to work hard */ + if (yy_atEOF) + return YYEOF; + + /* otherwise: need to refill the buffer */ + + /* first: make room (if you can) */ + if (yy_startRead > 0) { + System.arraycopy(yy_buffer, yy_startRead, yy_buffer, 0, yy_endRead - yy_startRead); + + /* translate stored positions */ + yy_endRead -= yy_startRead; + yy_currentPos -= yy_startRead; + yy_markedPos -= yy_startRead; + yy_pushbackPos -= yy_startRead; + yy_startRead = 0; + } + + /* is the buffer big enough? */ + if (yy_currentPos >= yy_buffer.length) { + /* if not: blow it up */ + char newBuffer[] = new char[yy_currentPos * 2]; + System.arraycopy(yy_buffer, 0, newBuffer, 0, yy_buffer.length); + yy_buffer = newBuffer; + } + + /* finally: fill the buffer with new input */ + int numRead = yy_reader.read(yy_buffer, yy_endRead, yy_buffer.length - yy_endRead); + + if (numRead == -1) + return YYEOF; + + yy_endRead += numRead; + + return yy_buffer[yy_currentPos++]; + } + + + /** + * Closes the input stream. + */ + final public void yyclose() throws java.io.IOException { + yy_atEOF = true; /* indicate end of file */ + yy_endRead = yy_startRead; /* invalidate buffer */ + yy_reader.close(); + } + + + /** + * Returns the current lexical state. + */ + final public int yystate() { + return yy_lexical_state; + } + + /** + * Enters a new lexical state + * + * @param newState + * the new lexical state + */ + final public void yybegin(int newState) { + yy_lexical_state = newState; + } + + + /** + * Returns the text matched by the current regular expression. + */ + final public String yytext() { + return new String(yy_buffer, yy_startRead, yy_markedPos - yy_startRead); + } + + /** + * Returns the length of the matched text region. + */ + final public int yylength() { + return yy_markedPos - yy_startRead; + } + + + /** + * Reports an error that occured while scanning - from the SED JFlex + * skeleton + * + * @param errorCode + * the code of the errormessage to display + */ + private void yy_ScanError(int errorCode) { + try { + Logger.log(Logger.ERROR, YY_ERROR_MSG[errorCode]); + } catch (ArrayIndexOutOfBoundsException e) { + Logger.log(Logger.ERROR, YY_ERROR_MSG[YY_UNKNOWN_ERROR]); + } + // DO NOT EXIT the VM on an error + // System.exit(1); + } + + + /** + * Pushes the specified amount of characters back into the input stream. + * + * They will be read again by then next call of the scanning method + * + * @param number + * the number of characters to be read again. This number must + * not be greater than yylength()! + */ + void yypushback(int number) { + if (number > yylength()) + yy_ScanError(YY_PUSHBACK_2BIG); + + yy_markedPos -= number; + } + + /** + * user method - skeleton.sed + */ + protected final boolean containsTagName(char[] markerTagName, int offset, int tagnameLength) { + for (int j = 0; j < fBlockMarkers.size(); j++) { + BlockMarker marker = (BlockMarker) fBlockMarkers.get(j); + if (marker.getTagName().length() == tagnameLength) { + boolean matchesSoFar = true; + for (int i = 0; i < tagnameLength && matchesSoFar; i++) { + if (marker.isCaseSensitive()) { + if (marker.getTagName().charAt(i) != markerTagName[i + offset]) + matchesSoFar = false; + } else { + if (Character.toLowerCase(marker.getTagName().charAt(i)) != Character.toLowerCase(markerTagName[i + offset])) + matchesSoFar = false; + } + } + if (matchesSoFar) + return true; + } + } + return false; + } + + /** + * user method - skeleton.sed + * + * Return ALL of the regions scannable within the remaining text Note: for + * verification use + */ + public final List getRegions() { + List tokens = new ArrayList(); + ITextRegion region = null; + try { + region = getNextToken(); + while (region != null) { + if (region != null) { + tokens.add(region); + } + region = getNextToken(); + } + } catch (StackOverflowError e) { + Logger.logException(getClass().getName() + ": input could not be tokenized correctly at position " + getOffset(), e);//$NON-NLS-1$ + throw e; + } catch (Exception e) { + // Since this is convenience method and NOT the recommended + // way of getting tokens, many errors are simply hidden + Logger.logException("Exception not handled retrieving regions: " + e.getLocalizedMessage(), e);//$NON-NLS-1$ + } + return tokens; + } + + /** + * user method - skeleton.sed + */ + private final void dump(String s) { + if (Debug.debugTokenizer) { + System.out.println(s + " (" + yychar + "-" + //$NON-NLS-2$//$NON-NLS-1$ + (yylength() + yychar) + "):\'" + //$NON-NLS-1$ + StringUtils.escape(yytext()) + "\'");//$NON-NLS-1$ + } + } + + /* user method - skeleton.sed */ + public final boolean isEOF() { + return yy_atEOF; + } + + /* user method - skeleton.sed */ + protected final boolean containsTagName(String markerTagName) { + Iterator blocks = fBlockMarkers.iterator(); + while (blocks.hasNext()) { + BlockMarker marker = (BlockMarker) blocks.next(); + if (marker.isCaseSensitive()) { + if (marker.getTagName().equals(markerTagName)) + return true; + } else { + if (marker.getTagName().equalsIgnoreCase(markerTagName)) + return true; + } + } + return false; + } + + /** + * Contains user EOF-code, which will be executed exactly once, when the + * end of file is reached + */ + private void yy_do_eof() { + if (!yy_eof_done) { + yy_eof_done = true; + // do nothing, this is the downstream parser's job + + } + } + + + /** + * Resumes scanning until the next regular expression is matched, the end + * of input is encountered or an I/O-Error occurs. + * + * @return the next token + * @exception IOException + * if any I/O-Error occurs + */ + public String primGetNextToken() throws java.io.IOException { + int yy_input; + int yy_action; + + + while (true) { + + yychar += yylength(); + + boolean yy_counted = false; + for (yy_currentPos = yy_startRead; yy_currentPos < yy_markedPos; yy_currentPos++) { + switch (yy_buffer[yy_currentPos]) { + case '\r' : + yyline++; + yy_counted = true; + break; + case '\n' : + if (yy_counted) + yy_counted = false; + else { + yyline++; + } + break; + default : + yy_counted = false; + } + } + + if (yy_counted) { + if (yy_advance() == '\n') + yyline--; + if (!yy_atEOF) + yy_currentPos--; + } + + yy_action = -1; + + yy_currentPos = yy_startRead = yy_markedPos; + + yy_state = yy_lexical_state; + + + yy_forAction : { + while (true) { + + yy_input = yy_advance(); + + if (yy_input == YYEOF) + break yy_forAction; + + int yy_next = yytrans[yy_rowMap[yy_state] + yycmap[yy_input]]; + if (yy_next == -1) + break yy_forAction; + yy_state = yy_next; + + int yy_attributes = YY_ATTRIBUTE[yy_state]; + if ((yy_attributes & 1) > 0) { + yy_action = yy_state; + yy_markedPos = yy_currentPos; + if ((yy_attributes & 8) > 0) + break yy_forAction; + } + + } + } + + + switch (yy_action) { + + case 274 : + case 275 : + case 276 : { + if (Debug.debugTokenizer) + dump("\nCDATA start");//$NON-NLS-1$ + fStateStack.push(yystate()); + yybegin(ST_CDATA_TEXT); + return XML_CDATA_OPEN; + } + case 278 : + break; + case 268 : { + if (Debug.debugTokenizer) + dump("element");//$NON-NLS-1$ + yybegin(ST_XML_ELEMENT_DECLARATION); + return XML_ELEMENT_DECLARATION; + } + case 279 : + break; + case 267 : { + if (Debug.debugTokenizer) + dump("attlist");//$NON-NLS-1$ + yybegin(ST_XML_ATTLIST_DECLARATION); + return XML_ATTLIST_DECLARATION; + } + case 280 : + break; + case 266 : { + if (Debug.debugTokenizer) + dump("doctype");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_DECLARATION); + return XML_DOCTYPE_DECLARATION; + } + case 281 : + break; + case 262 : { + if (Debug.debugTokenizer) + dump("doctype external id");//$NON-NLS-1$ + fEmbeddedHint = XML_DOCTYPE_EXTERNAL_ID_PUBREF; + yybegin(ST_XML_DOCTYPE_ID_PUBLIC); + return XML_DOCTYPE_EXTERNAL_ID_PUBLIC; + } + case 282 : + break; + case 261 : { + if (Debug.debugTokenizer) + dump("doctype external id");//$NON-NLS-1$ + fEmbeddedHint = XML_DOCTYPE_EXTERNAL_ID_SYSREF; + yybegin(ST_XML_DOCTYPE_ID_SYSTEM); + return XML_DOCTYPE_EXTERNAL_ID_SYSTEM; + } + case 283 : + break; + case 257 : { + if (Debug.debugTokenizer) + dump("DHTML processing instruction target");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_DHTML_ATTRIBUTE_NAME); + return XML_TAG_NAME; + } + case 284 : + break; + case 234 : { + if (Debug.debugTokenizer) + dump("\nCharRef");//$NON-NLS-1$ + return XML_CHAR_REFERENCE; + } + case 285 : + break; + case 231 : { + if (Debug.debugTokenizer) + dump("\ncomment start");//$NON-NLS-1$ + fEmbeddedHint = XML_COMMENT_TEXT; + fEmbeddedPostState = ST_XML_COMMENT; + yybegin(ST_XML_COMMENT); + return XML_COMMENT_OPEN; + } + case 286 : + break; + case 213 : { + if (Debug.debugTokenizer) + dump("XML processing instruction target");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_NAME; + } + case 287 : + break; + case 212 : { + if (Debug.debugTokenizer) + dump("comment end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_COMMENT_CLOSE; + } + case 288 : + break; + case 211 : { + if (Debug.debugTokenizer) + dump("CDATA end");//$NON-NLS-1$ + yybegin(fStateStack.pop()); + return XML_CDATA_CLOSE; + } + case 289 : + break; + case 210 : { + if (Debug.debugTokenizer) + dump("\nPEReference");//$NON-NLS-1$ + return XML_PE_REFERENCE; + } + case 290 : + break; + case 207 : { + if (Debug.debugTokenizer) + dump("\nEntityRef");//$NON-NLS-1$ + return XML_ENTITY_REFERENCE; + } + case 291 : + break; + case 158 : + case 172 : + case 180 : { + return XML_DOCTYPE_INTERNAL_SUBSET; + } + case 292 : + break; + case 146 : { + yybegin(YYINITIAL); + fEmbeddedHint = UNDEFINED; + if (Debug.debugTokenizer) + dump("empty tag close");//$NON-NLS-1$ + return XML_EMPTY_TAG_CLOSE; + } + case 293 : + break; + case 131 : { + if (Debug.debugTokenizer) + dump("XML processing instruction end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 294 : + break; + case 130 : { + // ended with nothing inside + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 295 : + break; + case 127 : { + if (Debug.debugTokenizer) + dump("processing instruction end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 296 : + break; + case 119 : { + fStateStack.push(yystate()); + if (Debug.debugTokenizer) + dump("\ndeclaration start");//$NON-NLS-1$ + yybegin(ST_XML_DECLARATION); + return XML_DECLARATION_OPEN; + } + case 297 : + break; + case 118 : { + if (Debug.debugTokenizer) + dump("\nprocessing instruction start");//$NON-NLS-1$ + yybegin(ST_PI); + return XML_PI_OPEN; + } + case 298 : + break; + case 62 : { + if (Debug.debugTokenizer) + dump("DHTML processing instruction end");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + yybegin(YYINITIAL); + return XML_PI_CLOSE; + } + case 299 : + break; + case 56 : + case 58 : + case 59 : + case 60 : + case 135 : { + if (Debug.debugTokenizer) + dump("XML processing instruction attribute value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_PI_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; + } + case 300 : + break; + case 55 : { + if (Debug.debugTokenizer) + dump("XML processing instruction '='");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_PI_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; + } + case 301 : + break; + case 54 : { + if (Debug.debugTokenizer) + dump("XML processing instruction attribute name");//$NON-NLS-1$ + yybegin(ST_XML_PI_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; + } + case 302 : + break; + case 50 : + case 51 : + case 52 : { + // block scan until close is found + return doScan("?>", false, XML_PI_CONTENT, ST_XML_PI_TAG_CLOSE, ST_XML_PI_TAG_CLOSE); + } + case 303 : + break; + case 49 : { + yybegin(ST_PI_CONTENT); + return WHITE_SPACE; + } + case 304 : + break; + case 46 : + case 47 : + case 48 : + case 128 : + case 129 : + case 214 : + case 236 : + case 248 : { + if (Debug.debugTokenizer) + dump("processing instruction target");//$NON-NLS-1$ + fEmbeddedHint = XML_CONTENT; + yybegin(ST_PI_WS); + return XML_TAG_NAME; + } + case 305 : + break; + case 41 : + case 42 : { + if (Debug.debugTokenizer) + dump("comment content");//$NON-NLS-1$ + return scanXMLCommentText(); + } + case 306 : + break; + case 40 : { + if (Debug.debugTokenizer) + dump("LINE FEED");//$NON-NLS-1$ + return WHITE_SPACE; + } + case 307 : + break; + case 0 : + case 31 : + case 121 : + case 123 : + case 205 : + case 206 : + case 233 : { + if (Debug.debugTokenizer) + dump("\nXML content");//$NON-NLS-1$ + return XML_CONTENT; + } + case 308 : + break; + case 5 : + case 8 : + case 9 : + case 10 : + case 12 : + case 13 : + case 14 : + case 15 : + case 17 : + case 18 : + case 19 : + case 20 : + case 21 : + case 22 : + case 23 : + case 24 : + case 25 : + case 26 : + case 28 : + case 45 : { + if (Debug.debugTokenizer) + dump("white space");//$NON-NLS-1$ + return WHITE_SPACE; + } + case 309 : + break; + case 16 : + case 70 : { + if (Debug.debugTokenizer) + dump("inappropriate tag name");//$NON-NLS-1$ + yybegin(YYINITIAL); + return XML_CONTENT; + } + case 310 : + break; + case 27 : + case 105 : + case 106 : + case 191 : + case 226 : + case 244 : + case 254 : + case 263 : + case 269 : + case 272 : { + if (Debug.debugTokenizer) + dump("elementdecl contentspec");//$NON-NLS-1$ + return XML_ELEMENT_DECL_CONTENT; + } + case 311 : + break; + case 29 : + case 112 : + case 113 : + case 202 : + case 230 : + case 246 : + case 255 : + case 264 : + case 270 : + case 273 : { + if (Debug.debugTokenizer) + dump("attlist contentspec");//$NON-NLS-1$ + return XML_ATTLIST_DECL_CONTENT; + } + case 312 : + break; + case 32 : + case 71 : + case 82 : { + if (Debug.debugTokenizer) + dump("\nstart tag open");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_TAG_NAME); + return XML_TAG_OPEN; + } + case 313 : + break; + case 33 : + case 34 : + case 37 : + case 38 : + case 39 : + case 43 : + case 44 : + case 53 : + case 57 : + case 61 : + case 63 : + case 67 : + case 73 : + case 79 : + case 84 : + case 85 : + case 86 : + case 87 : + case 89 : + case 90 : + case 92 : + case 97 : + case 102 : + case 109 : { + if (Debug.debugTokenizer) + System.out.println("!!!unexpected!!!: \"" + yytext() + "\":" + //$NON-NLS-2$//$NON-NLS-1$ + yychar + "-" + (yychar + yylength()));//$NON-NLS-1$ + return UNDEFINED; + } + case 314 : + break; + case 35 : + case 36 : { + if (Debug.debugTokenizer) + dump("CDATA text");//$NON-NLS-1$ + fEmbeddedHint = XML_CDATA_TEXT; + fEmbeddedPostState = ST_CDATA_TEXT; + String blockContext = doBlockScan("]]>", XML_CDATA_TEXT, ST_CDATA_END);//$NON-NLS-1$ + if (blockContext == XML_CDATA_TEXT) + yybegin(ST_CDATA_END); + return blockContext; + } + case 315 : + break; + case 64 : { + if (Debug.debugTokenizer) + dump("DHTML processing instruction attribute name");//$NON-NLS-1$ + yybegin(ST_DHTML_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; + } + case 316 : + break; + case 65 : { + if (Debug.debugTokenizer) + dump("DHTML processing instruction '='");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_DHTML_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; + } + case 317 : + break; + case 66 : + case 68 : + case 69 : + case 143 : { + if (Debug.debugTokenizer) + dump("DHTML processing instruction attribute value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_DHTML_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; + } + case 318 : + break; + case 72 : { + if (Debug.debugTokenizer) + dump("tag close");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + if (isBlockMarker()) { + fEmbeddedHint = getBlockMarkerContext(); + fEmbeddedPostState = ST_BLOCK_TAG_SCAN; + yybegin(ST_BLOCK_TAG_SCAN); + } else + yybegin(YYINITIAL); + return XML_TAG_CLOSE; + } + case 319 : + break; + case 74 : + case 75 : { + if (Debug.debugTokenizer) + dump("tag name");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return XML_TAG_NAME; + } + case 320 : + break; + case 76 : { + if (Debug.debugTokenizer) + dump("attr name");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_EQUALS); + return XML_TAG_ATTRIBUTE_NAME; + } + case 321 : + break; + case 77 : { + if (Debug.debugTokenizer) + dump("equals");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_VALUE; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_ATTRIBUTE_VALUE); + return XML_TAG_ATTRIBUTE_EQUALS; + } + case 322 : + break; + case 78 : + case 80 : + case 81 : + case 150 : { + if (Debug.debugTokenizer) + dump("attr value");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_ATTRIBUTE_NAME; + fEmbeddedPostState = ST_XML_EQUALS; + yybegin(ST_XML_ATTRIBUTE_NAME); + return XML_TAG_ATTRIBUTE_VALUE; + } + case 323 : + break; + case 83 : { + if (Debug.debugTokenizer) + dump("declaration end");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if (fStateStack.peek() != YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; + } + case 324 : + break; + case 88 : { + if (Debug.debugTokenizer) + dump("doctype type");//$NON-NLS-1$ + yybegin(ST_XML_DOCTYPE_EXTERNAL_ID); + return XML_DOCTYPE_NAME; + } + case 325 : + break; + case 91 : + case 93 : + case 94 : + case 95 : + case 164 : + case 165 : + case 168 : + case 169 : + case 221 : { + if (Debug.debugTokenizer) + dump("doctype public reference");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_DOCTYPE_ID_SYSTEM); + return XML_DOCTYPE_EXTERNAL_ID_PUBREF; + } + case 326 : + break; + case 96 : + case 98 : + case 99 : + case 100 : + case 176 : { + if (Debug.debugTokenizer) + dump("doctype system reference");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_DECLARATION_CLOSE); + return XML_DOCTYPE_EXTERNAL_ID_SYSREF; + } + case 327 : + break; + case 101 : + case 103 : + case 104 : + case 184 : + case 185 : + case 188 : + case 189 : + case 224 : { + if (Debug.debugTokenizer) + dump("elementdecl name");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_ELEMENT_DECLARATION_CONTENT); + return XML_ELEMENT_DECL_NAME; + } + case 328 : + break; + case 107 : { + if (Debug.debugTokenizer) + dump("elementdecl close");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if (fStateStack.peek() != YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; + } + case 329 : + break; + case 108 : + case 110 : + case 111 : + case 195 : + case 196 : + case 199 : + case 200 : + case 228 : { + if (Debug.debugTokenizer) + dump("attlist name");//$NON-NLS-1$ + fEmbeddedHint = UNDEFINED; + fEmbeddedPostState = YYINITIAL; + yybegin(ST_XML_ATTLIST_DECLARATION_CONTENT); + return XML_ATTLIST_DECL_NAME; + } + case 330 : + break; + case 114 : { + if (Debug.debugTokenizer) + dump("attlist close");//$NON-NLS-1$ + if (Debug.debugTokenizer) { + if (fStateStack.peek() != YYINITIAL) + System.out.println("end embedded region");//$NON-NLS-1$ + } + yybegin(fStateStack.pop()); + return XML_DECLARATION_CLOSE; + } + case 331 : + break; + case 117 : { + if (Debug.debugTokenizer) + dump("\nend tag open");//$NON-NLS-1$ + fEmbeddedHint = XML_TAG_NAME; + fEmbeddedPostState = ST_XML_ATTRIBUTE_NAME; + yybegin(ST_XML_TAG_NAME); + return XML_END_TAG_OPEN; + } + case 332 : + break; + case 115 : + case 116 : { + return doBlockTagScan(); + } + case 333 : + break; + default : + if (yy_input == YYEOF && yy_startRead == yy_currentPos) { + yy_atEOF = true; + yy_do_eof(); + return null; + } else { + yy_ScanError(YY_NO_MATCH); + } + } + } + } + + +}
\ No newline at end of file diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/AttributeEqualsRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/AttributeEqualsRegion.java new file mode 100644 index 0000000000..2a577768b8 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/AttributeEqualsRegion.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + + +public class AttributeEqualsRegion implements ITextRegion { + static private final byte fTextLength = 1; + static private final String fType = XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS; + private short fLength; + private int fStart; + + + public AttributeEqualsRegion() { + super(); + } + + public AttributeEqualsRegion(int start, int textLength, int length) { + this(); + fStart = start; + fLength = (short) length; + } + + + public void adjustLengthWith(int i) { + fLength += i; + + } + + public void adjustStart(int i) { + fStart += i; + + } + + public void adjustTextLength(int i) { + // not supported + + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + fLength = (short) region.getLength(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + return fLength; + } + + public int getStart() { + return fStart; + } + + public int getTextEnd() { + return fStart + fTextLength; + } + + public int getTextLength() { + return fTextLength; + } + + public String getType() { + return fType; + } + + public String toString() { + return RegionToStringUtil.toString(this); + } + + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + // can never be updated + return null; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/AttributeNameRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/AttributeNameRegion.java new file mode 100644 index 0000000000..fd678bd02c --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/AttributeNameRegion.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.util.Debug; +import org.eclipse.wst.sse.core.util.Utilities; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +public class AttributeNameRegion implements ITextRegion { + // specify correct type + static private final String fType = XMLRegionContext.XML_TAG_ATTRIBUTE_NAME; + private int fLength; + private int fStart; + private int fTextLength; + + public AttributeNameRegion() { + super(); + } + + public AttributeNameRegion(int start, int textLength, int length) { + this(); + fStart = start; + fTextLength = textLength; + fLength = length; + } + + public void adjustLengthWith(int i) { + fLength += i; + + } + + public void adjustStart(int i) { + fStart += i; + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + fTextLength += 1; + + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + fLength = region.getLength(); + fTextLength = region.getTextLength(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + return fLength; + } + + public int getStart() { + return fStart; + } + + public int getTextEnd() { + return fStart + fTextLength; + } + + public int getTextLength() { + return fTextLength; + } + + public String getType() { + return fType; + } + + public String toString() { + return RegionToStringUtil.toString(this); + } + + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + RegionChangedEvent result = null; + // if the region is an easy type (e.g. attribute value), + // and the requested changes are all + // alphanumeric, then make the change here locally. + // (This can obviously be made more sophisticated as the need arises, + // but should + // always follow this pattern.) + if (Debug.debugStructuredDocument) { + System.out.println("\t\tContextRegion::updateModel"); //$NON-NLS-1$ + System.out.println("\t\t\tregion type is " + fType); //$NON-NLS-1$ + } + boolean canHandle = false; + // note: we'll always handle deletes from these + // regions ... if its already that region, + // deleting something from it won't change its + // type. (remember, the calling program needs + // to insure we are not called, if not all contained + // on one region. + if ((changes == null) || (changes.length() == 0)) { + // delete case + // We can not do the quick delete, if + // if all the text in a region is to be deleted. + // Or, if the delete starts in the white space region. + // In these cases, a reparse is needed. + // Minor note, we use textEnd-start since it always + // less than or equal to end-start. This might + // cause us to miss a few cases we could have handled, + // but will prevent us from trying to handle funning cases + // involving whitespace. + if ((fStart >= getTextEnd()) || (Math.abs(lengthToReplace) >= getTextEnd() - getStart())) { + canHandle = false; + } else { + canHandle = true; + } + } else { + if ((RegionUpdateRule.canHandleAsWhiteSpace(this, parent, changes, requestStart, lengthToReplace)) || RegionUpdateRule.canHandleAsLetterOrDigit(this, parent, changes, requestStart, lengthToReplace)) { + canHandle = true; + } else { + canHandle = false; + } + + } + if (canHandle) { + // at this point, we still have the old region. We won't create a + // new instance, we'll just update the one we have, by changing + // its end postion, + // The parent flatnode, upon return, has responsibility + // for updating sibling regions. + // and in turn, the structuredDocument itself has responsibility + // for + // updating the text store and down stream flatnodes. + if (Debug.debugStructuredDocument) { + System.out.println("change handled by region"); //$NON-NLS-1$ + } + int lengthDifference = Utilities.calculateLengthDifference(changes, lengthToReplace); + // Note: we adjust both end and text end, because for any change + // that is in only the trailing whitespace region, we should not + // do a quick change, + // so 'canHandle' should have been false for those case. + // TO_DO_FUTURE: cache value of canHandleAsWhiteSpace from above + // If we are handling as whitespace, there is no need to increase + // the text length, only + // the total length is changing. + if (!RegionUpdateRule.canHandleAsWhiteSpace(this, parent, changes, requestStart, lengthToReplace)) { + fTextLength += lengthDifference; + } + fLength += lengthDifference; + result = new RegionChangedEvent(parent.getParentDocument(), requester, parent, this, changes, requestStart, lengthToReplace); + } + + return result; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/AttributeValueRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/AttributeValueRegion.java new file mode 100644 index 0000000000..03eb4634da --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/AttributeValueRegion.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.util.Debug; +import org.eclipse.wst.sse.core.util.Utilities; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +public class AttributeValueRegion implements ITextRegion { + // specify correct type + static private final String fType = XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE; + private int fLength; + private int fStart; + private int fTextLength; + + public AttributeValueRegion() { + super(); + } + + public AttributeValueRegion(int start, int textLength, int length) { + this(); + fStart = start; + fTextLength = textLength; + fLength = length; + } + + public void adjustLengthWith(int i) { + fLength += i; + } + + public void adjustStart(int i) { + fStart += i; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + fTextLength += 1; + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + fLength = region.getLength(); + fTextLength = region.getTextLength(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + return fLength; + } + + public int getStart() { + return fStart; + } + + public int getTextEnd() { + return fStart + fTextLength; + } + + public int getTextLength() { + return fTextLength; + } + + public String getType() { + return fType; + } + + public String toString() { + return RegionToStringUtil.toString(this); + } + + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + RegionChangedEvent result = null; + // if the region is an easy type (e.g. attribute value), + // and the requested changes are all + // alphanumeric, then make the change here locally. + // (This can obviously be made more sophisticated as the need arises, + // but should + // always follow this pattern.) + if (Debug.debugStructuredDocument) { + System.out.println("\t\tContextRegion::updateModel"); //$NON-NLS-1$ + System.out.println("\t\t\tregion type is " + fType); //$NON-NLS-1$ + } + boolean canHandle = false; + // note: we'll always handle deletes from these + // regions ... if its already that region, + // deleting something from it won't change its + // type. (remember, the calling program needs + // to insure we are not called, if not all contained + // on one region. + if ((changes == null) || (changes.length() == 0)) { + // delete case + // We can not do the quick delete, if + // if all the text in a region is to be deleted. + // Or, if the delete starts in the white space region. + // In these cases, a reparse is needed. + // Minor note, we use textEnd-start since it always + // less than or equal to end-start. This might + // cause us to miss a few cases we could have handled, + // but will prevent us from trying to handle funning cases + // involving whitespace. + if ((fStart >= getTextEnd()) || (Math.abs(lengthToReplace) >= getTextEnd() - getStart())) { + canHandle = false; + } else { + canHandle = true; + } + } else { + if ((RegionUpdateRule.canHandleAsWhiteSpace(this, parent, changes, requestStart, lengthToReplace)) || RegionUpdateRule.canHandleAsLetterOrDigit(this, parent, changes, requestStart, lengthToReplace)) { + canHandle = true; + } else { + canHandle = false; + } + } + if (canHandle) { + // at this point, we still have the old region. We won't create a + // new instance, we'll just update the one we have, by changing + // its end postion, + // The parent flatnode, upon return, has responsibility + // for updating sibling regions. + // and in turn, the structuredDocument itself has responsibility + // for + // updating the text store and down stream flatnodes. + if (Debug.debugStructuredDocument) { + System.out.println("change handled by region"); //$NON-NLS-1$ + } + int lengthDifference = Utilities.calculateLengthDifference(changes, lengthToReplace); + // Note: we adjust both end and text end, because for any change + // that is in only the trailing whitespace region, we should not + // do a quick change, + // so 'canHandle' should have been false for those case. + // TO_DO_FUTURE: cache value of canHandleAsWhiteSpace from above + // If we are handling as whitespace, there is no need to increase + // the text length, only + // the total length is changing. + if (!RegionUpdateRule.canHandleAsWhiteSpace(this, parent, changes, requestStart, lengthToReplace)) { + fTextLength += lengthDifference; + } + // update length (and end) after above check for white space, + // since + // it looks to determine if at end of region. + fLength += lengthDifference; + result = new RegionChangedEvent(parent.getParentDocument(), requester, parent, this, changes, requestStart, lengthToReplace); + } + return result; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/BlockTextRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/BlockTextRegion.java new file mode 100644 index 0000000000..b382257d5a --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/BlockTextRegion.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.internal.parser.ForeignRegion; + + + +public class BlockTextRegion extends ForeignRegion { + + /** + * BlockTextRegion constructor comment. + */ + public BlockTextRegion() { + super(); + } + + public BlockTextRegion(String newContext, int newStart, int newTextLength, int newLength) { + super(newContext, newStart, newTextLength, newLength); + } + + public BlockTextRegion(String newContext, int newStart, int newTextLength, int newLength, String newLanguage) { + super(newContext, newStart, newTextLength, newLength, newLanguage); + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/GenericTemplateRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/GenericTemplateRegion.java new file mode 100644 index 0000000000..291a3411e7 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/GenericTemplateRegion.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +/** + * + * This class is not intended to be used, its just present to server as a + * generic starting point for adding new specific region types. + */ + +public class GenericTemplateRegion implements ITextRegion { + // specify correct type + static private final String fType = XMLRegionContext.UNDEFINED; + private int fLength; + private int fStart; + private int fTextLength; + + + public GenericTemplateRegion() { + super(); + } + + public GenericTemplateRegion(int start, int textLength, int length) { + this(); + fStart = start; + fTextLength = textLength; + fLength = length; + } + + public void adjustLengthWith(int i) { + fLength += i; + + } + + public void adjustStart(int i) { + fStart += i; + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + fTextLength += 1; + + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + fLength = region.getLength(); + fTextLength = region.getTextLength(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + return fLength; + } + + public int getStart() { + return fStart; + } + + public int getTextEnd() { + return fStart + fTextLength; + } + + public int getTextLength() { + return fTextLength; + } + + public String getType() { + return fType; + } + + public String toString() { + return RegionToStringUtil.toString(this); + } + + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + // can never be updated + return null; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/RegionToStringUtil.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/RegionToStringUtil.java new file mode 100644 index 0000000000..9693beb913 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/RegionToStringUtil.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.text.ITextRegion; + + +public class RegionToStringUtil { + static public String toString(ITextRegion region) { + String className = region.getClass().getName(); + String shortClassName = className.substring(className.lastIndexOf(".") + 1); //$NON-NLS-1$ + // ==> // String resultText = null; + String result = shortClassName + "--> " + region.getType() + ": " + region.getStart() + "-" + region.getTextEnd() + (region.getTextEnd() != region.getEnd() ? ("/" + region.getEnd()) : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ + // NOTE: if the document held by any region has been updated and the + // region offsets have not + // yet been updated, the output from this method invalid. + //return com.ibm.sed.util.StringUtils.escape("('"+(getFirstRegion() + // == null || document == null? "" : + // getText(getFirstRegion()))+"'"+getStart()+" - + // "+getEnd()+"'"+(getClose() == null || document == null || + // getRegions().size()<2 ? "" : getText(getClose()))+"') + // "+getRegions()); + return result; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/RegionUpdateRule.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/RegionUpdateRule.java new file mode 100644 index 0000000000..8692688ea3 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/RegionUpdateRule.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.util.Debug; +import org.eclipse.wst.sse.core.util.Utilities; + + +/** + * + * This is a utility class to centralize 'region' update. Note: care must be + * taken that is is not used for StructuredDocumentRegions, or container + * regions, its only for "token regions" + * + */ +public class RegionUpdateRule { + + static public boolean allLetterOrDigit(String changes) { + boolean result = true; + for (int i = 0; i < changes.length(); i++) { + // TO_DO_FUTURE: check that a Java Letter or Digit is + // the same thing as an XML letter or digit + if (!(Character.isLetterOrDigit(changes.charAt(i)))) { + result = false; + break; + } + } + return result; + } + + static public boolean allWhiteSpace(String changes) { + boolean result = true; + for (int i = 0; i < changes.length(); i++) { + if (!Character.isWhitespace(changes.charAt(i))) { + result = false; + break; + } + } + return result; + } + + static public boolean canHandleAsLetterOrDigit(ITextRegion region, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + if (parent == null) + return canHandleAsLetterOrDigit(region, changes, requestStart, lengthToReplace); + boolean result = false; + // Make sure we are in a non-white space area + if ((requestStart <= (parent.getTextEndOffset(region))) && (allLetterOrDigit(changes))) { + result = true; + } + return result; + } + + static public boolean canHandleAsLetterOrDigit(ITextRegion region, String changes, int requestStart, int lengthToReplace) { + boolean result = false; + // Make sure we are in a non-white space area + if ((requestStart <= (region.getTextEnd())) && (allLetterOrDigit(changes))) { + result = true; + } + return result; + } + + static public boolean canHandleAsWhiteSpace(ITextRegion region, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + // we don't explect a null parent, but just in case! + // (in which case, we must be dealing with regions that are + // structuredDocumentRegions). + if (parent == null) + return canHandleAsWhiteSpace(region, changes, requestStart, lengthToReplace); + boolean result = false; + // if we are in the "white space" area of a region, then + // we don't want to handle, a reparse is needed. + // the white space region is consider anywhere that would + // leave whitespace between this character and the text part. + // and of course, we can insert whitespace in whitespace region + // + // if there is no whitespace in this region, no need to look further + if (region.getEnd() > region.getTextEnd()) { + // no need to add one to end of text, as we used to, since we + // change definition of length to equate to offset plus one. + if (requestStart > parent.getTextEndOffset(region)) { + // ok, we are in the whitespace region, so we can't handle, + // unless + // we are just inserting whitespace. + if (allWhiteSpace(changes)) { + result = true; + } else { + result = false; + } + } + } + return result; + } + + static public boolean canHandleAsWhiteSpace(ITextRegion region, String changes, int requestStart, int lengthToReplace) { + boolean result = false; + // if we are in the "white space" area of a region, then + // we don't want to handle, a reparse is needed. + // the white space region is consider anywhere that would + // leave whitespace between this character and the text part. + // and of course, we can insert whitespace in whitespace region + // + // if there is no whitespace in this region, no need to look further + if (region.getEnd() > region.getTextEnd()) { + // no need to add one to end of text, as we used to, since we + // change definition of length to equate to offset plus one. + if (requestStart > region.getTextEnd()) { + // ok, we are in the whitespace region, so we can't handle, + // unless + // we are just inserting whitespace. + if (allWhiteSpace(changes)) { + result = true; + } else { + result = false; + } + } + } + return result; + } + + // need an adjust text length API before this can be used + static public StructuredDocumentEvent updateModel(ITextRegion region, Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + RegionChangedEvent result = null; + // if the region is an easy type (e.g. attribute value), + // and the requested changes are all + // alphanumeric, then make the change here locally. + // (This can obviously be made more sophisticated as the need arises, + // but should + // always follow this pattern.) + if (Debug.debugStructuredDocument) { + System.out.println("\t\tContextRegion::updateModel"); //$NON-NLS-1$ + System.out.println("\t\t\tregion type is " + region.getType()); //$NON-NLS-1$ + } + boolean canHandle = false; + // note: we'll always handle deletes from these + // regions ... if its already that region, + // deleting something from it won't change its + // type. (remember, the calling program needs + // to insure we are not called, if not all contained + // on one region. + if ((changes == null) || (changes.length() == 0)) { + // delete case + // We can not do the quick delete, if + // if all the text in a region is to be deleted. + // Or, if the delete starts in the white space region. + // In these cases, a reparse is needed. + // Minor note, we use textEnd-start since it always + // less than or equal to end-start. This might + // cause us to miss a few cases we could have handled, + // but will prevent us from trying to handle funning cases + // involving whitespace. + if ((region.getStart() >= region.getTextEnd()) || (Math.abs(lengthToReplace) >= region.getTextEnd() - region.getStart())) { + canHandle = false; + } else { + canHandle = true; + } + } else { + if ((RegionUpdateRule.canHandleAsWhiteSpace(region, parent, changes, requestStart, lengthToReplace)) || RegionUpdateRule.canHandleAsLetterOrDigit(region, parent, changes, requestStart, lengthToReplace)) { + canHandle = true; + } else { + canHandle = false; + } + } + if (canHandle) { + // at this point, we still have the old region. We won't create a + // new instance, we'll just update the one we have, by changing + // its end postion, + // The parent flatnode, upon return, has responsibility + // for updating sibling regions. + // and in turn, the structuredDocument itself has responsibility + // for + // updating the text store and down stream flatnodes. + if (Debug.debugStructuredDocument) { + System.out.println("change handled by region"); //$NON-NLS-1$ + } + int lengthDifference = Utilities.calculateLengthDifference(changes, lengthToReplace); + // Note: we adjust both end and text end, because for any change + // that is in only the trailing whitespace region, we should not + // do a quick change, + // so 'canHandle' should have been false for those case. + region.adjustLengthWith(lengthDifference); + // TO_DO_FUTURE: cache value of canHandleAsWhiteSpace from above + // If we are handling as whitespace, there is no need to increase + // the text length, only + // the total length is changing. + if (!RegionUpdateRule.canHandleAsWhiteSpace(region, parent, changes, region.getStart(), lengthToReplace)) { + // region.adjustTextLength(lengthDifference); + } + result = new RegionChangedEvent(parent.getParentDocument(), requester, parent, region, changes, requestStart, lengthToReplace); + } + return result; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/TagCloseRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/TagCloseRegion.java new file mode 100644 index 0000000000..87044ddc5c --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/TagCloseRegion.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.exceptions.SourceEditingRuntimeException; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +public class TagCloseRegion implements ITextRegion { + static private final byte fLength = 1; + static private final byte fTextLength = 1; + static private final String fType = XMLRegionContext.XML_TAG_CLOSE; + private int fStart; + + + public TagCloseRegion() { + super(); + } + + public TagCloseRegion(int start) { + this(); + fStart = start; + } + + public void adjustLengthWith(int i) { + throw new SourceEditingRuntimeException("invalid for this region type"); //$NON-NLS-1$ + + } + + public void adjustStart(int i) { + fStart += i; + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + // not supported + + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + return fLength; + } + + public int getStart() { + return fStart; + } + + public int getTextEnd() { + return fStart + fTextLength; + } + + public int getTextLength() { + return fTextLength; + } + + public String getType() { + return fType; + } + + public String toString() { + return RegionToStringUtil.toString(this); + } + + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + // can never be updated + return null; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/TagNameRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/TagNameRegion.java new file mode 100644 index 0000000000..f01e5b017a --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/TagNameRegion.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +public class TagNameRegion implements ITextRegion { + static private final String fType = XMLRegionContext.XML_TAG_NAME; + private short fLength; + private int fStart; + private short fTextLength; + + + public TagNameRegion() { + super(); + } + + public TagNameRegion(int start, int textLength, int length) { + this(); + fStart = start; + fTextLength = (short) textLength; + fLength = (short) length; + } + + public void adjustLengthWith(int i) { + fLength += i; + + } + + public void adjustStart(int i) { + fStart += i; + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + fTextLength += i; + + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + fLength = (short) region.getLength(); + fTextLength = (short) region.getTextLength(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + return fLength; + } + + public int getStart() { + return fStart; + } + + public int getTextEnd() { + return fStart + fTextLength; + } + + public int getTextLength() { + return fTextLength; + } + + public String getType() { + return fType; + } + + public String toString() { + return RegionToStringUtil.toString(this); + } + + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + // can never be updated + return null; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/TagOpenRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/TagOpenRegion.java new file mode 100644 index 0000000000..f09c83dadd --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/TagOpenRegion.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +public class TagOpenRegion implements ITextRegion { + static private final String fType = XMLRegionContext.XML_TAG_OPEN; + private int fLength; + private int fStart; + private int fTextLength; + + + public TagOpenRegion() { + super(); + } + + public TagOpenRegion(int start, int textLength, int length) { + this(); + fStart = start; + fTextLength = textLength; + fLength = length; + } + + public void adjustLengthWith(int i) { + fLength += i; + + } + + public void adjustStart(int i) { + fStart += i; + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + fTextLength += 1; + + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + fLength = region.getLength(); + fTextLength = region.getTextLength(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + return fLength; + } + + public int getStart() { + return fStart; + } + + public int getTextEnd() { + return fStart + fTextLength; + } + + public int getTextLength() { + return fTextLength; + } + + public String getType() { + return fType; + } + + public String toString() { + return RegionToStringUtil.toString(this); + } + + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + // can never be updated + return null; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/WhiteSpaceOnlyRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/WhiteSpaceOnlyRegion.java new file mode 100644 index 0000000000..2727095457 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/WhiteSpaceOnlyRegion.java @@ -0,0 +1,190 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + + + +import org.eclipse.wst.sse.core.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.exceptions.SourceEditingRuntimeException; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.util.Debug; +import org.eclipse.wst.sse.core.util.Utilities; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +public class WhiteSpaceOnlyRegion implements ITextRegion { + static private final byte fTextLength = 0; + + static private final String fType = XMLRegionContext.WHITE_SPACE; + protected int fLength; + protected int fStart; + + public WhiteSpaceOnlyRegion(int start, int length) { + super(); + fStart = start; + fLength = length; + } + + public void adjust(int i) { + fStart += i; + } + + public void adjustLengthWith(int i) { + fLength += i; + } + + public void adjustStart(int i) { + fStart += i; + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + // not supported + + } + + public boolean contains(int position) { + + return fStart <= position && position < fStart + fLength; + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + fLength = region.getLength(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + return fLength; + } + + public int getStart() { + return fStart; + } + + public int getTextEnd() { + return fStart + fTextLength; + } + + public int getTextLength() { + return fTextLength; + } + + public String getType() { + return fType; + } + + public void setLength(int i) { + fLength = i; + } + + public void setStart(int i) { + fStart = i; + } + + public void setTextLength(short i) { + throw new SourceEditingRuntimeException("invalid call"); //$NON-NLS-1$ + } + + public void setType(String string) { + throw new SourceEditingRuntimeException("invalid call"); //$NON-NLS-1$ + } + + public String toString() { + return RegionToStringUtil.toString(this); + } + + /** + * For this ITextRegion type, the start must in terms of what the region + * expects ... that is, its not document offset, but start relative to + * what ever contains it. + */ + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + // if the region is an easy type (e.g. attribute value), + // and the requested changes are all + // alphanumeric, then make the change here locally. + // (This can obviously be made more sophisticated as the need arises, + // but should + // always follow this pattern.) + if (Debug.debugStructuredDocument) { + System.out.println("\t\tContextRegion::updateModel"); //$NON-NLS-1$ + System.out.println("\t\t\tregion type is " + fType); //$NON-NLS-1$ + } + boolean canHandle = false; + // note: we'll always handle deletes from these + // regions ... if its already that region, + // deleting something from it won't change its + // type. (remember, the calling program needs + // to insure we are not called, if not all contained + // on one region. + if ((changes == null) || (changes.length() == 0)) { + // delete case + // We can not do the quick delete, if + // if all the text in a region is to be deleted. + // Or, if the delete starts in the white space region. + // In these cases, a reparse is needed. + // Minor note, we use textEnd-start since it always + // less than or equal to end-start. This might + // cause us to miss a few cases we could have handled, + // but will prevent us from trying to handle funning cases + // involving whitespace. + if ((fStart >= getTextEnd()) || (Math.abs(lengthToReplace) >= getTextEnd() - getStart())) { + canHandle = false; + } else { + canHandle = true; + } + } else { + if (RegionUpdateRule.canHandleAsWhiteSpace(this, parent, changes, requestStart, lengthToReplace)) { + canHandle = true; + } else { + canHandle = false; + } + + } + RegionChangedEvent result = null; + + if (canHandle) { + // at this point, we still have the old region. We won't create a + // new instance, we'll just update the one we have, by changing + // its end postion, + // The parent flatnode, upon return, has responsibility + // for updating sibling regions. + // and in turn, the structuredDocument itself has responsibility + // for + // updating the text store and down stream flatnodes. + if (Debug.debugStructuredDocument) { + System.out.println("change handled by region"); //$NON-NLS-1$ + } + int lengthDifference = Utilities.calculateLengthDifference(changes, lengthToReplace); + // Note: we adjust both end and text end, because for any change + // that is in only the trailing whitespace region, we should not + // do a quick change, + // so 'canHandle' should have been false for those case. + fLength += lengthDifference; + + result = new RegionChangedEvent(parent.getParentDocument(), requester, parent, this, changes, requestStart, lengthToReplace); + } + + return result; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLCDataTextRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLCDataTextRegion.java new file mode 100644 index 0000000000..e6f3cbecef --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLCDataTextRegion.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.util.Debug; +import org.eclipse.wst.sse.core.util.Utilities; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +public class XMLCDataTextRegion implements ITextRegion { + static private final String fType = XMLRegionContext.XML_CDATA_TEXT; + private int fLength; + private int fStart; + private int fTextLength; + + + public XMLCDataTextRegion() { + super(); + } + + public XMLCDataTextRegion(int start, int textLength, int length) { + this(); + fStart = start; + fTextLength = textLength; + fLength = length; + } + + public void adjustLengthWith(int i) { + fLength += i; + + } + + public void adjustStart(int i) { + fStart += i; + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + fTextLength += i; + + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + fLength = region.getLength(); + fTextLength = region.getTextLength(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + // TODO: shouldn't cdata be like XML Content ... length and text + // length + // always be the same, regardless of whitespace? + return fLength; + } + + public int getStart() { + return fStart; + } + + public int getTextEnd() { + return fStart + fTextLength; + } + + public int getTextLength() { + return fTextLength; + } + + public String getType() { + return fType; + } + + public String toString() { + return RegionToStringUtil.toString(this); + } + + + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + // TODO: this is a pretty lame method, since XML CData region can have + // a much + // better rule for region update, but this is what previous + // (unfactored) + // version had, so I'll carry it over, of now. + RegionChangedEvent result = null; + // if the region is an easy type (e.g. attribute value), + // and the requested changes are all + // alphanumeric, then make the change here locally. + // (This can obviously be made more sophisticated as the need arises, + // but should + // always follow this pattern.) + if (Debug.debugStructuredDocument) { + System.out.println("\t\tContextRegion::updateModel"); //$NON-NLS-1$ + System.out.println("\t\t\tregion type is " + fType); //$NON-NLS-1$ + } + boolean canHandle = false; + // note: we'll always handle deletes from these + // regions ... if its already that region, + // deleting something from it won't change its + // type. (remember, the calling program needs + // to insure we are not called, if not all contained + // on one region. + if ((changes == null) || (changes.length() == 0)) { + // delete case + // We can not do the quick delete, if + // if all the text in a region is to be deleted. + // Or, if the delete starts in the white space region. + // In these cases, a reparse is needed. + // Minor note, we use textEnd-start since it always + // less than or equal to end-start. This might + // cause us to miss a few cases we could have handled, + // but will prevent us from trying to handle funning cases + // involving whitespace. + if ((fStart >= getTextEnd()) || (Math.abs(lengthToReplace) >= getTextEnd() - getStart())) { + canHandle = false; + } else { + canHandle = true; + } + } else { + if ((RegionUpdateRule.canHandleAsWhiteSpace(this, parent, changes, requestStart, lengthToReplace)) || RegionUpdateRule.canHandleAsLetterOrDigit(this, parent, changes, requestStart, lengthToReplace)) { + canHandle = true; + } else { + canHandle = false; + } + + } + if (canHandle) { + // at this point, we still have the old region. We won't create a + // new instance, we'll just update the one we have, by changing + // its end postion, + // The parent flatnode, upon return, has responsibility + // for updating sibling regions. + // and in turn, the structuredDocument itself has responsibility + // for + // updating the text store and down stream flatnodes. + if (Debug.debugStructuredDocument) { + System.out.println("change handled by region"); //$NON-NLS-1$ + } + int lengthDifference = Utilities.calculateLengthDifference(changes, lengthToReplace); + // Note: we adjust both end and text end, because for any change + // that is in only the trailing whitespace region, we should not + // do a quick change, + // so 'canHandle' should have been false for those case. + // TO_DO_FUTURE: cache value of canHandleAsWhiteSpace from above + // If we are handling as whitespace, there is no need to increase + // the text length, only + // the total length is changing. + if (!RegionUpdateRule.canHandleAsWhiteSpace(this, parent, changes, requestStart, lengthToReplace)) { + fTextLength += lengthDifference; + } + fLength += lengthDifference; + result = new RegionChangedEvent(parent.getParentDocument(), requester, parent, this, changes, requestStart, lengthToReplace); + } + + return result; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLContentRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLContentRegion.java new file mode 100644 index 0000000000..7155dfcedf --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLContentRegion.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.util.Debug; +import org.eclipse.wst.sse.core.util.Utilities; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + + +public class XMLContentRegion implements ITextRegion { + static private final String fType = XMLRegionContext.XML_CONTENT; + // length and textLength are always the same for content region + //private int fTextLength; + private int fLength; + private int fStart; + + + public XMLContentRegion() { + super(); + } + + public XMLContentRegion(int start, int length) { + this(); + fStart = start; + fLength = length; + } + + + public void adjustLengthWith(int i) { + fLength += i; + + } + + public void adjustStart(int i) { + fStart += i; + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + // not supported + + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + fLength = region.getLength(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + return fLength; + } + + public int getStart() { + return fStart; + } + + public int getTextEnd() { + return fStart + fLength; + } + + public int getTextLength() { + return fLength; + } + + public String getType() { + return fType; + } + + public String toString() { + return RegionToStringUtil.toString(this); + } + + + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + // TODO: this is a pretty lame method, since XML Content can have a + // much + // better rule for region update, but this is what previous + // (unfactored) + // version had, so I'll carry it over, of now. + RegionChangedEvent result = null; + // if the region is an easy type (e.g. attribute value), + // and the requested changes are all + // alphanumeric, then make the change here locally. + // (This can obviously be made more sophisticated as the need arises, + // but should + // always follow this pattern.) + if (Debug.debugStructuredDocument) { + System.out.println("\t\tContextRegion::updateModel"); //$NON-NLS-1$ + System.out.println("\t\t\tregion type is " + fType); //$NON-NLS-1$ + } + boolean canHandle = false; + // note: we'll always handle deletes from these + // regions ... if its already that region, + // deleting something from it won't change its + // type. (remember, the calling program needs + // to insure we are not called, if not all contained + // on one region. + if ((changes == null) || (changes.length() == 0)) { + // delete case + // We can not do the quick delete, if + // if all the text in a region is to be deleted. + // Or, if the delete starts in the white space region. + // In these cases, a reparse is needed. + // Minor note, we use textEnd-start since it always + // less than or equal to end-start. This might + // cause us to miss a few cases we could have handled, + // but will prevent us from trying to handle funning cases + // involving whitespace. + if ((fStart >= getTextEnd()) || (Math.abs(lengthToReplace) >= getTextEnd() - getStart())) { + canHandle = false; + } else { + canHandle = true; + } + } else { + if ((RegionUpdateRule.canHandleAsWhiteSpace(this, parent, changes, requestStart, lengthToReplace)) || RegionUpdateRule.canHandleAsLetterOrDigit(this, parent, changes, requestStart, lengthToReplace)) { + canHandle = true; + } else { + canHandle = false; + } + + } + if (canHandle) { + // at this point, we still have the old region. We won't create a + // new instance, we'll just update the one we have, by changing + // its end postion, + // The parent flatnode, upon return, has responsibility + // for updating sibling regions. + // and in turn, the structuredDocument itself has responsibility + // for + // updating the text store and down stream flatnodes. + if (Debug.debugStructuredDocument) { + System.out.println("change handled by region"); //$NON-NLS-1$ + } + int lengthDifference = Utilities.calculateLengthDifference(changes, lengthToReplace); + // Note: we adjust both end and text end, because for any change + // that is in only the trailing whitespace region, we should not + // do a quick change, + // so 'canHandle' should have been false for those case. + fLength += lengthDifference; + // TO_DO_FUTURE: cache value of canHandleAsWhiteSpace from above + // If we are handling as whitespace, there is no need to increase + // the text length, only + // the total length is changing. + // don't need for content region + // if (!RegionUpdateRule.canHandleAsWhiteSpace(this, changes, + // fStart, lengthToReplace)) { + // fTextLength += lengthDifference; + // } + result = new RegionChangedEvent(parent.getParentDocument(), requester, parent, this, changes, requestStart, lengthToReplace); + } + + return result; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLHeadParserFactory.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLHeadParserFactory.java new file mode 100644 index 0000000000..66d9ee9413 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLHeadParserFactory.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.text.ITextRegion; + +public class XMLHeadParserFactory { + public ITextRegion createToken(String context, int start, int textLength, int length, String text) { + ITextRegion newRegion = null; + // if (context == XMLRegionContext.XML_CDATA_TEXT) { + newRegion = new XMLHeadParserRegion(context, start, textLength, length, text); + // } + return newRegion; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLHeadParserRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLHeadParserRegion.java new file mode 100644 index 0000000000..98dad747b4 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLHeadParserRegion.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + +import org.eclipse.wst.sse.core.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +/** + * + * This class is not intended to be used, its just present to server as a + * generic starting point for adding new specific region types. + */ + +public class XMLHeadParserRegion implements ITextRegion { + private int fLength; + private int fStart; + private String fText; + private int fTextLength; + // specify correct type + private String fType = XMLRegionContext.UNDEFINED; + + public XMLHeadParserRegion() { + super(); + } + + public XMLHeadParserRegion(String context, int start, int textLength, int length, String text) { + this(); + fType = context; + fStart = start; + fTextLength = textLength; + fLength = length; + fText = text; + } + + public void adjustLengthWith(int i) { + fLength += i; + + } + + public void adjustStart(int i) { + fStart += i; + + } + + /* + * (non-Javadoc) + * + * @see com.ibm.sed.structured.text.ITextRegion#adjustTextLength(int) + */ + public void adjustTextLength(int i) { + fTextLength += 1; + + } + + public void equatePositions(ITextRegion region) { + fStart = region.getStart(); + fLength = region.getLength(); + fTextLength = region.getTextLength(); + } + + public int getEnd() { + return fStart + fLength; + } + + public int getLength() { + return fLength; + } + + public int getStart() { + return fStart; + } + + public String getText() { + return fText; + } + + public int getTextEnd() { + return fStart + fTextLength; + } + + public int getTextLength() { + return fTextLength; + } + + public String getType() { + return fType; + } + + public String toString() { + return RegionToStringUtil.toString(this); + } + + public StructuredDocumentEvent updateModel(Object requester, IStructuredDocumentRegion parent, String changes, int requestStart, int lengthToReplace) { + // can never be updated + return null; + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLParserRegionFactory.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLParserRegionFactory.java new file mode 100644 index 0000000000..6b03796502 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/parser/regions/XMLParserRegionFactory.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.parser.regions; + + + +import org.eclipse.wst.sse.core.internal.parser.ContextRegion; +import org.eclipse.wst.sse.core.text.ITextRegion; +import org.eclipse.wst.sse.core.text.ITextRegionContainer; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +/** + * + * This region factory is very specific to the parser output, and the specific + * implementation classes for various regions. + */ + +public class XMLParserRegionFactory { + + public XMLParserRegionFactory() { + super(); + } + + public ITextRegion createToken(ITextRegionContainer parent, String context, int start, int textLength, int length) { + return this.createToken(parent, context, start, textLength, length, null, null); + } + + public ITextRegion createToken(ITextRegionContainer parent, String context, int start, int textLength, int length, String lang, String surroundingTag) { + ITextRegion newRegion = createToken(context, start, textLength, length); + // DW, 4/16/2003 token regions no longer have parents + //newRegion.setParent(parent); + return newRegion; + } + + public ITextRegion createToken(String context, int start, int textLength, int length) { + return this.createToken(context, start, textLength, length, null, null); + } + + public ITextRegion createToken(String context, int start, int textLength, int length, String lang, String surroundingTag) { + ITextRegion newRegion = null; + if (context == XMLRegionContext.XML_CDATA_TEXT) { + newRegion = new XMLCDataTextRegion(start, textLength, length); + } else if (context == XMLRegionContext.XML_CONTENT) { + newRegion = new XMLContentRegion(start, length); + } else if (context == XMLRegionContext.XML_TAG_NAME) { + newRegion = new TagNameRegion(start, textLength, length); + } else if (context == XMLRegionContext.XML_TAG_ATTRIBUTE_NAME) { + newRegion = new AttributeNameRegion(start, textLength, length); + } else if (context == XMLRegionContext.XML_TAG_ATTRIBUTE_EQUALS) { + newRegion = new AttributeEqualsRegion(start, textLength, length); + } else if (context == XMLRegionContext.XML_TAG_ATTRIBUTE_VALUE) { + newRegion = new AttributeValueRegion(start, textLength, length); + } else if (context == XMLRegionContext.XML_TAG_OPEN) { + newRegion = new TagOpenRegion(start, textLength, length); + } else if (context == XMLRegionContext.XML_TAG_CLOSE) { + newRegion = new TagCloseRegion(start); + } else if (context == XMLRegionContext.WHITE_SPACE) { + newRegion = new WhiteSpaceOnlyRegion(start, length); + } else + // removed this condition during transition, and implemented in + // subclass + // if (context == XMLJSPRegionContexts.JSP_CONTENT) { + // newRegion = new JSPCodeRegion(context, start, textLength, length); + // } else + if (context == XMLRegionContext.BLOCK_TEXT) { + newRegion = new BlockTextRegion(context, start, textLength, length); + ((BlockTextRegion) newRegion).setSurroundingTag(surroundingTag); + } else { + newRegion = new ContextRegion(context, start, textLength, length); + } + return newRegion; + } + + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/propagate/PropagatingAdapterFactoryImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/propagate/PropagatingAdapterFactoryImpl.java new file mode 100644 index 0000000000..ed13b8a959 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/propagate/PropagatingAdapterFactoryImpl.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.propagate; + + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.wst.sse.core.AbstractAdapterFactory; +import org.eclipse.wst.sse.core.AdapterFactory; +import org.eclipse.wst.sse.core.INodeAdapter; +import org.eclipse.wst.sse.core.INodeNotifier; +import org.eclipse.wst.sse.core.PropagatingAdapter; +import org.eclipse.wst.sse.core.PropagatingAdapterFactory; + + +/** + * The PropagatingAdapterFactory is part of the "adapt on create" mechanism. A + * PropagatingAdapter, once added to a node, will cause proagating adapters to + * be created for all child nodes. A side effect of creating a + * PropagatingAdapter for a node is that is is also creates adapters for and + * adapts the Node for all other registered 'create on adapt' Adapters. This + * other adapters are registered by registering their factories via plugin + * extension point. + */ +public class PropagatingAdapterFactoryImpl extends AbstractAdapterFactory implements PropagatingAdapterFactory { + + private PropagatingAdapter adapterInstance; + protected List contributedFactories = null; + + /** + * PropagatingAdapterFactory constructor comment. + */ + public PropagatingAdapterFactoryImpl() { + this(PropagatingAdapter.class, true); + } + + protected PropagatingAdapterFactoryImpl(Object adapterKey, boolean registerAdapters) { //, + // Object + // modelType) + // { + super(adapterKey, registerAdapters); + } + + public void addContributedFactories(AdapterFactory factory) { + if (contributedFactories != null) { + contributedFactories.add(factory); + } + + } + + public AdapterFactory copy() { + PropagatingAdapterFactory clonedInstance = new PropagatingAdapterFactoryImpl(this.adapterKey, this.shouldRegisterAdapter); + // clone this adapters specific list of adapter factories too + if (contributedFactories != null) { + Iterator iterator = contributedFactories.iterator(); + clonedInstance.setContributedFactories(new ArrayList()); + while (iterator.hasNext()) { + AdapterFactory existingFactory = (AdapterFactory) iterator.next(); + clonedInstance.addContributedFactories(existingFactory.copy()); + } + } + return clonedInstance; + } + + /** + * createAdapter method comment. + */ + protected INodeAdapter createAdapter(INodeNotifier target) { + // every notifier get's one of these + // (and the same instance of it) + return getAdapterInstance(); + } + + /** + * Gets the adapterInstance. + * + * @return Returns a PropagatingAdapter + */ + protected PropagatingAdapter getAdapterInstance() { + if (adapterInstance == null) { + adapterInstance = new PropagatingAdapterImpl(); + if (contributedFactories != null) { + for (int i = 0; i < contributedFactories.size(); i++) + adapterInstance.addAdaptOnCreateFactory((PropagatingAdapterFactory) contributedFactories.get(i)); + } + } + return adapterInstance; + } + + public void release() { + // give the adapter instance a chance to release its factories + getAdapterInstance().release(); + + } + + public void setContributedFactories(ArrayList list) { + contributedFactories = list; + + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/propagate/PropagatingAdapterImpl.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/propagate/PropagatingAdapterImpl.java new file mode 100644 index 0000000000..4f0b3201f0 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/propagate/PropagatingAdapterImpl.java @@ -0,0 +1,168 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.propagate; + + + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.wst.sse.core.AdapterFactory; +import org.eclipse.wst.sse.core.INodeNotifier; +import org.eclipse.wst.sse.core.PropagatingAdapter; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + + +public class PropagatingAdapterImpl implements PropagatingAdapter { + + public static final Class PropagatingAdapterClass = PropagatingAdapter.class; + // because so many of these are created in huge file, + // Jeffrey Liu suggested these be done lazily, since not all + // models and not all nodes actually have a list of factories. + private List adaptOnCreateFactories = null; + + /** + * AbstractPropagatingAdapterImpl constructor comment. + */ + public PropagatingAdapterImpl() { + super(); + } + + protected void adaptOnCreate(XMLNode node) { + // give each of the factories a chance to adapt the node, if it + // chooses to + if (adaptOnCreateFactories != null) { + Iterator iterator = adaptOnCreateFactories.iterator(); + while (iterator.hasNext()) { + AdapterFactory factory = (AdapterFactory) iterator.next(); + factory.adapt(node); + } + } + + } + + /** + * This mechanism can be made "easier to use" later. + */ + public void addAdaptOnCreateFactory(AdapterFactory factory) { + //adaptOnCreateFactories.add(factory); + getAdaptOnCreateFactories().add(factory); + } + + /** + * Gets the adaptOnCreateFactories. + * + * @return Returns a List + */ + public List getAdaptOnCreateFactories() { + if (adaptOnCreateFactories == null) + adaptOnCreateFactories = new ArrayList(); + return adaptOnCreateFactories; + } + + // protected void unadaptOnRemove(INodeNotifier node) { + // // give each of the factories a chance to process remove event + // // This is a bit out of the normal adapter pattern, but I couldn't + // // think of a better way to "remove" pageDirectiveWatchers, if and + // // when the page directive was 'removed' (edited). + // // + // Iterator iterator = adaptOnCreateFactories.iterator(); + // while (iterator.hasNext()) { + // AdapterFactory factory = (AdapterFactory) iterator.next(); + // if (factory instanceof PropagatingAdapterFactory) { + // ((PropagatingAdapterFactory)factory).unadapt(node); + // } + // } + // + // } + + /** + * @see PropagatingAdapter#initializeForFactory(AdapterFactory, + * INodeNotifier) + */ + public void initializeForFactory(AdapterFactory factory, INodeNotifier node) { + // we're DOM specific implimentation + if (node instanceof XMLNode) { + XMLNode xmlNode = (XMLNode) node; + propagateTo(xmlNode); + } + } + + /** + * Allowing the INodeAdapter to compare itself against the type allows it + * to return true in more than one case. + */ + public boolean isAdapterForType(Object type) { + return type.equals(PropagatingAdapterClass); + } + + protected boolean isInteresting(Object newValue) { + return (newValue != null && (newValue instanceof Element || newValue instanceof Document || newValue instanceof DocumentType)); + } + + /** + */ + public void notifyChanged(INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) { + // DMW, 2002.8.10. I changed this so we only propagate to Elements ... + // not attributes too! + // I'm assuming this will help performance and memory, but don't know + // if anyone was depending on + // this being proagate to attributes. + if (eventType == INodeNotifier.ADD && isInteresting(newValue)) { + propagateTo((XMLNode) newValue); + } + // else if (eventType == INodeNotifier.CONTENT_CHANGED) { + // notifier.getAdapterFor(PropagatingAdapterClass); + // } + // else if (eventType == INodeNotifier.CHANGE) { + // } + // else if (eventType == INodeNotifier.REMOVE && + // isInteresting(oldValue)) { + // unadaptOnRemove((XMLNode)oldValue); + // } + // else if (eventType == INodeNotifier.STRUCTURE_CHANGED) { + // } + } + + protected void propagateTo(XMLNode node) { + // get adapter to ensure its created + node.getAdapterFor(PropagatingAdapterClass); + adaptOnCreate(node); + propagateToChildren(node); + } + + protected void propagateToChildren(XMLNode parent) { + for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { + propagateTo((XMLNode) child); + } + } + + /** + * @see PropagatingAdapter#release() + */ + public void release() { + if (adaptOnCreateFactories != null) { + Iterator iterator = adaptOnCreateFactories.iterator(); + while (iterator.hasNext()) { + AdapterFactory factory = (AdapterFactory) iterator.next(); + factory.release(); + } + } + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/text/XMLStructuredDocumentRegion.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/text/XMLStructuredDocumentRegion.java new file mode 100644 index 0000000000..77de14a4e7 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/text/XMLStructuredDocumentRegion.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.text; + +import org.eclipse.wst.sse.core.internal.text.BasicStructuredDocumentRegion; +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.xml.core.parser.XMLRegionContext; + + +public class XMLStructuredDocumentRegion extends BasicStructuredDocumentRegion implements IStructuredDocumentRegion { + + public XMLStructuredDocumentRegion() { + super(); + } + + public String getType() { + String result = super.getType(); + // normally, we want the second region as the flatnode type ... since + // the + // first one is usually just "open tag". + if ((result != XMLRegionContext.XML_PI_OPEN) && (getRegions().size() > 1)) { + result = getRegions().get(1).getType(); + } + return result; + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/util/DebugDocument.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/util/DebugDocument.java new file mode 100644 index 0000000000..13afbc8647 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/util/DebugDocument.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.util; + +import java.io.PrintStream; + +import org.eclipse.wst.sse.core.text.IStructuredDocumentRegion; +import org.eclipse.wst.xml.core.document.XMLNode; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +public class DebugDocument { + + public static void dump(Document document) { + if (document == null) + return; + System.out.println("Dump of DOM"); //$NON-NLS-1$ + + dump(document, System.out); + + // + System.out.println(); + System.out.println("= = = = = ="); //$NON-NLS-1$ + System.out.println(); + + } + + public static void dump(Document document, PrintStream out) { + Node node = document.getFirstChild(); + while (node != null) { + dump(node, out); + node = node.getNextSibling(); + } + + } + + public static void dump(Node topNode) { + dump(topNode, System.out); + } + + public static void dump(Node topNode, PrintStream out) { + if (topNode == null) + return; + // print out this node + // + printNode(topNode, out); + + // now print out its children + //NodeList nodes = topNode.getChildNodes(); + //int len = nodes.getLength(); + //for (int i = 0; i < len; i++) { + + //Node node = nodes.item(i); + //dump(node, out); + //} + } + + public static void printNode(Node topNode) { + printNode(topNode, System.out); + + } + + public static void printNode(Node topNode, PrintStream out) { + // print out this node + // + IStructuredDocumentRegion firstStructuredDocumentRegion = ((XMLNode) topNode).getFirstStructuredDocumentRegion(); + IStructuredDocumentRegion lastStructuredDocumentRegion = ((XMLNode) topNode).getLastStructuredDocumentRegion(); + if ((firstStructuredDocumentRegion == null) || (lastStructuredDocumentRegion == null)) { + // no text to output + } else { + int start = firstStructuredDocumentRegion.getStart(); + int end = lastStructuredDocumentRegion.getEnd(); + + String outString = topNode.toString(); + outString = org.eclipse.wst.sse.core.util.StringUtils.escape(outString); + out.println("[" + start + ", " + end + "]" + outString); //$NON-NLS-3$//$NON-NLS-2$//$NON-NLS-1$ + } + // now do its children + NodeList nodes = topNode.getChildNodes(); + int len = nodes.getLength(); + for (int i = 0; i < len; i++) { + Node childNode = nodes.item(i); + printNode(childNode, out); + } + + } + + public DebugDocument() { + super(); + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/validate/AbstractPropagatingValidator.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/validate/AbstractPropagatingValidator.java new file mode 100644 index 0000000000..9144662bd2 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/validate/AbstractPropagatingValidator.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.validate; + +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.sse.core.validate.ValidationAdapter; +import org.w3c.dom.Node; + + +public abstract class AbstractPropagatingValidator extends ValidationComponent { + + /** + * Constructor for AbstractPropagatingValidator. + */ + public AbstractPropagatingValidator() { + super(); + } + + protected abstract ValidationComponent getPropagatee(); + + protected abstract ValidationAdapter getValidator(); + + /** + * @see com.ibm.sed.adapters.validate.ValidationAdapter#validate(IndexedRegion) + */ + public void validate(IndexedRegion node) { + if (node == null) + return; + getValidator().validate(node); + + + Propagator.propagateToChildElements(getPropagatee(), (Node) node); + } + +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/validate/Propagator.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/validate/Propagator.java new file mode 100644 index 0000000000..3cdcca5312 --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/validate/Propagator.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.validate; + +import org.eclipse.wst.sse.core.INodeNotifier; +import org.eclipse.wst.sse.core.IndexedRegion; +import org.eclipse.wst.sse.core.validate.ValidationAdapter; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +public class Propagator { + + public static void propagateToChildElements(ValidationComponent validator, Node parent) { + if (parent == null) + return; + Class clazz = validator.getClass(); + + NodeList children = parent.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + Node child = children.item(i); + if (child == null || child.getNodeType() != Node.ELEMENT_NODE) + continue; + + INodeNotifier notifier = (INodeNotifier) child; + ValidationAdapter va = (ValidationAdapter) notifier.getExistingAdapter(clazz); + if (va == null) { + notifier.addAdapter(validator); + va = validator; + } + va.validate((IndexedRegion) child); + } + } + + /** + * Propagator is just a placeholder of utilities. Don't instantiate. + */ + private Propagator() { + super(); + } +} diff --git a/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/validate/ValidationComponent.java b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/validate/ValidationComponent.java new file mode 100644 index 0000000000..797d9cbf0a --- /dev/null +++ b/bundles/org.eclipse.wst.xml.core/src/org/eclipse/wst/xml/core/internal/validate/ValidationComponent.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2001, 2004 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.xml.core.internal.validate; + + + +import org.eclipse.wst.sse.core.validate.ValidationAdapter; +import org.eclipse.wst.sse.core.validate.ValidationReporter; + +public abstract class ValidationComponent implements ValidationAdapter { + + protected ValidationReporter reporter = null; + + /** + * ValidationComponent constructor comment. + */ + public ValidationComponent() { + super(); + } + + /** + * Allowing the INodeAdapter to compare itself against the type allows it + * to return true in more than one case. + */ + public boolean isAdapterForType(Object type) { + return (type == ValidationAdapter.class); + } + + /** + */ + public void notifyChanged(org.eclipse.wst.sse.core.INodeNotifier notifier, int eventType, Object changedFeature, Object oldValue, Object newValue, int pos) { + // This method will be implemented in the V2. + } + + /** + */ + public void setReporter(ValidationReporter reporter) { + this.reporter = reporter; + } +} |