diff options
Diffstat (limited to 'json/bundles/org.eclipse.wst.json.core/src')
126 files changed, 22773 insertions, 0 deletions
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/JSONCorePlugin.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/JSONCorePlugin.java new file mode 100644 index 0000000000..8f7d964090 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/JSONCorePlugin.java @@ -0,0 +1,118 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core;
+
+import java.io.IOException;
+
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.json.schema.IJSONSchemaDocument;
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.internal.schema.SchemaProcessorRegistryReader;
+import org.eclipse.wst.json.core.internal.schema.catalog.Catalog;
+import org.eclipse.wst.json.core.internal.schema.catalog.CatalogSet;
+import org.eclipse.wst.json.core.schema.catalog.ICatalog;
+import org.osgi.framework.BundleContext;
+
+/**
+ * The activator class controls the plug-in life cycle
+ */
+public class JSONCorePlugin extends Plugin {
+
+ // The plug-in ID
+ public static final String PLUGIN_ID = "org.eclipse.wst.json.core"; //$NON-NLS-1$
+
+ public static final String USER_CATALOG_ID = "user_catalog"; //$NON-NLS-1$
+ public static final String DEFAULT_CATALOG_ID = "default_catalog"; //$NON-NLS-1$
+ public static final String SYSTEM_CATALOG_ID = "system_catalog"; //$NON-NLS-1$
+
+ private CatalogSet catalogSet = null;
+ private String defaultCatalogFileStateLocation;
+
+ // The shared instance
+ private static JSONCorePlugin plugin;
+
+ /**
+ * The constructor
+ */
+ public JSONCorePlugin() {
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext
+ * )
+ */
+ public void start(BundleContext context) throws Exception {
+ super.start(context);
+ plugin = this;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext
+ * )
+ */
+ public void stop(BundleContext context) throws Exception {
+ plugin = null;
+ super.stop(context);
+ }
+
+ /**
+ * Returns the shared instance
+ *
+ * @return the shared instance
+ */
+ public static JSONCorePlugin getDefault() {
+ return plugin;
+ }
+
+ private String getPluginStateLocation(String fileName) {
+ String location = getStateLocation().append(fileName).toString();
+ String file_protocol = "file:"; //$NON-NLS-1$
+ if (location != null && !location.startsWith(file_protocol)) {
+ location = file_protocol + location;
+ }
+ return location;
+ }
+
+ public IJSONSchemaDocument getSchemaDocument(IJSONNode node)
+ throws IOException {
+ return SchemaProcessorRegistryReader.getInstance().getSchemaDocument(
+ node.getModel());
+ }
+
+ public ICatalog getDefaultJSONCatalog() {
+ if (catalogSet == null) {
+ catalogSet = new CatalogSet();
+
+ defaultCatalogFileStateLocation = getPluginStateLocation(Catalog.DEFAULT_CATALOG_FILE);
+
+ catalogSet.putCatalogPersistenceLocation(DEFAULT_CATALOG_ID,
+ defaultCatalogFileStateLocation);
+ catalogSet.putCatalogPersistenceLocation(SYSTEM_CATALOG_ID,
+ getPluginStateLocation(Catalog.SYSTEM_CATALOG_FILE));
+ catalogSet.putCatalogPersistenceLocation(USER_CATALOG_ID,
+ getPluginStateLocation(Catalog.USER_CATALOG_FILE));
+ }
+ return catalogSet.lookupOrCreateCatalog(DEFAULT_CATALOG_ID,
+ defaultCatalogFileStateLocation);
+ }
+
+ public void clearCatalogCache() {
+ if (catalogSet != null) {
+ catalogSet.clearResourceCache();
+ }
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/CleanupProcessorJSON.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/CleanupProcessorJSON.java new file mode 100644 index 0000000000..9ed28496a6 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/CleanupProcessorJSON.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2001, 2009 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.cleanup.CleanupProcessorCSS + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.cleanup; + +import java.io.IOException; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.json.core.contenttype.ContentTypeIdForJSON; +import org.eclipse.wst.json.core.document.IJSONDocument; +import org.eclipse.wst.json.core.document.IJSONModel; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.internal.format.IJSONSourceFormatter; +import org.eclipse.wst.json.core.internal.format.JSONFormatUtil; +import org.eclipse.wst.json.core.internal.format.JSONSourceFormatterFactory; +import org.eclipse.wst.sse.core.internal.cleanup.AbstractStructuredCleanupProcessor; +import org.eclipse.wst.sse.core.internal.cleanup.IStructuredCleanupHandler; +import org.eclipse.wst.sse.core.internal.format.IStructuredFormatProcessor; +import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.w3c.dom.Node; + +public class CleanupProcessorJSON extends AbstractStructuredCleanupProcessor { + + public void cleanupModel(IStructuredModel structuredModel, int start, + int length) { + JSONFormatUtil formatUtil = JSONFormatUtil.getInstance(); + if (structuredModel instanceof IJSONModel) { + IJSONDocument doc = ((IJSONModel) structuredModel).getDocument(); + IJSONSourceFormatter formatter = JSONSourceFormatterFactory + .getInstance().getSourceFormatter((INodeNotifier) doc); + StringBuilder buf = formatter.cleanup(doc); + if (buf != null) { + int startOffset = ((IndexedRegion) doc).getStartOffset(); + int endOffset = ((IndexedRegion) doc).getEndOffset(); + formatUtil.replaceSource(doc.getModel(), startOffset, endOffset + - startOffset, buf.toString()); + } + } + } + + protected String getContentType() { + return ContentTypeIdForJSON.ContentTypeID_JSON; + } + + public void cleanupModel(IStructuredModel structuredModel) { + int start = 0; + int length = structuredModel.getStructuredDocument().getLength(); + + cleanupModel(structuredModel, start, length); + } + + public void cleanupDocument(IDocument document) throws IOException, + CoreException { + // TODO should implement, or delete? + + } + + public void cleanupDocument(IDocument document, int start, int length) + throws IOException, CoreException { + // TODO should implement, or delete? + + } + + protected IStructuredCleanupHandler getCleanupHandler(Node node) { + return null; + } + + protected IStructuredFormatProcessor getFormatProcessor() { + return null; + } + + protected void refreshCleanupPreferences() { + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/IJSONCleanupStrategy.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/IJSONCleanupStrategy.java new file mode 100644 index 0000000000..ccb1f8ff8b --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/IJSONCleanupStrategy.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2004, 2010 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.cleanup.CSSCleanupStrategy + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.cleanup; + + + +public interface IJSONCleanupStrategy { + + static final short ASIS = 0; + static final short LOWER = 1; + static final short UPPER = 2; + + /** + * + * @return short + */ + short getIdentCase(); + + /** + * + * @return short + */ + short getPropNameCase(); + + /** + * + * @return short + */ + short getPropValueCase(); + + /** + * + * @return short + */ + short getSelectorTagCase(); + + short getClassSelectorCase(); + + short getIdSelectorCase(); + + /** + * + * @return boolean + */ + boolean isFormatSource(); + + /** + * + * @return boolean + */ + boolean isQuoteValues(); + + /** + * + * @param formatSource + * boolean + */ + void setFormatSource(boolean formatSource); + + /** + * + * @param identCase + * short + */ + void setIdentCase(short identCase); + + /** + * + * @param propNameCase + * short + */ + void setPropNameCase(short propNameCase); + + /** + * + * @param propValueCase + * short + */ + void setPropValueCase(short propValueCase); + + /** + * + * @param quoteValues + * boolean + */ + void setQuoteValues(boolean quoteValues); + + /** + * + * @param selectorTagCase + * short + */ + void setSelectorTagCase(short selectorTagCase); + + void setClassSelectorCase(short classSelectorCase); + + void setIdSelectorCase(short idSelectorCase); +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/JSONCleanupStrategyImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/JSONCleanupStrategyImpl.java new file mode 100644 index 0000000000..e65559acf5 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/JSONCleanupStrategyImpl.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2004, 2010 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org/eclipse/wst/xml/core/internal/validation/core/AbstractNestedValidator.java + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.cleanup; + +import org.eclipse.core.runtime.Preferences; +import org.eclipse.wst.json.core.JSONCorePlugin; +import org.eclipse.wst.json.core.preferences.JSONCorePreferenceNames; + +public class JSONCleanupStrategyImpl implements IJSONCleanupStrategy { + + static private IJSONCleanupStrategy instance = null; + // initialize with defaults + protected short fIdentCase = ASIS; + protected short fPropNameCase = ASIS; + protected short fPropValueCase = ASIS; + protected short fSelectorTagCase = UPPER; + protected boolean fQuoteValues = true; + protected boolean fFormatSource = true; + protected short fClassCase = ASIS; + protected short fIdCase = ASIS; + + /** + * JSONCleanupStrategyImpl constructor comment. + */ + protected JSONCleanupStrategyImpl() { + super(); + initialize(); + } + + /** + * + * @return short + */ + public short getIdentCase() { + return fIdentCase; + } + + /** + * + * @return org.eclipse.wst.css.core.internal.cleanup.JSONCleanupStrategy + */ + public synchronized static IJSONCleanupStrategy getInstance() { + if (instance == null) + instance = new JSONCleanupStrategyImpl(); + return instance; + } + + /** + * + * @return short + */ + public short getPropNameCase() { + return fPropNameCase; + } + + /** + * + * @return short + */ + public short getPropValueCase() { + return fPropValueCase; + } + + /** + * + * @return short + */ + public short getSelectorTagCase() { + return fSelectorTagCase; + } + + /** + * + */ + private void initialize() { + Preferences prefs = JSONCorePlugin.getDefault().getPluginPreferences(); +// fIdentCase = getCleanupCaseValue(prefs +// .getInt(JSONCorePreferenceNames.CLEANUP_CASE_IDENTIFIER)); +// fPropNameCase = getCleanupCaseValue(prefs +// .getInt(JSONCorePreferenceNames.CLEANUP_CASE_PROPERTY_NAME)); +// fPropValueCase = getCleanupCaseValue(prefs +// .getInt(JSONCorePreferenceNames.CLEANUP_CASE_PROPERTY_VALUE)); +// fSelectorTagCase = getCleanupCaseValue(prefs +// .getInt(JSONCorePreferenceNames.CLEANUP_CASE_SELECTOR)); +// fIdCase = getCleanupCaseValue(prefs +// .getInt(JSONCorePreferenceNames.CLEANUP_CASE_ID_SELECTOR)); +// fClassCase = getCleanupCaseValue(prefs +// .getInt(JSONCorePreferenceNames.CLEANUP_CASE_CLASS_SELECTOR)); + fQuoteValues = prefs + .getBoolean(JSONCorePreferenceNames.QUOTE_ATTR_VALUES); + fFormatSource = prefs.getBoolean(JSONCorePreferenceNames.FORMAT_SOURCE); + } + + /** + * Return the JSONCleanupStrategy equivalent case short value when given an + * int + * + * @param value + * @return equivalent case short or ASIS if cannot be determined + */ + private short getCleanupCaseValue(int value) { + switch (value) { + case JSONCorePreferenceNames.LOWER: + return LOWER; + case JSONCorePreferenceNames.UPPER: + return UPPER; + } + return ASIS; + } + + /** + * + * @return boolean + */ + public boolean isFormatSource() { + return fFormatSource; + } + + /** + * + * @return boolean + */ + public boolean isQuoteValues() { + return fQuoteValues; + } + + /** + * + * @param formatSource + * boolean + */ + public void setFormatSource(boolean formatSource) { + fFormatSource = formatSource; + } + + /** + * + * @param identCase + * short + */ + public void setIdentCase(short identCase) { + fIdentCase = identCase; + } + + /** + * + * @param propNameCase + * short + */ + public void setPropNameCase(short propNameCase) { + fPropNameCase = propNameCase; + } + + /** + * + * @param propValueCase + * short + */ + public void setPropValueCase(short propValueCase) { + fPropValueCase = propValueCase; + } + + /** + * + * @param quoteValues + * boolean + */ + public void setQuoteValues(boolean quoteValues) { + fQuoteValues = quoteValues; + } + + /** + * + * @param selectorTagCase + * short + */ + public void setSelectorTagCase(short selectorTagCase) { + fSelectorTagCase = selectorTagCase; + } + + // TODO: a saveOptions should be added to JSONCleanupStrategy interface + public void saveOptions() { +// JSONCorePlugin +// .getDefault() +// .getPluginPreferences() +// .setValue(JSONCorePreferenceNames.CLEANUP_CASE_IDENTIFIER, +// fIdentCase); +// JSONCorePlugin +// .getDefault() +// .getPluginPreferences() +// .setValue(JSONCorePreferenceNames.CLEANUP_CASE_PROPERTY_NAME, +// fPropNameCase); +// JSONCorePlugin +// .getDefault() +// .getPluginPreferences() +// .setValue(JSONCorePreferenceNames.CLEANUP_CASE_PROPERTY_VALUE, +// fPropValueCase); +// JSONCorePlugin +// .getDefault() +// .getPluginPreferences() +// .setValue(JSONCorePreferenceNames.CLEANUP_CASE_SELECTOR, +// fSelectorTagCase); +// JSONCorePlugin +// .getDefault() +// .getPluginPreferences() +// .setValue(JSONCorePreferenceNames.CLEANUP_CASE_ID_SELECTOR, +// fIdCase); +// JSONCorePlugin +// .getDefault() +// .getPluginPreferences() +// .setValue(JSONCorePreferenceNames.CLEANUP_CASE_CLASS_SELECTOR, +// fClassCase); + JSONCorePlugin + .getDefault() + .getPluginPreferences() + .setValue(JSONCorePreferenceNames.QUOTE_ATTR_VALUES, + fQuoteValues); + JSONCorePlugin.getDefault().getPluginPreferences() + .setValue(JSONCorePreferenceNames.FORMAT_SOURCE, fFormatSource); + JSONCorePlugin.getDefault().savePluginPreferences(); + } + + public short getClassSelectorCase() { + return fClassCase; + } + + public short getIdSelectorCase() { + return fIdCase; + } + + public void setClassSelectorCase(short classSelectorCase) { + fClassCase = classSelectorCase; + } + + public void setIdSelectorCase(short idSelectorCase) { + fIdCase = idSelectorCase; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/contenttype/ContentTypeIdForJSON.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/contenttype/ContentTypeIdForJSON.java new file mode 100644 index 0000000000..4a7fb34a71 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/contenttype/ContentTypeIdForJSON.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2005 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.contenttype.ContentTypeIdForCSS + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.contenttype; + +/** + * This class, with its one field, is a convience to provide compile-time safety + * when refering to a contentType ID. The value of the contenttype id field must + * match what is specified in plugin.xml file. + */ + +public class ContentTypeIdForJSON { + /** + * The value of the contenttype id field must match what is specified in + * plugin.xml file. Note: this value is intentially set with default + * protected method so it will not be inlined. + */ + public final static String ContentTypeID_JSON = getConstantString(); + + /** + * Don't allow instantiation. + */ + private ContentTypeIdForJSON() { + super(); + } + + static String getConstantString() { + return "org.eclipse.wst.json.core.jsonsource"; //$NON-NLS-1$ + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONArray.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONArray.java new file mode 100644 index 0000000000..5ce93144fb --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONArray.java @@ -0,0 +1,22 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+/**
+ * JSON Array API.
+ *
+ */
+public interface IJSONArray extends IJSONStructure {
+
+ IJSONArray add(IJSONValue value);
+
+ IJSONArray remove(IJSONValue value);
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONBooleanValue.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONBooleanValue.java new file mode 100644 index 0000000000..b876740072 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONBooleanValue.java @@ -0,0 +1,19 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+/**
+ * JSON Boolean value API.
+ *
+ */
+public interface IJSONBooleanValue extends IJSONValue {
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONDocument.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONDocument.java new file mode 100644 index 0000000000..88d60a54ed --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONDocument.java @@ -0,0 +1,55 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+/**
+ * JSON Document API.
+ *
+ */
+public interface IJSONDocument extends IJSONNode {
+
+ /**
+ * Returns the SSE JSON model.
+ *
+ * @return the SSE JSON model.
+ */
+ IJSONModel getModel();
+
+ /**
+ * Create an instance of {@link IJSONObject}.
+ *
+ * @return an instance of {@link IJSONObject}.
+ */
+ IJSONObject createJSONObject();
+
+ /**
+ * Create an instance of {@link IJSONArray}.
+ *
+ * @return an instance of {@link IJSONArray}.
+ */
+ IJSONArray createJSONArray();
+
+ /**
+ * Create JSON pair name/value
+ *
+ * @param name
+ * @return an instance JSON pair name/value
+ */
+ IJSONPair createJSONPair(String name);
+
+ IJSONBooleanValue createBooleanValue();
+
+ IJSONNumberValue createNumberValue();
+
+ IJSONNullValue createNullValue();
+
+ IJSONStringValue createStringValue();
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONModel.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONModel.java new file mode 100644 index 0000000000..7de86efc65 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONModel.java @@ -0,0 +1,28 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+
+/**
+ * The SSE JSON model API.
+ *
+ */
+public interface IJSONModel extends IStructuredModel {
+
+ /**
+ * Returns the JSON Document.
+ *
+ * @return the JSON Document.
+ */
+ IJSONDocument getDocument();
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNode.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNode.java new file mode 100644 index 0000000000..9be3f65b0b --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNode.java @@ -0,0 +1,122 @@ +/**
+ * Copyright (c) 2013-2016 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+import org.eclipse.json.jsonpath.IJSONPath;
+import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
+import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+
+/**
+ * The JSON node API.
+ *
+ */
+public interface IJSONNode extends IndexedRegion, INodeNotifier {
+
+ short DOCUMENT_NODE = -1;
+ short OBJECT_NODE = 0;
+ short ARRAY_NODE = 1;
+ short PAIR_NODE = 2;
+
+ short VALUE_STRING_NODE = 3;
+ short VALUE_NUMBER_NODE = 4;
+ short VALUE_BOOLEAN_NODE = 5;
+ short VALUE_NULL_NODE = 6;
+
+ short OBJECT_KEY_NODE = 7;
+
+ /**
+ * Returns the structured document that underlies this node's model.
+ *
+ * Returns null if this node is not actively part of a source document. In
+ * contrast, in the pure DOM world, "owning document" is not null even after
+ * a node is deleted from the DOM.
+ *
+ * ISSUE: we need to fix our implementation to match this spec.
+ *
+ * @return the structured document.
+ */
+ IStructuredDocument getStructuredDocument();
+
+ /**
+ * Gets the last structured document region of this node.
+ *
+ * ISSUE: need to resolve getEnd/getLast confusion.
+ *
+ * @return IStructuredDocumentRegion - returns the last structured document
+ * region associated with
+ */
+ IStructuredDocumentRegion getEndStructuredDocumentRegion();
+
+ /**
+ * Gets the first structured document region of this node.
+ *
+ * ISSUE: need to resolve getFirst/getStart confusion
+ *
+ * @return the first structured document region of this node.
+ */
+ IStructuredDocumentRegion getFirstStructuredDocumentRegion();
+
+ /**
+ * Gets the last structured document region of this node.
+ *
+ * ISSUE: need to resolve getEnd/getLast confusion.
+ *
+ * @return IStructuredDocumentRegion - returns the last structured document
+ * region associated with
+ */
+ IStructuredDocumentRegion getLastStructuredDocumentRegion();
+
+ String getNodeName();
+
+ String getNodeValue() throws JSONException;
+
+ void setNodeValue(String paramString) throws JSONException;
+
+ IJSONNode removeChild(IJSONNode oldChild) throws JSONException;
+
+ IJSONNode cloneNode(boolean deep);
+
+ IJSONDocument getOwnerDocument();
+
+ IJSONNode getFirstChild();
+
+ IJSONNode getLastChild();
+
+ IJSONNode getPreviousSibling();
+
+ IJSONNode getNextSibling();
+
+ IJSONNode getParentNode();
+
+ short getNodeType();
+
+ IStructuredDocumentRegion getStartStructuredDocumentRegion();
+
+ IJSONNode insertBefore(IJSONNode node, IJSONNode next) throws JSONException;
+
+ /**
+ * Returns the model associated with this node. Returns null if not part of
+ * an active model.
+ *
+ * @return IDOMModel - returns the IDOMModel this node is part of.
+ */
+ IJSONModel getModel();
+
+ boolean hasChildNodes();
+
+ IJSONPair getOwnerPairNode();
+
+ IJSONNode getParentOrPairNode();
+
+ IJSONPath getPath();
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNullValue.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNullValue.java new file mode 100644 index 0000000000..996ed9df6d --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNullValue.java @@ -0,0 +1,15 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+public interface IJSONNullValue extends IJSONValue{
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNumberValue.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNumberValue.java new file mode 100644 index 0000000000..276837eae0 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNumberValue.java @@ -0,0 +1,15 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+public interface IJSONNumberValue extends IJSONValue{
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONObject.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONObject.java new file mode 100644 index 0000000000..a40f081b8b --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONObject.java @@ -0,0 +1,19 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+public interface IJSONObject extends IJSONStructure {
+
+ IJSONObject add(IJSONPair pair);
+
+ IJSONObject remove(IJSONPair pair);
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONPair.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONPair.java new file mode 100644 index 0000000000..b51d1307a2 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONPair.java @@ -0,0 +1,26 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+public interface IJSONPair extends IJSONStructure {
+
+ String getName();
+
+ IJSONValue getValue();
+
+ short getNodeValueType();
+
+ String getSimpleValue();
+
+ String getValueRegionType();
+
+ IJSONObject getOwnerObject();
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONStringValue.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONStringValue.java new file mode 100644 index 0000000000..1bc4c57ddf --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONStringValue.java @@ -0,0 +1,15 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+public interface IJSONStringValue extends IJSONValue{
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONStructure.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONStructure.java new file mode 100644 index 0000000000..ebfba6f773 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONStructure.java @@ -0,0 +1,23 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+/**
+ * JSON structure API.
+ *
+ */
+public interface IJSONStructure extends IJSONValue {
+
+ /**
+ * Returns true if the JSON structure is closed and false otherwise.
+ */
+ boolean isClosed();
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONValue.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONValue.java new file mode 100644 index 0000000000..fd9b4e4100 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONValue.java @@ -0,0 +1,19 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+public interface IJSONValue extends IJSONNode {
+
+ String getValueRegionType();
+
+ String getSimpleValue();
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/JSONException.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/JSONException.java new file mode 100644 index 0000000000..cbefce2815 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/JSONException.java @@ -0,0 +1,17 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.document;
+
+public class JSONException extends RuntimeException {
+
+ public static final short HIERARCHY_REQUEST_ERR = 3;
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/format/FormatProcessorJSON.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/format/FormatProcessorJSON.java new file mode 100644 index 0000000000..ac4eb11b50 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/format/FormatProcessorJSON.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.format.FormatProcessorCSS + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.format; + +import org.eclipse.jface.text.DocumentRewriteSession; +import org.eclipse.jface.text.DocumentRewriteSessionType; +import org.eclipse.jface.text.IDocumentExtension4; +import org.eclipse.jface.text.Region; +import org.eclipse.wst.json.core.document.IJSONDocument; +import org.eclipse.wst.json.core.document.IJSONModel; +import org.eclipse.wst.json.core.document.IJSONPair; +import org.eclipse.wst.json.core.internal.format.IJSONSourceFormatter; +import org.eclipse.wst.json.core.internal.format.JSONFormatUtil; +import org.eclipse.wst.json.core.internal.format.JSONSourceFormatterFactory; +import org.eclipse.wst.json.core.regions.JSONRegionContexts; +import org.eclipse.wst.sse.core.internal.format.AbstractStructuredFormatProcessor; +import org.eclipse.wst.sse.core.internal.format.IStructuredFormatPreferences; +import org.eclipse.wst.sse.core.internal.format.IStructuredFormatter; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.w3c.dom.Node; + +/** + * Format processor for JSON. + * + */ +public class FormatProcessorJSON extends AbstractStructuredFormatProcessor { + /* + * Max length of text to be formatted to be considered a "small change" Used + * for document rewrite session type. + */ + private final int MAX_SMALL_FORMAT_SIZE = 1000; + + @Override + protected String getFileExtension() { + return "json"; //$NON-NLS-1$ + } + + @Override + public void formatModel(IStructuredModel structuredModel, int start, + int length) { + JSONFormatUtil formatUtil = JSONFormatUtil.getInstance(); + if (structuredModel instanceof IJSONModel) { + // BUG102822 take advantage of IDocumentExtension4 + IDocumentExtension4 docExt4 = null; + if (structuredModel.getStructuredDocument() instanceof IDocumentExtension4) { + docExt4 = (IDocumentExtension4) structuredModel + .getStructuredDocument(); + } + DocumentRewriteSession rewriteSession = null; + + try { + DocumentRewriteSessionType rewriteType = (length > MAX_SMALL_FORMAT_SIZE) ? DocumentRewriteSessionType.UNRESTRICTED + : DocumentRewriteSessionType.UNRESTRICTED_SMALL; + rewriteSession = (docExt4 == null || docExt4 + .getActiveRewriteSession() != null) ? null : docExt4 + .startRewriteSession(rewriteType); + + IJSONDocument doc = ((IJSONModel) structuredModel) + .getDocument(); + IndexedRegion startRegion = ((IJSONModel) structuredModel).getIndexedRegion(start); + IndexedRegion endRegion = ((IJSONModel) structuredModel).getIndexedRegion(start + length); + if (startRegion != null && endRegion != null) { + start = startRegion.getStartOffset(); + int offset; + if (endRegion instanceof IJSONPair) { + offset = endRegion.getEndOffset(); + IStructuredDocumentRegion nextRegion = structuredModel.getStructuredDocument() + .getRegionAtCharacterOffset(offset + 1); + if (nextRegion.getType() == JSONRegionContexts.JSON_COMMA) { + offset = nextRegion.getEndOffset(); + } + } else { + offset = endRegion.getEndOffset(); + } + int end = offset - start; + IJSONSourceFormatter formatter = JSONSourceFormatterFactory + .getInstance().getSourceFormatter( + doc); + StringBuilder buf = formatter.format(doc, new Region(start, end)); + if (buf != null) { + formatUtil.replaceSource(doc.getModel(), start, end, buf.toString()); + } + } + } finally { + // BUG102822 take advantage of IDocumentExtension4 + if (docExt4 != null && rewriteSession != null) + docExt4.stopRewriteSession(rewriteSession); + } + } + } + + public IStructuredFormatPreferences getFormatPreferences() { + return null; + } + + @Override + protected IStructuredFormatter getFormatter(Node node) { + return null; + } + + @Override + protected void refreshFormatPreferences() { + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/JSONCoreMessages.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/JSONCoreMessages.java new file mode 100644 index 0000000000..c186ed9b42 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/JSONCoreMessages.java @@ -0,0 +1,57 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal;
+
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Messages for JSON Core Plugin.
+ *
+ */
+public class JSONCoreMessages extends NLS {
+
+ private static final String BUNDLE_NAME = "org.eclipse.wst.json.core.internal.JSONCoreMessages";//$NON-NLS-1$
+
+ private static ResourceBundle fResourceBundle;
+
+ public static String Missing_start_array;
+ public static String Missing_end_array;
+ public static String Missing_start_object;
+ public static String Missing_end_object;
+
+ public static String Catalog_entry_uri_not_set;
+ public static String Catalog_resolution_null_catalog;
+ public static String Catalog_resolution_malformed_url;
+ public static String Catalog_resolution_io_exception;
+
+ static {
+ // load message values from bundle file
+ NLS.initializeMessages(BUNDLE_NAME, JSONCoreMessages.class);
+ }
+
+ private JSONCoreMessages() {
+ // cannot create new instance
+ }
+
+ public static ResourceBundle getResourceBundle() {
+ try {
+ if (fResourceBundle == null) {
+ fResourceBundle = ResourceBundle.getBundle(BUNDLE_NAME);
+ }
+ } catch (MissingResourceException x) {
+ fResourceBundle = null;
+ }
+ return fResourceBundle;
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/JSONCoreMessages.properties b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/JSONCoreMessages.properties new file mode 100644 index 0000000000..b97d1e896d --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/JSONCoreMessages.properties @@ -0,0 +1,10 @@ +# Validation
+Missing_start_array=Missing start array
+Missing_end_array=Missing end array
+Missing_start_object=Missing start object
+Missing_end_object=Missing end object
+# Catalog
+Catalog_entry_uri_not_set=Catalog entry URI is not set
+Catalog_resolution_null_catalog=Catalog resolution attempted with null catalog; ignored
+Catalog_resolution_malformed_url=Malformed URL exception trying to resolve
+Catalog_resolution_io_exception=I/O exception trying to resolve
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/Logger.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/Logger.java new file mode 100644 index 0000000000..7ac16d1d9d --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/Logger.java @@ -0,0 +1,158 @@ +/*******************************************************************************
+ * Copyright (c) 2001, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Jens Lukowski/Innoopract - initial renaming/restructuring
+ * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.ui.internal.Logger
+ * modified in order to process JSON Objects.
+ *******************************************************************************/
+package org.eclipse.wst.json.core.internal;
+
+import com.ibm.icu.util.StringTokenizer;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.osgi.framework.Bundle;
+
+/**
+ * Small convenience class to log messages to plugin's log file and also, if
+ * desired, the console. This class should only be used by classes in this
+ * plugin. Other plugins should make their own copy, with appropriate ID.
+ */
+public class Logger {
+ private static final String PLUGIN_ID = "org.eclipse.wst.json.core"; //$NON-NLS-1$
+
+ public static final int ERROR = IStatus.ERROR; // 4
+ public static final int ERROR_DEBUG = 200 + ERROR;
+ public static final int INFO = IStatus.INFO; // 1
+ public static final int INFO_DEBUG = 200 + INFO;
+
+ public static final int OK = IStatus.OK; // 0
+
+ public static final int OK_DEBUG = 200 + OK;
+
+ private static final String TRACEFILTER_LOCATION = "/debug/tracefilter"; //$NON-NLS-1$
+ public static final int WARNING = IStatus.WARNING; // 2
+ public static final int WARNING_DEBUG = 200 + WARNING;
+
+ /**
+ * Adds message to log.
+ *
+ * @param level
+ * severity level of the message (OK, INFO, WARNING, ERROR,
+ * OK_DEBUG, INFO_DEBUG, WARNING_DEBUG, ERROR_DEBUG)
+ * @param message
+ * text to add to the log
+ * @param exception
+ * exception thrown
+ */
+ protected static void _log(int level, String message, Throwable exception) {
+ if (level == OK_DEBUG || level == INFO_DEBUG || level == WARNING_DEBUG || level == ERROR_DEBUG) {
+ if (!isDebugging())
+ return;
+ }
+
+ int severity = IStatus.OK;
+ switch (level) {
+ case INFO_DEBUG :
+ case INFO :
+ severity = IStatus.INFO;
+ break;
+ case WARNING_DEBUG :
+ case WARNING :
+ severity = IStatus.WARNING;
+ break;
+ case ERROR_DEBUG :
+ case ERROR :
+ severity = IStatus.ERROR;
+ }
+ message = (message != null) ? message : "null"; //$NON-NLS-1$
+ Status statusObj = new Status(severity, PLUGIN_ID, severity, message, exception);
+ Bundle bundle = Platform.getBundle(PLUGIN_ID);
+ if (bundle != null)
+ Platform.getLog(bundle).log(statusObj);
+ }
+
+ /**
+ * Prints message to log if category matches /debug/tracefilter option.
+ *
+ * @param message
+ * text to print
+ * @param category
+ * category of the message, to be compared with
+ * /debug/tracefilter
+ */
+ protected static void _trace(String category, String message, Throwable exception) {
+ if (isTracing(category)) {
+ message = (message != null) ? message : "null"; //$NON-NLS-1$
+ Status statusObj = new Status(IStatus.OK, PLUGIN_ID, IStatus.OK, message, exception);
+ Bundle bundle = Platform.getBundle(PLUGIN_ID);
+ if (bundle != null)
+ Platform.getLog(bundle).log(statusObj);
+ }
+ }
+
+ /**
+ * @return true if the platform is debugging
+ */
+ public static boolean isDebugging() {
+ return Platform.inDebugMode();
+ }
+
+ /**
+ * Determines if currently tracing a category
+ *
+ * @param category
+ * @return true if tracing category, false otherwise
+ */
+ public static boolean isTracing(String category) {
+ if (!isDebugging())
+ return false;
+
+ String traceFilter = Platform.getDebugOption(PLUGIN_ID + TRACEFILTER_LOCATION);
+ if (traceFilter != null) {
+ StringTokenizer tokenizer = new StringTokenizer(traceFilter, ","); //$NON-NLS-1$
+ while (tokenizer.hasMoreTokens()) {
+ String cat = tokenizer.nextToken().trim();
+ if (category.equals(cat)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static void log(int level, String message) {
+ _log(level, message, null);
+ }
+
+ public static void log(int level, String message, Throwable exception) {
+ _log(level, message, exception);
+ }
+
+ public static void logException(String message, Throwable exception) {
+ _log(ERROR, message, exception);
+ }
+
+ public static void logException(Throwable exception) {
+ _log(ERROR, exception.getMessage(), exception);
+ }
+
+ public static void trace(String category, String message) {
+ _trace(category, message, null);
+ }
+
+ public static void traceException(String category, String message, Throwable exception) {
+ _trace(category, message, exception);
+ }
+
+ public static void traceException(String category, Throwable exception) {
+ _trace(category, exception.getMessage(), exception);
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/ByteReader.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/ByteReader.java new file mode 100644 index 0000000000..9b284cbd88 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/ByteReader.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.contenttype.ByteReader + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.contenttype; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import org.eclipse.wst.sse.core.internal.encoding.CodedIO; + +/** + * 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 no 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 { + + + public static final int DEFAULT_BUFFER_SIZE = CodedIO.MAX_BUF_SIZE; + + 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) { + this.fInputStream = inputStream; + if (!inputStream.markSupported()) { + throw new IllegalArgumentException("ByteReader is required to have a resettable stream"); //$NON-NLS-1$ + } + this.fBuffer = new byte[size]; + + } + + public void close() throws IOException { + this.fInputStream.close(); + } + + public void mark(int readAheadLimit) { + this.fInputStream.mark(readAheadLimit); + } + + public boolean markSupported() { + return true; + } + + public int read() throws IOException { + int b0 = this.fInputStream.read(); + return (b0 & 0x00FF); + } + + public int read(char ch[], int offset, int length) throws IOException { + if (length > this.fBuffer.length) { + length = this.fBuffer.length; + } + + int count = this.fInputStream.read(this.fBuffer, 0, length); + + for (int i = 0; i < count; i++) { + int b0 = this.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[offset + i] = c0; + } + return count; + } + + public boolean ready() throws IOException { + return this.fInputStream.available() > 0; + } + + public void reset() throws IOException { + this.fInputStream.reset(); + } + + public long skip(long n) throws IOException { + return this.fInputStream.skip(n); + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/ContentDescriberForJSON.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/ContentDescriberForJSON.java new file mode 100644 index 0000000000..871dffb986 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/ContentDescriberForJSON.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.contenttype.ContentDescriberForCSS + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.contenttype; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; + +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.core.runtime.content.IContentDescriber; +import org.eclipse.core.runtime.content.IContentDescription; +import org.eclipse.core.runtime.content.ITextContentDescriber; +import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento; +import org.eclipse.wst.sse.core.internal.encoding.IContentDescriptionExtended; +import org.eclipse.wst.sse.core.internal.encoding.IResourceCharsetDetector; + +public final class ContentDescriberForJSON implements ITextContentDescriber { + final private static QualifiedName[] SUPPORTED_OPTIONS = { + IContentDescription.CHARSET, IContentDescription.BYTE_ORDER_MARK, + IContentDescriptionExtended.DETECTED_CHARSET, + IContentDescriptionExtended.UNSUPPORTED_CHARSET, + IContentDescriptionExtended.APPROPRIATE_DEFAULT }; + + public int describe(InputStream contents, IContentDescription description) + throws IOException { + int result = IContentDescriber.INDETERMINATE; + + if (description == null) { + result = computeValidity(contents); + } else { + calculateSupportedOptions(contents, description); + result = computeValidity(contents); + } + return result; + } + + public int describe(Reader contents, IContentDescription description) + throws IOException { + int result = IContentDescriber.INDETERMINATE; + + if (description == null) { + result = computeValidity(contents); + } else { + calculateSupportedOptions(contents, description); + result = computeValidity(contents); + } + return result; + } + + public QualifiedName[] getSupportedOptions() { + + return SUPPORTED_OPTIONS; + } + + private void calculateSupportedOptions(InputStream contents, + IContentDescription description) throws IOException { + if (isRelevent(description)) { + IResourceCharsetDetector detector = getDetector(); + detector.set(contents); + handleCalculations(description, detector); + } + } + + /** + * @param contents + * @param description + * @throws IOException + */ + private void calculateSupportedOptions(Reader contents, + IContentDescription description) throws IOException { + if (isRelevent(description)) { + IResourceCharsetDetector detector = getDetector(); + detector.set(contents); + handleCalculations(description, detector); + } + } + + private int computeValidity(InputStream inputStream) { + // currently no specific check for validilty + // based on contents. + return IContentDescriber.INDETERMINATE; + } + + private int computeValidity(Reader reader) { + // currently no specific check for validilty + // based on contents. + return IContentDescriber.INDETERMINATE; + } + + private IResourceCharsetDetector getDetector() { + return new JSONResourceEncodingDetector(); + } + + /** + * @param description + * @param detector + * @throws IOException + */ + private void handleCalculations(IContentDescription description, + IResourceCharsetDetector detector) throws IOException { + // note: if we're asked for one, we set them all. I need to be sure if + // called + // mulitiple times (one for each, say) that we don't waste time + // processing same + // content again. + EncodingMemento encodingMemento = ((JSONResourceEncodingDetector) detector) + .getEncodingMemento(); + // TODO: I need to verify to see if this BOM work is always done + // by text type. + Object detectedByteOrderMark = encodingMemento.getUnicodeBOM(); + if (detectedByteOrderMark != null) { + Object existingByteOrderMark = description + .getProperty(IContentDescription.BYTE_ORDER_MARK); + // not sure why would ever be different, so if is different, may + // need to "push" up into base. + if (!detectedByteOrderMark.equals(existingByteOrderMark)) + description.setProperty(IContentDescription.BYTE_ORDER_MARK, + detectedByteOrderMark); + } + + if (!encodingMemento.isValid()) { + // note: after setting here, its the mere presence of + // IContentDescriptionExtended.UNSUPPORTED_CHARSET + // in the resource's description that can be used to determine if + // invalid + // in those cases, the "detected" property contains an + // "appropriate default" to use. + description.setProperty( + IContentDescriptionExtended.UNSUPPORTED_CHARSET, + encodingMemento.getInvalidEncoding()); + description.setProperty( + IContentDescriptionExtended.APPROPRIATE_DEFAULT, + encodingMemento.getAppropriateDefault()); + } + + Object detectedCharset = encodingMemento.getDetectedCharsetName(); + Object javaCharset = encodingMemento.getJavaCharsetName(); + + // we always include detected, if its different than java + handleDetectedSpecialCase(description, detectedCharset, javaCharset); + + if (javaCharset != null) { + Object existingCharset = description + .getProperty(IContentDescription.CHARSET); + if (javaCharset.equals(existingCharset)) { + handleDetectedSpecialCase(description, detectedCharset, + javaCharset); + } else { + // we may need to add what we found, but only need to add + // if different from default.the + Object defaultCharset = getDetector().getSpecDefaultEncoding(); + if (defaultCharset != null) { + if (!defaultCharset.equals(javaCharset)) { + description.setProperty(IContentDescription.CHARSET, + javaCharset); + } + } else { + // assuming if there is no spec default, we always need to + // add, I'm assuming + description.setProperty(IContentDescription.CHARSET, + javaCharset); + } + } + } + + } + + private void handleDetectedSpecialCase(IContentDescription description, + Object detectedCharset, Object javaCharset) { + // since equal, we don't need to add, but if our detected version is + // different than javaCharset, then we should add it. This will + // happen, for example, if there's differences in case, or differences + // due to override properties + if (detectedCharset != null) { + + // Once we detected a charset, we should set the property even + // though it's the same as javaCharset + // because there are clients that rely on this property to + // determine if the charset is actually detected in file or not. + description.setProperty( + IContentDescriptionExtended.DETECTED_CHARSET, + detectedCharset); + } + } + + /** + * @param description + * @return + */ + private boolean isRelevent(IContentDescription description) { + boolean result = false; + if (description == null) + result = false; + else if (description.isRequested(IContentDescription.BYTE_ORDER_MARK)) + result = true; + else if (description.isRequested(IContentDescription.CHARSET)) + result = true; + else if (description + .isRequested(IContentDescriptionExtended.APPROPRIATE_DEFAULT)) + result = true; + else if (description + .isRequested(IContentDescriptionExtended.DETECTED_CHARSET)) + result = true; + else if (description + .isRequested(IContentDescriptionExtended.UNSUPPORTED_CHARSET)) + result = true; + // else if + // (description.isRequested(IContentDescriptionExtended.ENCODING_MEMENTO)) + // result = true; + return result; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/JSONResourceEncodingDetector.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/JSONResourceEncodingDetector.java new file mode 100644 index 0000000000..f8a4a38e45 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/JSONResourceEncodingDetector.java @@ -0,0 +1,115 @@ +/*******************************************************************************
+ * Copyright (c) 2004, 2008 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
+ * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.contenttype.CSSResourceEncodingDetector
+ * modified in order to process JSON Objects.
+ *******************************************************************************/
+package org.eclipse.wst.json.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.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.wst.sse.core.internal.encoding.CodedIO;
+import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento;
+import org.eclipse.wst.sse.core.internal.encoding.IResourceCharsetDetector;
+import org.eclipse.wst.sse.core.internal.encoding.NonContentBasedEncodingRules;
+
+public class JSONResourceEncodingDetector implements IResourceCharsetDetector {
+
+ private EncodingMemento fEncodingMemento;
+ private boolean fHeaderParsed;
+ private Reader fReader;
+
+ class NullMemento extends EncodingMemento {
+ /**
+ *
+ */
+ public NullMemento() {
+ super();
+ String defaultCharset = NonContentBasedEncodingRules
+ .useDefaultNameRules(null);
+ setJavaCharsetName(defaultCharset);
+ setAppropriateDefault(defaultCharset);
+ setDetectedCharsetName(null);
+ }
+ }
+
+ @Override
+ public String getEncoding() throws IOException {
+ return getEncodingMemento().getDetectedCharsetName();
+ }
+
+ public EncodingMemento getEncodingMemento() throws IOException {
+ if (fEncodingMemento == null) {
+ // safty net
+ fEncodingMemento = new NullMemento();
+ }
+ return fEncodingMemento;
+ }
+
+ @Override
+ public String getSpecDefaultEncoding() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ private void resetAll() {
+ fReader = null;
+ fHeaderParsed = false;
+ fEncodingMemento = null;
+ }
+
+ @Override
+ public void set(InputStream inputStream) {
+ resetAll();
+ fReader = new ByteReader(inputStream);
+ try {
+ fReader.mark(CodedIO.MAX_MARK_SIZE);
+ } catch (IOException e) {
+ // impossible, since we know ByteReader
+ // supports marking
+ throw new Error(e);
+ }
+ }
+
+ @Override
+ public void set(Reader reader) {
+ resetAll();
+ fReader = reader;
+ if (!fReader.markSupported()) {
+ fReader = new BufferedReader(fReader);
+ }
+ try {
+ fReader.mark(CodedIO.MAX_MARK_SIZE);
+ } catch (IOException e) {
+ // impossble, since we just checked if markable
+ throw new Error(e);
+ }
+ }
+
+ @Override
+ public void set(IStorage iStorage) throws CoreException {
+ resetAll();
+ InputStream inputStream = iStorage.getContents();
+ InputStream resettableStream = new BufferedInputStream(inputStream,
+ CodedIO.MAX_BUF_SIZE);
+ resettableStream.mark(CodedIO.MAX_MARK_SIZE);
+ set(resettableStream);
+ // TODO we'll need to "remember" IFile, or
+ // get its (or its project's) settings, in case
+ // those are needed to handle cases when the
+ // encoding is not in the file stream.
+ }
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/ISourceGenerator.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/ISourceGenerator.java new file mode 100644 index 0000000000..71622fd17c --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/ISourceGenerator.java @@ -0,0 +1,24 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.document;
+
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONObject;
+
+public interface ISourceGenerator {
+
+ String generateStartTag(IJSONObject element);
+
+ String generateEndTag(IJSONObject element);
+
+ String generateSource(IJSONNode node);
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONArrayImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONArrayImpl.java new file mode 100644 index 0000000000..38d160adc9 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONArrayImpl.java @@ -0,0 +1,297 @@ +/**
+ * Copyright (c) 2016 Angelo ZERR 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ * Alina Marin <alina@mx1.ibm.com> - fixed some stuff to improve the synch between the editor and the model.
+ */
+package org.eclipse.wst.json.core.internal.document;
+
+import org.eclipse.wst.json.core.document.IJSONArray;
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONValue;
+import org.eclipse.wst.json.core.document.JSONException;
+
+public class JSONArrayImpl extends JSONStructureImpl implements IJSONArray {
+
+ public JSONArrayImpl() {
+ }
+
+ public JSONArrayImpl(JSONArrayImpl object) {
+ super(object);
+ }
+
+ @Override
+ public IJSONNode cloneNode(boolean deep) {
+ JSONArrayImpl cloned = new JSONArrayImpl(this);
+
+ if (deep)
+ cloneChildNodes(cloned, deep);
+
+ return cloned;
+ }
+
+ @Override
+ public short getNodeType() {
+ return IJSONNode.ARRAY_NODE;
+ }
+
+ @Override
+ public String getNodeName() {
+ return "array";
+ }
+
+ @Override
+ public String getNodeValue() throws JSONException {
+ return null;
+ }
+
+ @Override
+ public IJSONArray add(IJSONValue value) {
+ if (value == null || value.getOwnerPairNode() != null)
+ return null;
+ JSONValueImpl attr = (JSONValueImpl) value;
+ attr.setOwnerPairNode(this.getOwnerPairNode());
+ attr.setParentNode(this);
+ this.notifyChildReplaced(value, null);
+ return this;
+ }
+
+ @Override
+ public IJSONArray remove(IJSONValue value) {
+ if (value == null || value.getOwnerPairNode() != null)
+ return null;
+ this.notifyChildReplaced(null, value);
+ return this;
+ }
+
+ // @Override
+ // public boolean getBoolean(int paramInt) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public boolean getBoolean(int paramInt, boolean paramBoolean) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public int getInt(int paramInt) {
+ // // TODO Auto-generated method stub
+ // return 0;
+ // }
+ //
+ // @Override
+ // public int getInt(int paramInt1, int paramInt2) {
+ // // TODO Auto-generated method stub
+ // return 0;
+ // }
+ //
+ // @Override
+ // public JsonArray getJsonArray(int paramInt) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public JsonNumber getJsonNumber(int paramInt) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public JsonObject getJsonObject(int paramInt) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public JsonString getJsonString(int paramInt) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public String getString(int paramInt) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public String getString(int paramInt, String paramString) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public <T extends JsonValue> List<T> getValuesAs(Class<T> paramClass) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public boolean isNull(int paramInt) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public ValueType getValueType() {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public boolean add(JsonValue arg0) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public void add(int arg0, JsonValue arg1) {
+ // // TODO Auto-generated method stub
+ //
+ // }
+ //
+ // @Override
+ // public boolean addAll(Collection<? extends JsonValue> arg0) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public boolean addAll(int arg0, Collection<? extends JsonValue> arg1) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public void clear() {
+ // // TODO Auto-generated method stub
+ //
+ // }
+ //
+ // @Override
+ // public boolean contains(Object arg0) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public boolean containsAll(Collection<?> arg0) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public JsonValue get(int arg0) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public int indexOf(Object arg0) {
+ // // TODO Auto-generated method stub
+ // return 0;
+ // }
+ //
+ // @Override
+ // public boolean isEmpty() {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public Iterator<JsonValue> iterator() {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public int lastIndexOf(Object arg0) {
+ // // TODO Auto-generated method stub
+ // return 0;
+ // }
+ //
+ // @Override
+ // public ListIterator<JsonValue> listIterator() {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public ListIterator<JsonValue> listIterator(int arg0) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public boolean remove(Object arg0) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public JsonValue remove(int arg0) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public boolean removeAll(Collection<?> arg0) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public boolean retainAll(Collection<?> arg0) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public JsonValue set(int arg0, JsonValue arg1) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public int size() {
+ // // TODO Auto-generated method stub
+ // return 0;
+ // }
+ //
+ // @Override
+ // public List<JsonValue> subList(int arg0, int arg1) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public Object[] toArray() {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public <T> T[] toArray(T[] arg0) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+
+ @Override
+ public String getSimpleValue() {
+ return null;
+ }
+
+ @Override
+ public String getValueRegionType() {
+ return null;
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONBooleanValueImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONBooleanValueImpl.java new file mode 100644 index 0000000000..521b35cb2f --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONBooleanValueImpl.java @@ -0,0 +1,42 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.document;
+
+import org.eclipse.wst.json.core.document.IJSONBooleanValue;
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.JSONException;
+
+public class JSONBooleanValueImpl extends JSONStructureImpl implements
+ IJSONBooleanValue {
+
+ @Override
+ public short getNodeType() {
+ return VALUE_BOOLEAN_NODE;
+ }
+
+ @Override
+ public String getNodeName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getNodeValue() throws JSONException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public IJSONNode cloneNode(boolean deep) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONDocumentImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONDocumentImpl.java new file mode 100644 index 0000000000..a7e46e06e2 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONDocumentImpl.java @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2013-2016 Angelo ZERR. + * 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: + * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation + */ +package org.eclipse.wst.json.core.internal.document; + +import org.eclipse.wst.json.core.document.IJSONArray; +import org.eclipse.wst.json.core.document.IJSONBooleanValue; +import org.eclipse.wst.json.core.document.IJSONDocument; +import org.eclipse.wst.json.core.document.IJSONModel; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONNullValue; +import org.eclipse.wst.json.core.document.IJSONNumberValue; +import org.eclipse.wst.json.core.document.IJSONObject; +import org.eclipse.wst.json.core.document.IJSONPair; +import org.eclipse.wst.json.core.document.IJSONStringValue; +import org.eclipse.wst.json.core.document.JSONException; + +public class JSONDocumentImpl extends JSONStructureImpl implements + IJSONDocument { + + private JSONModelImpl fModel = null; + + JSONDocumentImpl() { + super(); + setOwnerDocument(this);
+ } + + JSONDocumentImpl(JSONDocumentImpl that) { + super(that); + setOwnerDocument(this);
+ } + + @Override
+ public IJSONModel getModel() { + return fModel; + } + + void setModel(JSONModelImpl model) { + this.fModel = model; + } + + @Override + public IJSONObject createJSONObject() { + JSONObjectImpl object = new JSONObjectImpl(); + object.setOwnerDocument(this); + return object; + } + + @Override + public IJSONArray createJSONArray() { + JSONArrayImpl array = new JSONArrayImpl(); + array.setOwnerDocument(this); + return array; + } + + @Override + public IJSONPair createJSONPair(String name) { + JSONPairImpl pair = new JSONPairImpl(); + pair.setOwnerDocument(this); + pair.setName(name); + return pair; + } + + @Override + public IJSONBooleanValue createBooleanValue() { + JSONBooleanValueImpl value = new JSONBooleanValueImpl(); + value.setOwnerDocument(this); + return value; + } + + @Override + public IJSONNumberValue createNumberValue() { + JSONNumberValueImpl value = new JSONNumberValueImpl(); + value.setOwnerDocument(this); + return value; + } + + @Override + public IJSONNullValue createNullValue() { + JSONNullValueImpl value = new JSONNullValueImpl(); + value.setOwnerDocument(this); + return value; + } + + @Override + public IJSONStringValue createStringValue() { + JSONStringValueImpl value = new JSONStringValueImpl(); + value.setOwnerDocument(this); + return value; + } + + @Override + public short getNodeType() { + return DOCUMENT_NODE; + } + + @Override + public String getNodeName() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getNodeValue() throws JSONException { + // TODO Auto-generated method stub + return null; + } + + @Override + public IJSONNode cloneNode(boolean deep) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONGeneratorImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONGeneratorImpl.java new file mode 100644 index 0000000000..79e273554f --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONGeneratorImpl.java @@ -0,0 +1,39 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.document;
+
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONObject;
+
+public class JSONGeneratorImpl implements ISourceGenerator {
+
+ private static final ISourceGenerator INSTANCE = new JSONGeneratorImpl();
+
+ public static ISourceGenerator getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public String generateStartTag(IJSONObject element) {
+ return "{";
+ }
+
+ @Override
+ public String generateEndTag(IJSONObject element) {
+ return "}";
+ }
+
+ @Override
+ public String generateSource(IJSONNode node) {
+ return null;
+ }
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelContext.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelContext.java new file mode 100644 index 0000000000..665711718d --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelContext.java @@ -0,0 +1,285 @@ +/******************************************************************************* + * Copyright (c) 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.XMLModelContext + * modified in order to process JSON Objects. + * Alina Marin <alina@mx1.ibm.com> - fixed some stuff to improve the synch between the editor and the model. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + +import org.eclipse.wst.json.core.document.IJSONArray; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONObject; +import org.eclipse.wst.json.core.document.IJSONStructure; +import org.eclipse.wst.json.core.internal.document.JSONObjectImpl; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.Text; + +/** + * JSONModelContext class + */ +class JSONModelContext { + private IJSONNode nextNode = null; + private IJSONNode currentNode = null; + private IJSONNode parentNode = null; + + // private JSONModelImpl model = null; + private IJSONNode rootNode = null; + + /** + * JSONModelContext constructor + * + * @param rootNode + * org.w3c.dom.Node + */ + JSONModelContext(IJSONNode 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 (IJSONNode parent = this.parentNode.getParentNode(); parent != null; + // parent = parent + // .getParentNode()) { + // if (parent.getNodeType() != IJSONNode.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 + */ + // IJSONObject findStartTag(String tagName, String rootName) { + // if (tagName == null) + // return null; + // + // // check previous for empty content element + // IJSONNode prev = null; + // if (this.nextNode != null) + // prev = this.nextNode.getPreviousSibling(); + // else if (this.parentNode != null) + // prev = this.parentNode.getLastChild(); + // if (prev != null && prev.getNodeType() == IJSONNode.OBJECT_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; + // } + + IJSONObject findParentObject() { + if (parentNode != null + && parentNode.getNodeType() == IJSONNode.OBJECT_NODE) { + return (IJSONObject) parentNode; + } + return null; + } + + IJSONArray findParentArray() { + if (parentNode != null + && parentNode.getNodeType() == IJSONNode.ARRAY_NODE) { + return (IJSONArray) parentNode; + } + return null; + } + + IJSONStructure findParentStructure() { + if (parentNode != null + && (parentNode.getNodeType() == IJSONNode.OBJECT_NODE || parentNode + .getNodeType() == IJSONNode.ARRAY_NODE)) { + return (IJSONStructure) parentNode; + } + return null; + } + + /** + * getCurrentNode method + * + * @return org.w3c.dom.Node + */ + IJSONNode getCurrentNode() { + return this.currentNode; + } + + /** + * getNextNode method + * + * @return org.w3c.dom.Node + */ + IJSONNode getNextNode() { + return this.nextNode; + } + + /** + * getParentNode method + * + * @return org.w3c.dom.Node + */ + IJSONNode getParentNode() { + return this.parentNode; + } + + /** + * getRootNode method + * + * @return org.w3c.dom.Node + */ + IJSONNode getRootNode() { + return this.rootNode; + } + + /** + * setLast method + */ + void setLast() { + if (this.parentNode == null) + return; + if (this.nextNode != null) { + IJSONNode prev = this.nextNode.getPreviousSibling(); + if (prev == null || prev.getNodeType() != IJSONNode.OBJECT_NODE) + return; + JSONObjectImpl element = (JSONObjectImpl) prev; + if (element.hasEndTag() || !element.isContainer() + || element.isEmptyTag()) + return; + setParentNode(prev); + } + + // find last open parent + IJSONNode parent = this.parentNode; + IJSONNode last = parent.getLastChild(); + while (last != null) { + if (last.getNodeType() != IJSONNode.OBJECT_NODE) + break; + JSONObjectImpl element = (JSONObjectImpl) 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 setCurrentNode(IJSONNode currentNode) { + this.currentNode = currentNode; + if (currentNode == null) + return; + this.parentNode = currentNode.getParentNode(); + this.nextNode = currentNode.getNextSibling(); + } + + /** + * setParentNode method + * + * @param parentNode + * org.w3c.dom.Node + */ + void setParentNode(IJSONNode parentNode) { + this.parentNode = parentNode; + this.nextNode = null; + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelImpl.java new file mode 100644 index 0000000000..0ce7ada54c --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelImpl.java @@ -0,0 +1,869 @@ +/******************************************************************************* + * Copyright (c) 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.DOMModelImpl + * modified in order to process JSON Objects. + * Alina Marin <alina@mx1.ibm.com> - fixed some stuff to improve the synch between the editor and the model. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + +import org.eclipse.wst.json.core.document.IJSONArray; +import org.eclipse.wst.json.core.document.IJSONDocument; +import org.eclipse.wst.json.core.document.IJSONModel; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONObject; +import org.eclipse.wst.json.core.document.IJSONPair; +import org.eclipse.wst.json.core.document.IJSONValue; +import org.eclipse.wst.json.core.internal.Logger; +import org.eclipse.wst.json.core.regions.JSONRegionContexts; +import org.eclipse.wst.sse.core.internal.model.AbstractStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.internal.provisional.events.IStructuredDocumentListener; +import org.eclipse.wst.sse.core.internal.provisional.events.NewDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.NoChangeEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionsReplacedEvent; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentRegionsReplacedEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; +import org.w3c.dom.Document; + +/** + * SSE {@link IStructuredDocument} implementation for JSON. + */ +public class JSONModelImpl extends AbstractStructuredModel implements + IStructuredDocumentListener, IJSONModel { + + private static String TRACE_PARSER_MANAGEMENT_EXCEPTION = "parserManagement"; //$NON-NLS-1$ + private Object active = null; + private JSONDocumentImpl document = null; + private ISourceGenerator generator = null; + private JSONModelNotifier notifier = null; + private JSONModelParser parser = null; + private boolean refresh = false; + private JSONModelUpdater updater = null; + + /** + * JSONModelImpl constructor + */ + public JSONModelImpl() { + super(); + this.document = (JSONDocumentImpl) 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 listeners 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() { + JSONModelNotifier notifier = getModelNotifier(); + notifier.cancelPending(); + super.aboutToReinitializeModel(); + } + + protected void pairReplaced(IJSONObject element, IJSONPair newAttr, + IJSONPair oldAttr) { + if (element == null) + return; + if (getActiveParser() == null) { + JSONModelUpdater updater = getModelUpdater(); + setActive(updater); + updater.initialize(); + updater.replaceAttr(element, newAttr, oldAttr); + setActive(null); + } + getModelNotifier().pairReplaced(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(); + // we null out here to avoid spurious"warning" message while debug + // tracing is enabled + fLockObject = null; + // 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(IJSONNode parentNode, IJSONNode newChild, + IJSONNode oldChild) { + if (parentNode == null) + return; + if (getActiveParser() == null) { + JSONModelUpdater updater = getModelUpdater(); + setActive(updater); + updater.initialize(); + updater.replaceChild(parentNode, newChild, oldChild); + setActive(null); + } + getModelNotifier().childReplaced(parentNode, newChild, oldChild); + } + + /** + */ + // protected void documentTypeChanged() { + // if (this.refresh) + // return; + // // unlike 'resfresh', 'reinitialize' finishes loop + // // and flushes remaining notification que before + // // actually reinitializing. + // // ISSUE: should reinit be used instead of handlerefresh? + // // this.setReinitializeNeeded(true); + // if (this.active != null || getModelNotifier().isChanging()) + // return; // defer + // handleRefresh(); + // } + + // protected void editableChanged(Node node) { + // if (node != null) { + // getModelNotifier().editableChanged(node); + // } + // } + + /** + */ + // protected void endTagChanged(IJSONObject element) { + // if (element == null) + // return; + // if (getActiveParser() == null) { + // JSONModelUpdater updater = getModelUpdater(); + // setActive(updater); + // updater.initialize(); + // // updater.changeEndTag(element); + // setActive(null); + // } + // getModelNotifier().endTagChanged(element); + // } + + /** + */ + private JSONModelParser getActiveParser() { + if (this.parser == null) + return null; + if (this.parser != this.active) + return null; + return this.parser; + } + + /** + */ + private JSONModelUpdater getActiveUpdater() { + if (this.updater == null) + return null; + if (this.updater != this.active) + return null; + return this.updater; + } + + @Override + public Object getAdapter(Class adapter) { + if (Document.class.equals(adapter)) + return getDocument(); + return super.getAdapter(adapter); + } + + @Override + public IJSONDocument getDocument() { + return this.document; + } + + public ISourceGenerator getGenerator() { + if (this.generator == null) { + this.generator = JSONGeneratorImpl.getInstance(); + } + return this.generator; + } + + @Override + public IndexedRegion getIndexedRegion(int offset) { + if (this.document == null) + return null; + // search in document children + IJSONNode parent = null; + int length = this.document.getEndOffset(); + if (offset * 2 < length) { + // search from the first + IJSONNode child = (IJSONNode) this.document.getFirstChild(); + while (child != null) { + if (child.getEndOffset() <= offset) { + child = (IJSONNode) 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) { + if (child instanceof IJSONPair) { + IJSONValue value = ((IJSONPair)child).getValue(); + if (value instanceof IJSONObject || value instanceof IJSONArray) { + if (value.getStartOffset() < offset) { + child = value; + continue; + } + } + } + return child; + } + } + // dig more + parent = child; + if (parent != null + && parent.getNodeType() == IJSONNode.PAIR_NODE) { + IJSONPair pair = (IJSONPair) parent; + child = pair.getValue(); + } else { + child = (IJSONNode) parent.getFirstChild(); + } + } + } else { + // search from the last + IJSONNode child = (IJSONNode) this.document.getLastChild(); + while (child != null) { + if (child.getStartOffset() > offset) { + child = (IJSONNode) 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) { + if (child instanceof IJSONPair) { + IJSONValue value = ((IJSONPair)child).getValue(); + if (value instanceof IJSONObject || value instanceof IJSONArray) { + if (value.getStartOffset() < offset) { + child = value; + continue; + } + } + } + return child; + } + } + // dig more + parent = child; + if (parent != null + && parent.getNodeType() == IJSONNode.PAIR_NODE) { + IJSONPair pair = (IJSONPair) parent; + child = pair.getValue(); + } else { + child = (IJSONNode) parent.getLastChild(); + } + } + } + return parent != null ? parent : document.getFirstChild(); + } + + /** + */ + public JSONModelNotifier getModelNotifier() { + if (this.notifier == null) { + this.notifier = new JSONModelNotifierImpl(); + } + return this.notifier; + } + + /** + */ + private JSONModelParser getModelParser() { + if (this.parser == null) { + this.parser = createModelParser(); + } + return this.parser; + } + + protected JSONModelParser createModelParser() { + return new JSONModelParser(this); + } + + /** + */ + private JSONModelUpdater getModelUpdater() { + if (this.updater == null) { + this.updater = createModelUpdater(); + } + return this.updater; + } + + protected JSONModelUpdater createModelUpdater() { + return new JSONModelUpdater(this); + } + + /** + */ + private void handleRefresh() { + if (!this.refresh) + return; + JSONModelNotifier notifier = getModelNotifier(); + boolean isChanging = notifier.isChanging(); + if (!isChanging) + notifier.beginChanging(true); + JSONModelParser parser = getModelParser(); + setActive(parser); + this.document.removeChildNodes(); + try { + this.refresh = false; + parser.replaceStructuredDocumentRegions(getStructuredDocument() + .getRegionList(), null); + } catch (Exception ex) { + Logger.logException(ex); + } finally { + setActive(null); + if (!isChanging) + notifier.endChanging(); + } + } + + protected IJSONDocument internalCreateDocument() { + JSONDocumentImpl document = new JSONDocumentImpl(); + document.setModel(this); + return document; + } + + boolean isReparsing() { + return (active != null); + } + + @Override + 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 (fStructuredDocument != null + && fStructuredDocument != structuredDocument) + setStructuredDocument(structuredDocument); + + internalSetNewDocument(structuredDocument); + } + + private void internalSetNewDocument(IStructuredDocument structuredDocument) { + if (structuredDocument == null) + return; + IStructuredDocumentRegionList flatNodes = structuredDocument + .getRegionList(); + if ((flatNodes == null) || (flatNodes.getLength() == 0)) { + return; + } + if (this.document == null) + return; // being constructed + + JSONModelUpdater 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; + } + JSONModelNotifier notifier = getModelNotifier(); + boolean isChanging = notifier.isChanging(); + // call even if changing to notify doing new model + getModelNotifier().beginChanging(true); + JSONModelParser 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; + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.provisional.events. + * IStructuredDocumentListener + * #noChange(org.eclipse.wst.sse.core.internal.provisional + * .events.NoChangeEvent) + */ + @Override + public void noChange(NoChangeEvent event) { + JSONModelUpdater 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; + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.provisional.events. + * IStructuredDocumentListener + * #nodesReplaced(org.eclipse.wst.sse.core.internal + * .provisional.events.StructuredDocumentRegionsReplacedEvent) + */ + @Override + public void nodesReplaced(StructuredDocumentRegionsReplacedEvent event) { + if (event == null) + return; + IStructuredDocumentRegionList oldStructuredDocumentRegions = event + .getOldStructuredDocumentRegions(); + IStructuredDocumentRegionList newStructuredDocumentRegions = event + .getNewStructuredDocumentRegions(); + JSONModelUpdater updater = getActiveUpdater(); + if (updater != null) { // being updated + try { + updater.replaceStructuredDocumentRegions( + newStructuredDocumentRegions, + oldStructuredDocumentRegions); + } catch (Exception ex) { + Logger.logException(ex); + this.refresh = true; + handleRefresh(); + } finally { + setActive(null); + } + // checkForReinit(); + return; + } + JSONModelNotifier notifier = getModelNotifier(); + boolean isChanging = notifier.isChanging(); + if (!isChanging) + notifier.beginChanging(); + JSONModelParser parser = getModelParser(); + setActive(parser); + try { + /* workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=486860 */ +// this.refresh = true; +// handleRefresh(); + boolean reloadModel = false; + // Check if the insertion is between two previously existing JSON Nodes, in that case + // the model is reloaded completely. + if (newStructuredDocumentRegions != null) { + int newCount = newStructuredDocumentRegions.getLength(); + for (int i = 0; i < newCount -1; i++) { + if (newStructuredDocumentRegions.item(i).getType().equals(JSONRegionContexts.JSON_COMMA)) { + IStructuredDocumentRegion nextNode = newStructuredDocumentRegions.item(i).getNext(); + while (nextNode != null) { + if (nextNode.getType().equals(JSONRegionContexts.JSON_OBJECT_KEY) + || nextNode.getType().equals(JSONRegionContexts.JSON_OBJECT_OPEN) + || nextNode.getType().equals(JSONRegionContexts.JSON_ARRAY_OPEN)) { + reloadModel = true; + break; + } + nextNode = nextNode.getNext(); + } + } + } + } + if (!reloadModel && oldStructuredDocumentRegions != null && oldStructuredDocumentRegions.getLength() > 0) { + if (oldStructuredDocumentRegions.getLength() > 3 || oldStructuredDocumentRegions.item(0).getType().equals(JSONRegionContexts.JSON_OBJECT_OPEN)) { + // Reload all the model when the first region that will be + // replaced is a JSONObject or if more than 3 regions are + // replaced in the model (a JSONPair is composed by 3 regions, + // this means more that one JSON Pair are replaced in the model) + + reloadModel = true; + } else { + // also, always reload the model in case of removing at least one UNDEFINED region + for (int i = 0; !reloadModel && i < oldStructuredDocumentRegions.getLength(); i++) { + if (oldStructuredDocumentRegions.item(i).getType().equals(JSONRegionContexts.UNDEFINED)) { + reloadModel = true; + } + } + } + } + if(reloadModel) { + this.refresh = true; + handleRefresh(); + } + else { + parser.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); + if (!isChanging) { + notifier.endChanging(); + handleRefresh(); + } + } + + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.provisional.events. + * IStructuredDocumentListener + * #regionChanged(org.eclipse.wst.sse.core.internal + * .provisional.events.RegionChangedEvent) + */ + @Override + 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; + JSONModelUpdater updater = getActiveUpdater(); + if (updater != null) { // being updated + try { + updater.changeRegion(event, flatNode, region); + } catch (Exception ex) { + Logger.logException(ex); + this.refresh = true; + handleRefresh(); + } finally { + setActive(null); + } + // checkForReinit(); + return; + } + JSONModelNotifier notifier = getModelNotifier(); + boolean isChanging = notifier.isChanging(); + if (!isChanging) + notifier.beginChanging(); + JSONModelParser parser = getModelParser(); + setActive(parser); + try { + parser.changeRegion(event, flatNode, region); + /* workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=486860 */ +// this.refresh = true; +// handleRefresh(); + } catch (Exception ex) { + Logger.logException(ex); + this.refresh = true; + handleRefresh(); + } finally { + setActive(null); + if (!isChanging) { + notifier.endChanging(); + handleRefresh(); + } + } + // checkForReinit(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.wst.sse.core.internal.provisional.events. + * IStructuredDocumentListener + * #regionsReplaced(org.eclipse.wst.sse.core.internal + * .provisional.events.RegionsReplacedEvent) + */ + @Override + 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; + JSONModelUpdater 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; + } + JSONModelNotifier notifier = getModelNotifier(); + boolean isChanging = notifier.isChanging(); + if (!isChanging) + notifier.beginChanging(); + JSONModelParser parser = getModelParser(); + setActive(parser); + try { + /* workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=486860 */ +// this.refresh = true; +// handleRefresh(); + boolean reloadModel = false; + // Check if the insertion is between two previously existing JSON Nodes, in that case + // the model is reloaded completely. + + if (flatNode.getType().equals(JSONRegionContexts.JSON_COMMA)) { + IStructuredDocumentRegion nextNode = flatNode.getNext(); + while (nextNode != null) { + if (nextNode.getType().equals(JSONRegionContexts.JSON_OBJECT_KEY) + || nextNode.getType().equals(JSONRegionContexts.JSON_OBJECT_OPEN) + || nextNode.getType().equals(JSONRegionContexts.JSON_ARRAY_OPEN)) + reloadModel = true; + nextNode = nextNode.getNext(); + } + } + if(reloadModel) { + this.refresh = true; + handleRefresh(); + } else { + 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(ISourceGenerator generator) { + // this.generator = generator; + // } + + /** + */ + public void setModelNotifier(JSONModelNotifier notifier) { + this.notifier = notifier; + } + + /** + */ + public void setModelParser(JSONModelParser parser) { + this.parser = parser; + } + + /** + */ + public void setModelUpdater(JSONModelUpdater updater) { + this.updater = updater; + } + + /** + * setStructuredDocument method + * + * @param structuredDocument + */ + 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 != null) { + internalSetNewDocument(structuredDocument); + structuredDocument.addDocumentChangingListener(this); + } + } + + /** + */ + protected void startTagChanged(IJSONObject element) { + if (element == null) + return; + if (getActiveParser() == null) { + JSONModelUpdater updater = getModelUpdater(); + setActive(updater); + updater.initialize(); + updater.changeStartTag(element); + setActive(null); + } + getModelNotifier().startTagChanged(element); + } + + protected void valueChanged(IJSONNode node) { + if (node == null) + return; + if (getActiveParser() == null) { + JSONModelUpdater updater = getModelUpdater(); + setActive(updater); + updater.initialize(); + updater.changeValue(node); + setActive(null); + } + getModelNotifier().valueChanged(node); + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelNotifier.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelNotifier.java new file mode 100644 index 0000000000..07329115a8 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelNotifier.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.XMLModelNotifier + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONObject; +import org.eclipse.wst.json.core.document.IJSONPair; + +/** + * + * JSONModelNotifier manages the notification process. Clients should not use + * extend or reference. + * + * ISSUE: should be internalized. + */ + +public interface JSONModelNotifier { + + /** + * attrReplaced method + * + * @param element + * org.w3c.dom.Element + * @param newAttr + * org.w3c.dom.Attr + * @param oldAttr + * org.w3c.dom.Attr + */ + void pairReplaced(IJSONObject element, IJSONPair newAttr, IJSONPair oldAttr); + + /** + * Signal that changing is starting. + * + */ + void beginChanging(); + + /** + * Signal that changing is starting with a brand new model. + * + */ + void beginChanging(boolean newModel); + + /** + * Cancel pending notifications. This is called in the context of + * "reinitialization" so is assumed ALL notifications can be safely + * canceled, assuming that once factories and adapters are re-initialized + * they will be re-notified as text is set in model, if still appropriate. + */ + void cancelPending(); + + void childReplaced(IJSONNode parentNode, IJSONNode newChild, + IJSONNode oldChild); + + /** + * Editable state changed for node. + * + */ + // void editableChanged(Node node); + + /** + * Signal changing is finished. + * + */ + void endChanging(); + + /** + * Signal end tag changed. + * + * @param element + * + */ + void endTagChanged(IJSONObject element); + + /** + * Used to reflect state of model. + * + * @return true if model had changed. + * + */ + boolean hasChanged(); + + /** + * Used to reflect state of parsing process. + * + * @return true if model is currently changing. + */ + boolean isChanging(); + + /** + * signal property changed + * + * @param node + */ + void propertyChanged(IJSONNode node); + + /** + * signal start tag changed + * + * @param element + */ + void startTagChanged(IJSONObject element); + + /** + * signal structured changed. + * + * @param node + */ + void structureChanged(IJSONNode node); + + /** + * valueChanged method + * + * @param node + * org.w3c.dom.Node + */ + void valueChanged(IJSONNode node); + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelNotifierImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelNotifierImpl.java new file mode 100644 index 0000000000..a802f53788 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelNotifierImpl.java @@ -0,0 +1,552 @@ +/******************************************************************************* + * Copyright (c) 2001, 2008 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.XMLModelNotifierImpl + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONObject; +import org.eclipse.wst.json.core.document.IJSONPair; +import org.eclipse.wst.json.core.document.IJSONValue; +import org.eclipse.wst.json.core.internal.Logger; +import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; +import org.eclipse.wst.sse.core.internal.util.Debug; + +public class JSONModelNotifierImpl implements JSONModelNotifier { + + private static 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; + int index; + + 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 IJSONNode changedRoot = null; + + private boolean changing = false; + private boolean doingNewModel = false; + private List fEvents = null; + private boolean flushing = false; + + /** + */ + public JSONModelNotifierImpl() { + super(); + } + + /** + * attrReplaced method + * + * @param element + * org.w3c.dom.Element + * @param newAttr + * org.w3c.dom.IJSONNode + * @param oldAttr + * org.w3c.dom.IJSONNode + */ + public void pairReplaced(IJSONObject element, IJSONPair newAttr, + IJSONPair oldAttr) { + if (element == null) + return; + IJSONNode attr = null; + IJSONValue oldValue = null; + IJSONValue newValue = null; + if (oldAttr != null) { + attr = oldAttr; + oldValue = oldAttr.getValue(); + } + if (newAttr != null) { + attr = newAttr; + newValue = newAttr.getValue(); + } + IJSONNode notifier = (IJSONNode) 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; + } + + public void cancelPending() { + // we don't want to change the size of this array, since + // the array may be being processed, in the deferred notification + // loop, but we can signal that all + // should be discarded, so any remaining ones will be ignored. + if (this.fEvents != null) { + int size = fEvents.size(); + for (int i = 0; i < size; i++) { + NotifyEvent event = (NotifyEvent) fEvents.get(i); + 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 + */ + @Override + public void childReplaced(IJSONNode parentNode, IJSONNode newChild, + IJSONNode oldChild) { + if (parentNode == null) + return; + IJSONNode notifier = (IJSONNode) 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; + // IJSONNode notifier = (IJSONNode) 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 STRUCTURE_CHANGED: " + p); //$NON-NLS-1$ + } + this.changedRoot = null; + } + this.changing = false; + } + + /** + */ + // public void endTagChanged(Element element) { + // if (element == null) + // return; + // IJSONNode notifier = (IJSONNode) element; + // int offset = notifier.getStartOffset(); + // notify(notifier, INodeNotifier.CHANGE, null, null, null, offset); + // propertyChanged(element); + // } + + /** + */ + public boolean hasChanged() { + return (this.fEvents != 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.fEvents == null) + this.fEvents = new ArrayList(); + // 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) + || (((IJSONNode) notifier).getNodeType() == IJSONNode.DOCUMENT_NODE)) { + this.fEvents.add(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.fEvents == null) + return; + if (this.flushing) + return; + this.flushing = true; // force notification + int count = this.fEvents.size(); + + if (!doingNewModel && fOptimizeDeferred) { + Map notifyEvents = new HashMap(); + for (int i = 0; i < count; i++) { + NotifyEvent event = (NotifyEvent) this.fEvents.get(i); + if (event == null) + continue; // error + event.index = i; + if (event.type == INodeNotifier.REMOVE) { + addToMap(event.oldValue, event, notifyEvents); + } + if (event.type == INodeNotifier.ADD) { + addToMap(event.newValue, event, notifyEvents); + } + } + Iterator it = notifyEvents.values().iterator(); + while (it.hasNext()) { + NotifyEvent[] es = (NotifyEvent[]) it.next(); + for (int i = 0; i < es.length - 1; i++) { + NotifyEvent event = es[i]; + if (es[i].discarded) + continue; + NotifyEvent next = es[i + 1]; + if (es[i].type == INodeNotifier.ADD + && next.type == INodeNotifier.REMOVE) { + // Added then removed later, discard both + event.discarded = true; + next.discarded = true; + if (Debug.debugNotifyDeferred) { + event.reason = event.reason + ADDED_THEN_REMOVED + + "(see " + next.index + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + next.reason = next.reason + ADDED_THEN_REMOVED + + "(see " + event.index + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + } + for (int i = 0; i < count; i++) { + NotifyEvent event = (NotifyEvent) this.fEvents.get(i); + if (event == null) + continue; // error + if (event.discarded) + continue; + if (event.notifier != null + && fOptimizeDeferredAccordingToParentAdded) { + if (event.type == INodeNotifier.ADD) { + NotifyEvent[] es = (NotifyEvent[]) notifyEvents + .get(event.notifier); + if (es != null) + for (int p = 0; p < es.length + && es[p].index < event.index; p++) { + NotifyEvent prev = es[p]; + 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 " + prev.index + ")"; //$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 " + prev.index + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + break; + } + } + } + } + if (event.discarded) + continue; + if (event.notifier != null + && fOptimizeDeferredAccordingToParentRemoved) { + if (event.type == INodeNotifier.REMOVE) { + NotifyEvent[] es = (NotifyEvent[]) notifyEvents + .get(event.notifier); + if (es != null) + for (int n = 0; n < es.length; n++) { + NotifyEvent next = es[n]; + if (next.index > event.index + && 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 " + next.index + ")"; //$NON-NLS-1$ //$NON-NLS-2$ + } + break; + } + } + } + } + } + if (event.discarded) + continue; + } + } + for (int i = 0; i < count; i++) { + NotifyEvent event = (NotifyEvent) this.fEvents.get(i); + if (event == null) + continue; // error + 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.fEvents.get(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 IJSONObject) { + String p = ((IJSONNode) event.notifier).getNodeName(); + String c = ((IJSONNode) o).getNodeName(); + String d = (event.discarded ? "! " : " "); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.println(d + p + t + c); + } + } + } + this.flushing = false; + this.fEvents = null; + } + + void addToMap(Object o, NotifyEvent event, Map map) { + if (o == null) + return; + Object x = map.get(o); + if (x == null) { + map.put(o, new NotifyEvent[] { event }); + } else { + NotifyEvent[] es = (NotifyEvent[]) x; + NotifyEvent[] es2 = new NotifyEvent[es.length + 1]; + System.arraycopy(es, 0, es2, 0, es.length); + es2[es.length] = event; + map.put(o, es2); + } + } + + /** + */ + private void notifyStructureChanged(IJSONNode 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$ + } + + } + + /** + * @param node + */ + private void setCommonRootIfNeeded(IJSONNode 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() != IJSONNode.DOCUMENT_NODE + && changedRoot != node) { + // IJSONNode common = ((JSONNodeImpl) + // this.changedRoot).getCommonAncestor(node); + // if (common != null) + // this.changedRoot = common; + // else + // this.changedRoot = node; + } + } + } + + /** + */ + // public void startTagChanged(Element element) { + // if (element == null) + // return; + // IJSONNode notifier = (IJSONNode) element; + // int offset = notifier.getStartOffset(); + // notify(notifier, INodeNotifier.CHANGE, null, null, null, offset); + // propertyChanged(element); + // } + + @Override + public void structureChanged(IJSONNode node) { + if (node == null) + return; + if (isChanging()) { + setCommonRootIfNeeded(node); + if (Debug.debugNotifyDeferred) { + String p = this.changedRoot.getNodeName(); + System.out.println("requested STRUCTURE_CHANGED: " + p); //$NON-NLS-1$ + } + return; + } + if (Debug.debugNotifyDeferred) { + String p = node.getNodeName(); + System.out.println("STRUCTURE_CHANGED: " + p); //$NON-NLS-1$ + } + notifyStructureChanged(node); + } + + /** + * valueChanged method + * + * @param node + * org.w3c.dom.Node + */ + // public void valueChanged(Node node) { + // if (node == null) + // return; + // IJSONNode notifier = null; + // if (node.getNodeType() == Node.ATTRIBUTE_NODE) { + // IJSONNode attr = (IJSONNode) node; + // notifier = (IJSONNode) attr.getOwnerElement(); + // // TODO_dmw: experimental: changed 06/29/2004 to send "structuure + // // 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 = (IJSONNode) node; + // String value = node.getNodeValue(); + // int offset = notifier.getStartOffset(); + // notify(notifier, INodeNotifier.CHANGE, null, null, value, offset); + // if (node.getNodeType() != Node.ELEMENT_NODE) { + // IJSONNode parent = (IJSONNode) node.getParentNode(); + // if (parent != null) { + // notify(parent, INodeNotifier.CONTENT_CHANGED, node, null, value, offset); + // } + // } + // } + // propertyChanged(notifier); + // } + + @Override + public void endTagChanged(IJSONObject element) { + // TODO Auto-generated method stub + + } + + @Override + public void propertyChanged(IJSONNode node) { + // TODO Auto-generated method stub + + } + + @Override + public void startTagChanged(IJSONObject element) { + // TODO Auto-generated method stub + + } + + @Override + public void valueChanged(IJSONNode node) { + // TODO Auto-generated method stub + + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelParser.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelParser.java new file mode 100644 index 0000000000..421264dda9 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelParser.java @@ -0,0 +1,1817 @@ +/******************************************************************************* + * Copyright (c) 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.XMLModelParser + * modified in order to process JSON Objects. + * Alina Marin <alina@mx1.ibm.com> - fixed some stuff to improve the synch between the editor and the model. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + +import java.util.Iterator; + +import org.eclipse.wst.json.core.document.IJSONArray; +import org.eclipse.wst.json.core.document.IJSONModel; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONObject; +import org.eclipse.wst.json.core.document.IJSONPair; +import org.eclipse.wst.json.core.document.IJSONValue; +import org.eclipse.wst.json.core.regions.JSONRegionContexts; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; + +/** + * JSONModelParser + */ +public class JSONModelParser { + private JSONModelContext context = null; + private JSONModelImpl model = null; + + /** + */ + protected JSONModelParser(JSONModelImpl model) { + super(); + + if (model != null) { + this.model = model; + } + } + + /** + * changeAttrName method + * + */ + private void changeAttrName(IStructuredDocumentRegion flatNode, + ITextRegion region) { + int offset = flatNode.getStart(); + if (offset < 0) + return; + JSONNodeImpl root = (JSONNodeImpl) this.context.getRootNode(); + if (root == null) + return; + IJSONNode node = root.getNodeAt(offset); + if (node == null) + return; + if (node.getNodeType() != IJSONNode.PAIR_NODE) { + return; + } + + JSONPairImpl pair = (JSONPairImpl) node; + String name = flatNode.getText(region); + pair.setName(name); + JSONObjectImpl parentObj = (JSONObjectImpl) node.getParentNode(); + if (parentObj != null && parentObj.getParentOrPairNode() instanceof JSONPairImpl) { + JSONPairImpl parentPair = (JSONPairImpl) parentObj.getParentOrPairNode(); + if (parentPair.getValue() != parentObj.getParentNode()) { + parentPair.setValue(parentObj); + } + } + } + + private void changeAttrValue(IStructuredDocumentRegion flatNode, + ITextRegion region) { + int offset = flatNode.getStart(); + if (offset < 0) + return; + JSONNodeImpl root = (JSONNodeImpl) this.context.getRootNode(); + if (root == null) + return; + IJSONNode node = root.getNodeAt(offset); + if (node == null) + return; + JSONValueImpl value = (JSONValueImpl) createJSONValue(region.getType()); + value.setStructuredDocumentRegion(flatNode); + if(node.getNodeType() == IJSONNode.PAIR_NODE) { + JSONPairImpl pair = (JSONPairImpl) node; + pair.updateValue(value); + } else if (node.getFirstStructuredDocumentRegion() != null && isJSONValue(node.getFirstStructuredDocumentRegion().getType())) { + JSONValueImpl oldValue = (JSONValueImpl) node; + oldValue.updateValue(value); + } else if (node instanceof JSONArrayImpl) { + if (value.getValueRegionType().equals(JSONRegionContexts.JSON_VALUE_BOOLEAN) + || value.getValueRegionType().equals(JSONRegionContexts.JSON_VALUE_NULL)) { + // If the parent is a JSONArray insert the new JSONValue at + // the end of the structure + JSONArrayImpl array = (JSONArrayImpl) node; + node.insertBefore(value, null); + array.add(value); + JSONPairImpl ownerPair = (JSONPairImpl) array.getParentOrPairNode(); + if (ownerPair.getValue() == null) + ownerPair.setValue(array); + else + ownerPair.updateValue(array); + } + } + } + + /** + * changeRegion method + * + */ + void changeRegion(RegionChangedEvent change, + IStructuredDocumentRegion flatNode, ITextRegion region) { + if (flatNode == null || region == null) + return; + if (this.model.getDocument() == null) + return; + this.context = new JSONModelContext(this.model.getDocument()); + + // determine if change was whitespace only change + boolean isWhitespaceChange = false; + if (change.getText() != null && change.getText().length() > 0) { + isWhitespaceChange = Character.isWhitespace(change.getText() + .charAt(0)); + } else if (change.getDeletedText() != null + && change.getDeletedText().length() > 0) { + isWhitespaceChange = Character.isWhitespace(change.getDeletedText() + .charAt(0)); + } + if (isWhitespaceChange) + return; + // optimize typical cases + String regionType = region.getType(); + /* + * if (regionType == JSONRegionContexts.JSON_CONTENT || regionType == + * JSONRegionContexts.JSON_COMMENT_TEXT || regionType == + * JSONRegionContexts.JSON_CDATA_TEXT || regionType == + * JSONRegionContexts.BLOCK_TEXT || isNestedContent(regionType)) { + * changeData(flatNode, region); } else + */ + if (regionType == JSONRegionContexts.JSON_OBJECT_KEY) { + changeAttrName(flatNode, region); + } else if (isJSONValue(regionType)) { + changeAttrValue(flatNode, region); + } else if(regionType == JSONRegionContexts.JSON_UNKNOWN) { + return; + } + // else if (regionType == JSONRegionContexts.JSON_TAG_ATTRIBUTE_VALUE) { + // if (isWhitespaceChange + // && (change.getOffset() >= flatNode.getStartOffset() + // + region.getTextEnd())) { + // // change is entirely in white-space + // return; + // } + // changeAttrValue(flatNode, region); + // } else if (regionType == + // JSONRegionContexts.JSON_TAG_ATTRIBUTE_EQUALS) { + // if (isWhitespaceChange + // && (change.getOffset() >= flatNode.getStartOffset() + // + region.getTextEnd())) { + // // change is entirely in white-space + // return; + // } + // changeAttrEqual(flatNode, region); + // } else if (regionType == JSONRegionContexts.JSON_TAG_NAME + // || isNestedTagName(regionType)) { + // if (isWhitespaceChange + // && (change.getOffset() >= flatNode.getStartOffset() + // + region.getTextEnd())) { + // // change is entirely in white-space + // return; + // } + // changeTagName(flatNode, region); + // } + else { + changeStructuredDocumentRegion(flatNode); + } + } + + /** + */ + private void changeStartObject(IStructuredDocumentRegion flatNode, + ITextRegionList newRegions, ITextRegionList oldRegions) { + int offset = flatNode.getStart(); + if (offset < 0) + return; // error + JSONNodeImpl root = (JSONNodeImpl) this.context.getRootNode(); + if (root == null) + return; // error + IJSONNode node = root.getNodeAt(offset); + if (node == null) + return; // error + + if (node.getNodeType() != IJSONNode.OBJECT_NODE) { + changeStructuredDocumentRegion(flatNode); + return; + } + JSONObjectImpl element = (JSONObjectImpl) 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 == JSONRegionContexts.JSON_TAG_ATTRIBUTE_NAME + // || regionType == JSONRegionContexts.JSON_TAG_ATTRIBUTE_EQUALS + // || regionType == JSONRegionContexts.JSON_TAG_ATTRIBUTE_VALUE) + // continue; + // if (regionType == JSONRegionContexts.JSON_TAG_CLOSE) { + // // change from empty tag may have impact on structure + // if (!element.isEmptyTag()) + // continue; + // } else if (regionType == JSONRegionContexts.JSON_TAG_NAME + // || isNestedTagName(regionType)) { + // 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 == JSONRegionContexts.JSON_TAG_ATTRIBUTE_NAME + // || regionType == JSONRegionContexts.JSON_TAG_ATTRIBUTE_EQUALS + // || regionType == JSONRegionContexts.JSON_TAG_ATTRIBUTE_VALUE) + // continue; + // if (regionType == JSONRegionContexts.JSON_TAG_CLOSE) { + // // change from empty tag may have impact on structure + // if (!element.isEmptyTag()) + // continue; + // } else if (regionType == JSONRegionContexts.JSON_TAG_NAME + // || isNestedTagName(regionType)) { + // // 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 == JSONRegionContexts.JSON_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.model.getDocument().createAttribute( + // name); + // if (attr != null) + // attr.setNameRegion(region); + // // defer insertion of new attribute + // newAttr = attr; + // } + // } else if (regionType == + // JSONRegionContexts.JSON_TAG_ATTRIBUTE_EQUALS) { + // if (attr != null) { + // attr.setEqualRegion(region); + // } + // } else if (regionType == + // JSONRegionContexts.JSON_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 + * + */ + private void changeStructuredDocumentRegion( + IStructuredDocumentRegion flatNode) { + if (flatNode == null) + return; + if (this.model.getDocument() == 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(); + } + + /** + * cleanupContext method + */ + private void cleanupEndTag() { + IJSONNode parent = this.context.getParentNode(); + IJSONNode next = this.context.getNextNode(); + while (parent != null) { + while (next != null) { + if (next.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl element = (JSONObjectImpl) next; + // if (element.isEndTag()) { + // // floating end tag + // String tagName = element.getTagName(); + // String rootName = getFindRootName(tagName); + // JSONObjectImpl start = (JSONObjectImpl) 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; + // } + // } + } + + IJSONNode first = next.getFirstChild(); + if (first != null) { + parent = next; + next = first; + this.context.setCurrentNode(next); + } else { + next = next.getNextSibling(); + this.context.setCurrentNode(next); + } + } + + // if (parent.getNodeType() == IJSONNode.OBJECT_NODE) { + // JSONObjectImpl element = (JSONObjectImpl) parent; + // if (!element.hasEndTag() && element.hasStartTag() + // && element.getNextSibling() == null) { + // String tagName = element.getTagName(); + // JSONObjectImpl end = (JSONObjectImpl) 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.setCurrentNode(next); + } else { + this.context.setParentNode(parent); + } + } + } + + /** + */ + private void demoteNodes(IJSONNode root, IJSONNode newParent, + IJSONNode oldParent, IJSONNode next) { + if (newParent.getNodeType() != IJSONNode.OBJECT_NODE) + return; + JSONObjectImpl newElement = (JSONObjectImpl) newParent; + + // find next + while (next == null) { + if (oldParent.getNodeType() != IJSONNode.OBJECT_NODE) + return; + JSONObjectImpl oldElement = (JSONObjectImpl) 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() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl nextElement = (JSONObjectImpl) next; + if (!nextElement.hasStartTag()) { + IJSONNode 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; + // IJSONNode p = newElement.getParentNode(); + // // check if reached to top + // if (p == null || p == oldParent + // || p.getNodeType() != IJSONNode.OBJECT_NODE) + // return; + // newElement = (JSONObjectImpl) 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() != IJSONNode.OBJECT_NODE) + // return; + // newElement = (JSONObjectImpl) p; + // continue; + // } + + IJSONNode child = next; + next = next.getNextSibling(); + oldParent.removeChild(child); + insertNode(newElement, child, null); + IJSONNode childParent = child.getParentNode(); + if (childParent != newElement) { + newElement = (JSONObjectImpl) childParent; + } + } + + // find next parent and sibling + while (next == null) { + if (oldParent.getNodeType() != IJSONNode.OBJECT_NODE) + return; + JSONObjectImpl oldElement = (JSONObjectImpl) 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 IJSONModel getModel() { + return this.model; + } + + /** + * insertEndTag method + * + */ + private void updateEndObject(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(); + // String regionType = region.getType(); + // if (regionType == JSONRegionContexts.JSON_TAG_NAME + // || isNestedTagName(regionType)) { + // if (tagName == null) + // tagName = flatNode.getText(region); + // } + // } + // + // if (tagName == null) { // invalid end tag + // insertText(flatNode); // regard as invalid text + // return; + // } + // + // String rootName = getFindRootName(tagName); + JSONObjectImpl start = (JSONObjectImpl) this.context + .findParentObject(); + if (start != null) { // start tag found + // insertEndTag(start); + start.setEndStructuredDocumentRegion(flatNode); + + // update context + this.context.setParentNode(start.getParentNode()); + // // re-check the next sibling + // newNext = start.getNextSibling(); + // if (newNext != null) + // this.context.setCurrentNode(newNext); + // else + // this.context.setParentNode(newParent); + // return; + } + // + // // invalid end tag + // JSONObjectImpl end = null; + // try { + // end = (JSONObjectImpl) + // this.model.getDocument().createElement(tagName); + // } catch (JSONException ex) { + // } + // if (end == null) { // invalid end tag + // insertText(flatNode); // regard as invalid text + // return; + // } + // end.setEndStructuredDocumentRegion(flatNode); + // insertNode(end); + } + + /** + * insertNode method + * + * @param child + * org.w3c.dom.Node + */ + private void insertNode(IJSONNode node) { + if (node == null || this.context == null) { + return; + } + IJSONNode parent = this.context.getParentNode(); + if (parent == null) { + return; + } + + if (parent.getNodeType() == IJSONNode.PAIR_NODE) { + IJSONPair pair = (IJSONPair) parent; + ((JSONPairImpl) pair).setValue((IJSONValue) node); + return; + } + + if (parent.getLastChild() != null + && parent.getLastChild().getNodeType() == IJSONNode.PAIR_NODE) { + IJSONPair pair = (IJSONPair) parent.getLastChild(); + ((JSONPairImpl) pair).setValue((IJSONValue) node); + return; + } + + IJSONNode next = this.context.getNextNode(); + insertNode(parent, node, next); + next = node.getNextSibling(); + if (next != null) { + this.context.setCurrentNode(next); + } else { + this.context.setParentNode(node.getParentNode()); + } + + // if (node != null && this.context != null) { + // IJSONNode aparent = this.context.getParentNode(); + // if (parent != null) { + // IJSONNode next = this.context.getNextNode(); + // // Reset parents which are closed container elements; should not + // // be parents + // if (parent.getNodeType() == IJSONNode.OBJECT_NODE) { + // String type = ((JSONObjectImpl) parent) + // .getStartStructuredDocumentRegion().getLastRegion() + // .getType(); + // if (((JSONObjectImpl) parent).isContainer() + // /* && type == JSONRegionContexts.JSON_EMPTY_TAG_CLOSE */) { + // // next = parent.getNextSibling(); + // // parent = parent.getParentNode(); + // } else { + // // ModelParserAdapter adapter = getParserAdapter(); + // // if (adapter != null) { + // // while (parent.getNodeType() == IJSONNode.OBJECT_NODE + // // && !adapter.canContain((Element) parent, + // // node) + // // && adapter + // // .isEndTagOmissible((Element) parent)) { + // // next = parent.getNextSibling(); + // // parent = parent.getParentNode(); + // // } + // // } + // } + // } + // insertNode(parent, node, next); + // next = node.getNextSibling(); + // if (next != null) { + // this.context.setCurrentNode(next); + // } else { + // this.context.setParentNode(node.getParentNode()); + // } + // } + // } + } + + /** + */ + private void insertNode(IJSONNode parent, IJSONNode node, IJSONNode next) { + while (next != null && next.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl nextElement = (JSONObjectImpl) 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); + } + + private void insertObject(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + JSONObjectImpl element = null; + if(this.context.getCurrentNode() != null && this.context.getCurrentNode() instanceof IJSONObject) { + element = (JSONObjectImpl) this.context.getCurrentNode(); + } else { + element = (JSONObjectImpl) this.model.getDocument() + .createJSONObject(); + } + if (element == null) { + return; + } + element.setStartStructuredDocumentRegion(flatNode); + insertStartObjectOLD(element); + } + + protected void insertStartObjectOLD(IJSONObject element) { + if (element == null) + return; + if (this.context == null) + return; + + insertNode(element); + + JSONObjectImpl newElement = (JSONObjectImpl) element; + if (newElement.isEmptyTag() || !newElement.isContainer()) + return; + + // Ignore container tags that have been closed + String type = newElement.getStartStructuredDocumentRegion() + .getLastRegion().getType(); + // if (newElement.isContainer() + // && type == JSONRegionContexts.JSON_EMPTY_TAG_CLOSE) + // return; + + // demote siblings + IJSONNode parent = this.context.getParentNode(); + if (parent == null) + return; // error + IJSONNode next = this.context.getNextNode(); + demoteNodes(element, element, parent, next); + + // update context + IJSONNode firstChild = element.getFirstChild(); + if (firstChild != null) + this.context.setCurrentNode(firstChild); + else + this.context.setParentNode(element); + } + + // ---------------------------- JSON Array + + protected void insertStartArray(IJSONArray element) { + if (element == null) + return; + if (this.context == null) + return; + + insertNode(element); + + JSONArrayImpl newElement = (JSONArrayImpl) element; + String type = newElement.getStartStructuredDocumentRegion() + .getLastRegion().getType(); + // demote siblings + IJSONNode parent = this.context.getParentNode(); + if (parent == null) + return; // error + IJSONNode next = this.context.getNextNode(); + demoteNodes(element, element, parent, next); + // update context + IJSONNode firstChild = element.getFirstChild(); + if (firstChild != null) + this.context.setCurrentNode(firstChild); + else + this.context.setParentNode(element); + } + + /** + * insertStartTag method + * + */ + private void insertArray(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + JSONArrayImpl element = (JSONArrayImpl) this.model.getDocument() + .createJSONArray(); + element.setStartStructuredDocumentRegion(flatNode); + insertStartArray(element); + } + + private void updateEndArray(IStructuredDocumentRegion flatNode) { + JSONArrayImpl start = (JSONArrayImpl) this.context.findParentArray(); + if (start != null) { // start tag found + start.setEndStructuredDocumentRegion(flatNode); + // update context + this.context.setParentNode(start.getParentNode()); + } + + } + + // ---------------- Commons insert + + /** + * insertStructuredDocumentRegion method + * + */ + protected void insertStructuredDocumentRegion( + IStructuredDocumentRegion flatNode) { + String regionType = StructuredDocumentRegionUtil + .getFirstRegionType(flatNode); + if (regionType == JSONRegionContexts.JSON_OBJECT_OPEN) { + insertObject(flatNode); + } else if (regionType == JSONRegionContexts.JSON_OBJECT_CLOSE) { + updateEndObject(flatNode); + } else if (regionType == JSONRegionContexts.JSON_ARRAY_OPEN) { + insertArray(flatNode); + } else if (regionType == JSONRegionContexts.JSON_ARRAY_CLOSE) { + updateEndArray(flatNode); + } else if (regionType == JSONRegionContexts.JSON_OBJECT_KEY) { + insertObjectKey(flatNode); + } else if (regionType == JSONRegionContexts.JSON_VALUE_BOOLEAN) { + insertValue(flatNode, regionType); + } else if (regionType == JSONRegionContexts.JSON_VALUE_NULL) { + insertValue(flatNode, regionType); + } else if (regionType == JSONRegionContexts.JSON_VALUE_NUMBER) { + insertValue(flatNode, regionType); + } else if (regionType == JSONRegionContexts.JSON_VALUE_STRING) { + insertValue(flatNode, regionType); + } + } + + private void insertValue(IStructuredDocumentRegion flatNode, + String regionType) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + ITextRegion nameRegion = StructuredDocumentRegionUtil + .getFirstRegion(flatNode); + // Create JSON Value + JSONValueImpl value = (JSONValueImpl) createJSONValue(regionType); + value.setStructuredDocumentRegion(flatNode); + // If current node is different from null, it means this value shoyld be + // inserted as the value of an existing pair node + if (this.context.getCurrentNode() != null && this.context.getCurrentNode() instanceof JSONPairImpl) { + JSONPairImpl pair = (JSONPairImpl) this.context.getCurrentNode(); + pair.setValue(value); + JSONNodeImpl parent = (JSONNodeImpl) this.context.getParentNode(); + parent.insertBefore(pair, this.context.getNextNode()); + // If parent is an instance of JSONObject add the pair node to the + // parent node + if(parent instanceof IJSONObject) { + JSONObjectImpl parentObject = (JSONObjectImpl) parent; + parentObject.add(pair); + } + } else { + // There is no context, it means the model is reloaded or loaded for + // the first time + JSONStructureImpl structure = (JSONStructureImpl) this.context.findParentStructure(); + if (structure != null) { + if (structure.getNodeType() == IJSONNode.OBJECT_NODE) { + // If the parent is a JSONObject look for the last children, + // ensure it is a JSONPair and add set the value of the last + // children + if (structure.getLastChild() != null + && structure.getLastChild().getNodeType() == IJSONNode.PAIR_NODE) { + ((JSONPairImpl) structure.getLastChild()).setValue(value); + } + if(structure.getParentOrPairNode() instanceof JSONPairImpl) { + JSONPairImpl parentPair = (JSONPairImpl) structure.getParentOrPairNode(); + if (structure.getParentNode() != parentPair.getValue()) { + parentPair.setValue(structure); + } + } + } else if (structure.getNodeType() == IJSONNode.ARRAY_NODE) { + // If the parent is a JSONArray insert the new JSONValue at + // the end of the structure + JSONArrayImpl array = (JSONArrayImpl) structure; + structure.insertBefore(value, null); + array.add(value); + JSONPairImpl ownerPair = (JSONPairImpl) array.getParentOrPairNode(); + if(ownerPair.getValue() == null) + ownerPair.setValue(array); + else + ownerPair.updateValue(array); + } else { + insertNode(structure, value, null); + } + } + } + } + + private IJSONValue createJSONValue(String regionType) { + if (regionType == JSONRegionContexts.JSON_VALUE_BOOLEAN) { + return this.model.getDocument().createBooleanValue(); + } else if (regionType == JSONRegionContexts.JSON_VALUE_NULL) { + return this.model.getDocument().createNullValue(); + } else if (regionType == JSONRegionContexts.JSON_VALUE_NUMBER) { + return this.model.getDocument().createNumberValue(); + } else if (regionType == JSONRegionContexts.JSON_VALUE_STRING) { + return this.model.getDocument().createStringValue(); + } + return null; + } + + private void insertNumberValue(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + ITextRegion nameRegion = StructuredDocumentRegionUtil + .getFirstRegion(flatNode); + JSONStructureImpl array = (JSONStructureImpl) this.context + .findParentStructure(); + if (array != null) { + JSONNumberValueImpl value = (JSONNumberValueImpl) this.model + .getDocument().createNumberValue(); + insertNode(array, value, null); + } + } + + private void insertNullValue(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + ITextRegion nameRegion = StructuredDocumentRegionUtil + .getFirstRegion(flatNode); + JSONStructureImpl array = (JSONStructureImpl) this.context + .findParentStructure(); + if (array != null) { + JSONNullValueImpl value = (JSONNullValueImpl) this.model + .getDocument().createNullValue(); + insertNode(array, value, null); + } + } + + private void insertStringValue(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) + return; + + ITextRegion nameRegion = StructuredDocumentRegionUtil + .getFirstRegion(flatNode); + JSONStructureImpl array = (JSONStructureImpl) this.context + .findParentStructure(); + if (array != null) { + JSONStringValueImpl value = (JSONStringValueImpl) this.model + .getDocument().createStringValue(); + insertNode(array, value, null); + } + } + + private void insertObjectKey(IStructuredDocumentRegion flatNode) { + ITextRegionList regions = flatNode.getRegions(); + if (regions == null) { + return; + } + JSONObjectImpl object = (JSONObjectImpl) this.context + .findParentObject(); + if (object == null) { + return; + } + + JSONPairImpl pair = null; + for (Iterator i = regions.iterator(); i.hasNext();) { + ITextRegion region = (ITextRegion) i.next(); + if (region == null) { + continue; + } + if (region.getType() == JSONRegionContexts.JSON_OBJECT_KEY) { + // Set the proper name and region to the JSONPair node + String name = flatNode.getText(region); + pair = (JSONPairImpl) this.model.getDocument().createJSONPair(name); + pair.setStartStructuredDocumentRegion(flatNode); + pair.setNameRegion(region); + // Ensure to add the pair node in the right place, taking as + // reference the current node offset from the context + if (context.getCurrentNode() != null + && context.getCurrentNode().getStartOffset() > pair.getStartOffset()) + insertNode(object, pair, this.context.getCurrentNode()); + else + insertNode(object, pair, this.context.getNextNode()); + // Add the pair to the parent object + object.add(pair); + // Update context with the new JSONPair as the current node + if (this.context.getCurrentNode() != null + && this.context.getCurrentNode().getStartOffset() == pair.getStartOffset()) + this.context.setCurrentNode(pair); + if(object.getParentOrPairNode() instanceof JSONPairImpl) { + JSONPairImpl parentPair = (JSONPairImpl) object.getParentOrPairNode(); + if (object.getParentNode() != parentPair.getValue()) { + parentPair.setValue(object); + } + } + } else if (region.getType() == JSONRegionContexts.JSON_COLON) { + pair.setEqualRegion(region); + } else if (region.getType() == JSONRegionContexts.JSON_VALUE_BOOLEAN) { + JSONBooleanValueImpl value = (JSONBooleanValueImpl) this.model + .getDocument().createBooleanValue(); + pair.setValue(value); + } else if (region.getType() == JSONRegionContexts.JSON_VALUE_NULL) { + JSONNullValueImpl value = (JSONNullValueImpl) this.model + .getDocument().createNullValue(); + pair.setValue(value); + } else if (region.getType() == JSONRegionContexts.JSON_VALUE_NUMBER) { + JSONNumberValueImpl value = (JSONNumberValueImpl) this.model + .getDocument().createNumberValue(); + pair.setValue(value); + } else if (region.getType() == JSONRegionContexts.JSON_VALUE_STRING) { + JSONStringValueImpl value = (JSONStringValueImpl) this.model + .getDocument().createStringValue(); + pair.setValue(value); + } + } + } + + protected boolean isNestedTag(String regionType) { + boolean result = false; + return result; + } + + protected boolean isNestedCommentText(String regionType) { + boolean result = false; + return result; + } + + protected boolean isNestedCommentOpen(String regionType) { + boolean result = false; + return result; + } + + protected boolean isNestedTagName(String regionType) { + boolean result = false; + return result; + } + + protected boolean isNestedContent(String regionType) { + boolean result = false; + return result; + } + + /** + */ + private void promoteNodes(IJSONNode root, IJSONNode newParent, + IJSONNode newNext, IJSONNode oldParent, IJSONNode next) { + JSONObjectImpl newElement = null; + if (newParent.getNodeType() == IJSONNode.OBJECT_NODE) { + newElement = (JSONObjectImpl) newParent; + } + + IJSONNode rootParent = root.getParentNode(); + while (oldParent != rootParent) { + while (next != null) { + boolean done = false; + boolean endTag = false; + if (next.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl nextElement = (JSONObjectImpl) next; + if (!nextElement.hasStartTag()) { + IJSONNode 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 + IJSONNode elementNext = newElement.getNextSibling(); + // promote siblings + promoteNodes(newElement, newParent, elementNext, + newElement, newNext); + newNext = newElement.getNextSibling(); + if (newParent.getNodeType() == IJSONNode.OBJECT_NODE) { + newElement = (JSONObjectImpl) newParent; + } else { + newElement = null; + } + continue; + } + + IJSONNode child = next; + next = next.getNextSibling(); + oldParent.removeChild(child); + insertNode(newParent, child, newNext); + IJSONNode childParent = child.getParentNode(); + if (childParent != newParent) { + newParent = childParent; + newElement = (JSONObjectImpl) newParent; + newNext = child.getNextSibling(); + } + } + } + + if (oldParent.getNodeType() != IJSONNode.OBJECT_NODE) + return; + JSONObjectImpl oldElement = (JSONObjectImpl) oldParent; + oldParent = oldElement.getParentNode(); + if (oldParent == null) + return; // error + next = oldElement.getNextSibling(); + + if (oldElement.hasEndTag()) { + IJSONObject end = null; + if (!oldElement.hasChildNodes() && !oldElement.hasStartTag()) { + oldParent.removeChild(oldElement); + end = oldElement; + } else { + // end = oldElement.removeEndTag(); + } + if (end != null) { + insertNode(newParent, end, newNext); + IJSONNode endParent = end.getParentNode(); + if (endParent != newParent) { + newParent = endParent; + newElement = (JSONObjectImpl) newParent; + newNext = end.getNextSibling(); + } + } + } + } + } + + /** + * removeEndTag method + * + * @param element + * org.w3c.dom.Element + */ + private void removeEndTag(IJSONNode element) { + if (element == null) + return; + if (this.context == null) + return; + + IJSONNode parent = element.getParentNode(); + if (parent == null) + return; // error + + if (!((JSONObjectImpl) element).isContainer()) { + // just update context + IJSONNode elementNext = element.getNextSibling(); + if (elementNext != null) + this.context.setCurrentNode(elementNext); + else + this.context.setParentNode(parent); + return; + } + + // demote siblings + IJSONNode next = element.getNextSibling(); + JSONObjectImpl newElement = (JSONObjectImpl) element; + // find new parent + for (IJSONNode last = newElement.getLastChild(); last != null; last = last + .getLastChild()) { + if (last.getNodeType() != IJSONNode.OBJECT_NODE) + break; + JSONObjectImpl lastElement = (JSONObjectImpl) last; + if (lastElement.hasEndTag() || lastElement.isEmptyTag() + || !lastElement.isContainer()) + break; + newElement = lastElement; + } + IJSONNode lastChild = newElement.getLastChild(); + demoteNodes(element, newElement, parent, next); + + // update context + IJSONNode newNext = null; + if (lastChild != null) + newNext = lastChild.getNextSibling(); + else + newNext = newElement.getFirstChild(); + if (newNext != null) + this.context.setCurrentNode(newNext); + else + this.context.setParentNode(newElement); + } + + /** + * removeNode method + * + * @param node + * org.w3c.dom.Node + */ + private void removeNode(IJSONNode node) { + if (node == null) + return; + if (this.context == null) + return; + + IJSONNode parent = node.getParentNode(); + if (parent == null) { + if(node instanceof IJSONValue) { + parent = node.getParentOrPairNode(); + if(parent == null) { + return; + } + } + } + IJSONNode next = node.getNextSibling(); + IJSONNode prev = node.getPreviousSibling(); + + // update context + IJSONNode oldParent = this.context.getParentNode(); + if (node == oldParent) { + if (next != null) + this.context.setCurrentNode(next); + else + this.context.setParentNode(parent); + } else { + IJSONNode oldNext = this.context.getNextNode(); + if (node == oldNext) { + this.context.setCurrentNode(next); + } + } + + parent.removeChild(node); + + // if (removeImplicitElement(parent) != null) + // return; + + // demote sibling + if (prev != null && prev.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl newElement = (JSONObjectImpl) prev; + if (!newElement.hasEndTag() && !newElement.isEmptyTag() + && newElement.isContainer()) { + // find new parent + for (IJSONNode last = newElement.getLastChild(); last != null; last = last + .getLastChild()) { + if (last.getNodeType() != IJSONNode.OBJECT_NODE) + break; + JSONObjectImpl lastElement = (JSONObjectImpl) last; + if (lastElement.hasEndTag() || lastElement.isEmptyTag() + || !lastElement.isContainer()) + break; + newElement = lastElement; + } + IJSONNode lastChild = newElement.getLastChild(); + demoteNodes(prev, newElement, parent, next); + + // update context + IJSONNode newNext = null; + if (lastChild != null) + newNext = lastChild.getNextSibling(); + else + newNext = newElement.getFirstChild(); + if (newNext != null) + this.context.setCurrentNode(newNext); + else + this.context.setParentNode(newElement); + } + } + } + + /** + * removeStartTag method + * + * @param element + * org.w3c.dom.Element + */ + private void removeStartObject(IJSONObject element) { + if (element == null) + return; + if (this.context == null) + return; + + // for implicit tag + JSONObjectImpl oldElement = (JSONObjectImpl) element; + // if (canBeImplicitTag(oldElement)) { + // Node newParent = null; + // Node prev = oldElement.getPreviousSibling(); + // if (prev != null && prev.getNodeType() == IJSONNode.OBJECT_NODE) { + // JSONObjectImpl prevElement = (JSONObjectImpl) 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.setCurrentNode(child); + // } else if (oldElement.hasEndTag()) { + // this.context.setParentNode(oldElement); + // } + // return; + // } + // } + // // for comment tag + // if (oldElement.isCommentTag()) + // oldElement.removeStartTag(); + + // promote children + IJSONNode elementParent = element.getParentNode(); + IJSONNode parent = elementParent; + if (parent == null) + return; + IJSONNode first = element.getFirstChild(); + IJSONNode firstElement = null; // for the case first is removed as end + // tag + if (first != null) { + // find new parent for children + JSONObjectImpl newElement = null; + for (IJSONNode last = element.getPreviousSibling(); last != null; last = last + .getLastChild()) { + if (last.getNodeType() != IJSONNode.OBJECT_NODE) + break; + JSONObjectImpl lastElement = (JSONObjectImpl) last; + if (lastElement.hasEndTag() || lastElement.isEmptyTag() + || !lastElement.isContainer()) + break; + newElement = lastElement; + } + IJSONNode next = first; + if (newElement != null) { + while (next != null) { + if (!newElement.hasEndTag() && newElement.hasStartTag() + && next.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl nextElement = (JSONObjectImpl) next; + if (!nextElement.hasStartTag() + && nextElement.hasEndTag() + /* && nextElement.matchEndTag(newElement) */) { + // stop at the matched invalid end tag + IJSONNode elementChild = nextElement + .getFirstChild(); + while (elementChild != null) { + IJSONNode 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; + + IJSONNode newParent = newElement.getParentNode(); + if (newParent == parent) + break; + if (newParent == null + || newParent.getNodeType() != IJSONNode.OBJECT_NODE) + break; // error + newElement = (JSONObjectImpl) newParent; + continue; + } + } + // if (!canContain(newElement, next)) { + // Node newParent = newElement.getParentNode(); + // if (newParent == parent) + // break; + // if (newParent == null + // || newParent.getNodeType() != IJSONNode.OBJECT_NODE) + // break; // error + // newElement = (JSONObjectImpl) newParent; + // continue; + // } + IJSONNode child = next; + next = next.getNextSibling(); + element.removeChild(child); + newElement.appendChild(child); + } + newElement = null; + } + if (parent.getNodeType() == IJSONNode.OBJECT_NODE) { + newElement = (JSONObjectImpl) parent; + } + while (next != null) { + if (newElement == null /* || canContain(newElement, next) */) { + IJSONNode child = next; + next = next.getNextSibling(); + element.removeChild(child); + parent.insertBefore(child, element); + continue; + } + + parent = newElement.getParentNode(); + if (parent == null) + return; + + // promote siblings + IJSONNode newNext = newElement.getNextSibling(); + IJSONNode child = element; + while (child != null) { + IJSONNode nextChild = child.getNextSibling(); + newElement.removeChild(child); + parent.insertBefore(child, newNext); + child = nextChild; + } + + // leave the old end tag where it is + if (newElement.hasEndTag()) { + // IJSONObject end = newElement.removeEndTag(); + // if (end != null) { + // parent.insertBefore(end, newNext); + // } + } + if (!newElement.hasStartTag()) { + // implicit element + if (!newElement.hasChildNodes()) { + parent.removeChild(newElement); + } + } + + if (parent.getNodeType() == IJSONNode.OBJECT_NODE) { + newElement = (JSONObjectImpl) parent; + } else { + newElement = null; + } + } + } + + IJSONNode newNext = element; + IJSONNode startElement = null; // for the case element is removed as end + // tag + if (oldElement.hasEndTag()) { + // find new parent for invalid end tag and siblings + JSONObjectImpl newElement = null; + for (IJSONNode last = element.getPreviousSibling(); last != null; last = last + .getLastChild()) { + if (last.getNodeType() != IJSONNode.OBJECT_NODE) + break; + JSONObjectImpl lastElement = (JSONObjectImpl) last; + if (lastElement.hasEndTag() || lastElement.isEmptyTag() + || !lastElement.isContainer()) + break; + newElement = lastElement; + } + if (newElement != null) { + // demote invalid end tag and sibling + IJSONNode next = element; + while (next != null) { + if (!newElement.hasEndTag() && newElement.hasStartTag() + && next.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl nextElement = (JSONObjectImpl) next; + if (!nextElement.hasStartTag() + && nextElement.hasEndTag() + /* && nextElement.matchEndTag(newElement) */) { + // stop at the matched invalid end tag + IJSONNode elementChild = nextElement + .getFirstChild(); + while (elementChild != null) { + IJSONNode 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; + + IJSONNode newParent = newElement.getParentNode(); + if (newParent == parent) + break; + if (newParent == null + || newParent.getNodeType() != IJSONNode.OBJECT_NODE) + break; // error + newElement = (JSONObjectImpl) newParent; + continue; + } + } + // if (!canContain(newElement, next)) { + // Node newParent = newElement.getParentNode(); + // if (newParent == parent) + // break; + // if (newParent == null + // || newParent.getNodeType() != IJSONNode.OBJECT_NODE) + // break; // error + // newElement = (JSONObjectImpl) newParent; + // continue; + // } + IJSONNode child = next; + next = next.getNextSibling(); + parent.removeChild(child); + if (child == oldElement) { + // if (!oldElement.isCommentTag()) { + // clone (re-create) end tag + // IJSONObject end = oldElement.removeEndTag(); + // if (end != null) { + // child = end; + // newNext = end; + // } + // } + } + newElement.appendChild(child); + } + } else { + // if (!oldElement.isCommentTag()) { + // clone (re-create) end tag + // IJSONObject end = oldElement.removeEndTag(); + // if (end != null) { + // parent.insertBefore(end, oldElement); + // parent.removeChild(oldElement); + // newNext = end; + // } + // } + } + } else { + newNext = oldElement.getNextSibling(); + parent.removeChild(oldElement); + } + + // update context + IJSONNode oldParent = this.context.getParentNode(); + IJSONNode oldNext = this.context.getNextNode(); + if (element == oldParent) { + if (oldNext != null) { + this.context.setCurrentNode(oldNext); // reset for new parent + } else if (newNext != null) { + this.context.setCurrentNode(newNext); + } else { + this.context.setParentNode(parent); + } + } else if (element == oldNext) { + if (firstElement != null) { + this.context.setParentNode(firstElement); + } else if (first != null) { + this.context.setCurrentNode(first); + } else if (startElement != null) { + this.context.setParentNode(startElement); + } else { + this.context.setCurrentNode(newNext); + } + } + + // removeImplicitElement(elementParent); + } + + private void removeStructuredDocumentRegion( + IStructuredDocumentRegion oldStructuredDocumentRegion) { + JSONNodeImpl node = (JSONNodeImpl) this.context.getCurrentNode(); + if (node != null) { + short nodeType = node.getNodeType(); + if (nodeType == IJSONNode.OBJECT_NODE + || nodeType == IJSONNode.ARRAY_NODE + || nodeType == IJSONNode.PAIR_NODE) { + removeNode(node); +// if(context.getCurrentNode() != null && context.getCurrentNode().equals(node)) +// context.setCurrentNode(null); + return; + } + } + } + + /** + * replaceRegions method + * + * @param newRegions + * java.util.Vector + * @param oldRegions + * java.util.Vector + */ + void replaceRegions(IStructuredDocumentRegion flatNode, + ITextRegionList newRegions, ITextRegionList oldRegions) { + if (flatNode == null) + return; + if (this.model.getDocument() == null) + return; + this.context = new JSONModelContext(this.model.getDocument()); + + boolean isWhiteSpaces = false; + if (newRegions != null + && (oldRegions == null || oldRegions.size() == 0)) { + isWhiteSpaces = true; + Iterator e = newRegions.iterator(); + while (e.hasNext() && isWhiteSpaces) { + ITextRegion region = (ITextRegion) e.next(); + String regionType = region.getType(); + isWhiteSpaces = (regionType == JSONRegionContexts.WHITE_SPACE); + } + if (isWhiteSpaces) { + return; + } + } + // optimize typical cases + String regionType = StructuredDocumentRegionUtil + .getFirstRegionType(flatNode); + if (regionType == JSONRegionContexts.JSON_OBJECT_OPEN) { + changeStartObject(flatNode, newRegions, oldRegions); + } + if(regionType == JSONRegionContexts.JSON_OBJECT_KEY) { + changeAttrName(flatNode, flatNode.getFirstRegion()); + } else if (isJSONValue(regionType)) { + changeAttrValue(flatNode, flatNode.getFirstRegion()); + } else if(regionType == JSONRegionContexts.JSON_UNKNOWN){ + return; + } + // else if (regionType == JSONRegionContexts.JSON_END_TAG_OPEN) { + // changeEndTag(flatNode, newRegions, oldRegions); + // } + else { + changeStructuredDocumentRegion(flatNode); + } + } + + /** + * replaceStructuredDocumentRegions method + * + */ + void replaceStructuredDocumentRegions( + IStructuredDocumentRegionList newStructuredDocumentRegions, + IStructuredDocumentRegionList oldStructuredDocumentRegions) { + if (this.model.getDocument() == null) + return; + this.context = new JSONModelContext(this.model.getDocument()); + + int newCount = (newStructuredDocumentRegions != null ? newStructuredDocumentRegions + .getLength() : 0); + int oldCount = (oldStructuredDocumentRegions != null ? oldStructuredDocumentRegions + .getLength() : 0); + + int index; + // Find the first document region from the list that is not a comma + // region + for (index = 0; index < oldCount-1; index++) { + if(!oldStructuredDocumentRegions.item(index).getType().equals(JSONRegionContexts.JSON_COMMA)) + break; + } + + if(oldCount > 0 && !oldStructuredDocumentRegions.item(index).getType().equals(JSONRegionContexts.JSON_COMMA)) { + // Set up the model context, just ensure the region is not a comma + setupContext(oldStructuredDocumentRegions.item(index)); + + for (int i = index; i < oldCount; i++) { + IStructuredDocumentRegion documentRegion = oldStructuredDocumentRegions + .item(i); + // Remove the old document region + removeStructuredDocumentRegion(documentRegion); + } + } else { + if (newCount == 0) + return; + // In case the oldStructuredDocumentRegions list is empty find the + // first document region from the list that is not a comma region + for (index = 0; index < newCount-1; index++) { + if(!newStructuredDocumentRegions.item(index).getType().equals(JSONRegionContexts.JSON_COMMA)) + break; + } + + if(newCount > 0 && !newStructuredDocumentRegions.item(index).getType().equals(JSONRegionContexts.JSON_COMMA)) { + setupContext(newStructuredDocumentRegions.item(index)); + } + } + // make sure the parent is set to deepest level + // when end tag has been removed + this.context.setLast(); + + if (newCount > 0) { + for (int i = 0; i < newCount; i++) { + IStructuredDocumentRegion documentRegion = newStructuredDocumentRegions + .item(i); + // Add each of the new document regions + insertStructuredDocumentRegion(documentRegion); + } + } + + this.context.setCurrentNode(null); + } + + /** + * setupContext method + * + */ + private void setupContext( + IStructuredDocumentRegion startStructuredDocumentRegion) { + int offset = startStructuredDocumentRegion.getStart(); + if (offset < 0) + return; + JSONNodeImpl root = (JSONNodeImpl) this.context.getRootNode(); + if (root == null) + return; + + if (offset == 0) { + // at the beginning of document + IJSONNode child = root.getFirstChild(); + if (child != null) + this.context.setCurrentNode(child); + else + this.context.setParentNode(root); + return; + } + + // Get the JSON Node at the specified position in the document, this + // position is the start offset of the flatNode + JSONNodeImpl node = (JSONNodeImpl) root.getNodeAt(offset); + if (node == null) { + // might be at the end of document + this.context.setParentNode(root); + this.context.setLast(); + return; + } + + if(node.getStartStructuredDocumentRegion() != null) { + if(offset == node.getStartStructuredDocumentRegion().getStartOffset()) { + // This is a JSONPair and the flatNode might be and update of + // the object key value + this.context.setCurrentNode(node); + return; + } else if (node.getEndStructuredDocumentRegion() != null) { + if(offset == node.getEndStructuredDocumentRegion().getStartOffset()) { + // This is a JSONPair and the flatNode might be an update of + // the pair value + this.context.setCurrentNode(node); + return; + } + } + } + + if(node instanceof JSONPairImpl && ((JSONPairImpl) node).getValue() != null) { + if(isJSONValue(startStructuredDocumentRegion.getType())) { + // This is a JSONPair and the flatNode might be the pair value + this.context.setCurrentNode(node); + return; + } + } + + // In case Node is a JSONObject or JSONArray, look for the current node + // into the children + for (IJSONNode child = node.getFirstChild(); child != null; child = child + .getNextSibling()) { + if (offset >= ((JSONNodeImpl) child).getEndOffset()) { + if(child instanceof JSONPairImpl && ((JSONPairImpl) child).getValue() == null) { + if (isJSONValue(startStructuredDocumentRegion.getType())) { + this.context.setCurrentNode(child); + return; + } + } else { + continue; + } + } + this.context.setCurrentNode(child); + return; + } + + // The node is tha paret node of the flatNode + this.context.setParentNode(node); + this.context.setLast(); + } + + protected JSONModelContext getContext() { + return context; + } + + /** + * Returns true if the region type is a JSONValue + * + */ + private boolean isJSONValue(String regionType){ + if(regionType == JSONRegionContexts.JSON_VALUE_STRING + || regionType == JSONRegionContexts.JSON_VALUE_BOOLEAN + || regionType == JSONRegionContexts.JSON_VALUE_NUMBER + || regionType == JSONRegionContexts.JSON_VALUE_NULL) { + return true; + } + return false; + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelUpdater.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelUpdater.java new file mode 100644 index 0000000000..41931957d7 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelUpdater.java @@ -0,0 +1,1757 @@ +/******************************************************************************* + * Copyright (c) 2001, 2013 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 + * David Carver (STAR) - bug 296999 - Inefficient use of new String() + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.XMLModelUpdater + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + +import java.util.Enumeration; +import java.util.Iterator; + +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONObject; +import org.eclipse.wst.json.core.regions.JSONRegionContexts; +import org.eclipse.wst.sse.core.internal.provisional.events.RegionChangedEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; + +/** + * JSONModelUpdater class + */ +public class JSONModelUpdater { + private int diff = 0; + private int gapLength = 0; + private int gapOffset = 0; + private IStructuredDocumentRegion gapStructuredDocumentRegion = null; + private ISourceGenerator generator = null; + private JSONModelImpl model = null; + private JSONNodeImpl nextNode = null; + private JSONNodeImpl parentNode = null; + + protected JSONModelUpdater(JSONModelImpl 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; +// IJSONObject element = (IJSONObject) attr.getOwnerIJSONObject(); +// 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 = JSONNodeImpl.EMPTY_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; +// IJSONObject element = (IJSONObject) attr.getOwnerIJSONObject(); +// 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 = JSONNodeImpl.EMPTY_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 = JSONNodeImpl.EMPTY_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(IJSONObject element) { +// String source = this.generator.generateEndTag(element); +// if (source == null) +// return; +// int length = source.length(); +// if (length == 0) +// return; +// +// IJSONObject impl = (IJSONObject) 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(RegionChangedEvent change, + IStructuredDocumentRegion flatNode, ITextRegion region) { + if (change.getOffset() >= flatNode.getStartOffset() + + region.getTextEnd()) { + // change is entirely in white-space + return; + } + + JSONNodeImpl root = (JSONNodeImpl) this.model.getDocument(); + this.parentNode = root; + this.nextNode = (JSONNodeImpl) root.getFirstChild(); + + removeGapStructuredDocumentRegion(flatNode); + insertGapStructuredDocumentRegionBefore(flatNode.getStart()); + changeStructuredDocumentRegion(flatNode); + insertGapStructuredDocumentRegionAfter(flatNode.getEnd()); + } + + /** + * This is a fallback method to regenerate the start tag. + */ + void changeStartTag(IJSONObject element) { + if (element == null) + return; + JSONObjectImpl impl = (JSONObjectImpl) element; + + if (!impl.hasStartTag() && !impl.hasEndTag()) { + // need to generate the start and the end tags + IJSONNode 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 + JSONNodeImpl 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; +// } +// } + } + + IJSONNode child = this.nextNode.getFirstChild(); + if (child != null) { + this.parentNode = this.nextNode; + this.nextNode = (JSONNodeImpl) child; + continue; + } + + if (this.nextNode.getNodeType() == IJSONNode.OBJECT_NODE) { + this.parentNode = this.nextNode; + this.nextNode = null; + continue; + } + + this.nextNode = (JSONNodeImpl) this.nextNode.getNextSibling(); + if (this.nextNode != null) + continue; + } + + if (this.parentNode.getNodeType() == IJSONNode.OBJECT_NODE) { + IJSONObject element = (IJSONObject) 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 = (JSONNodeImpl) this.parentNode.getNextSibling(); + this.parentNode = (JSONNodeImpl) this.parentNode.getParentNode(); + } + if (ownerNode == null) + throw new StructuredDocumentRegionManagementException(); + + short nodeType = ownerNode.getNodeType(); + if (nodeType == IJSONNode.OBJECT_NODE) { + JSONObjectImpl element = (JSONObjectImpl) 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 = (JSONNodeImpl) 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 = (JSONNodeImpl) 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 = (JSONNodeImpl) 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 = (JSONNodeImpl) 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 = (JSONNodeImpl) 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 = (JSONNodeImpl) 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 = JSONNodeImpl.EMPTY_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((IJSONNode) prev); +// if (preTag != null && preTag.length() > 0) { +// offset += preTag.length(); +// source = preTag + source; +// } +// } else { +// Node parent = text.getParentNode(); +// if (parent != null && parent.getNodeType() == IJSONNode.OBJECT_NODE) { +// IJSONObject element = (IJSONObject) 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(IJSONNode 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 == IJSONNode.OBJECT_NODE) { + changeStartTag((IJSONObject) node); + return; + } + + String source = this.generator.generateSource(node); + if (source == null) + source = JSONNodeImpl.EMPTY_STRING; + int length = source.length(); + + JSONNodeImpl impl = (JSONNodeImpl) 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(IJSONIJSONObject 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() != JSONRegionContexts.JSON_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(IJSONNode node) { +// if (node == null || node.isClosed()) +// return null; +// +// if (node.getNodeType() != IJSONNode.OBJECT_NODE) { +// return this.generator.generateCloseTag(node); +// } +// +// IJSONObject element = (IJSONObject) 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(); +// +// IJSONNode lastChild = (IJSONNode) 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 boolean isSelfClosedContainer(IJSONIJSONObject element) { +// if (element.getStartStructuredDocumentRegion() != null) { +// final ModelQuery mq = ModelQueryUtil.getModelQuery(element +// .getOwnerDocument()); +// if (mq != null) { +// final CMIJSONObjectDeclaration node = mq +// .getCMIJSONObjectDeclaration(element); +// if (node != null) { +// return node.getContentType() != CMIJSONObjectDeclaration.EMPTY +// && element.getStartStructuredDocumentRegion() +// .getLastRegion().getType() == JSONRegionContexts.JSON_EMPTY_TAG_CLOSE; +// } +// } +// } +// return false; +// } + + /** + */ +// private String getStartCloseTag(IJSONIJSONObject 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 + JSONNodeImpl 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; +// } +// } + } + + IJSONNode child = this.nextNode.getFirstChild(); + if (child != null) { + this.parentNode = this.nextNode; + this.nextNode = (JSONNodeImpl) child; + continue; + } + + if (this.nextNode.getNodeType() == IJSONNode.OBJECT_NODE) { + this.parentNode = this.nextNode; + this.nextNode = null; + continue; + } + + this.nextNode = (JSONNodeImpl) this.nextNode.getNextSibling(); + if (this.nextNode != null) + continue; + } + + if (this.parentNode.getNodeType() == IJSONNode.OBJECT_NODE) { + IJSONObject element = (IJSONObject) 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 = (JSONNodeImpl) this.parentNode.getNextSibling(); + this.parentNode = (JSONNodeImpl) this.parentNode.getParentNode(); + } + if (ownerNode == null) + throw new StructuredDocumentRegionManagementException(); + + short nodeType = ownerNode.getNodeType(); + if (nodeType == IJSONNode.OBJECT_NODE) { + JSONObjectImpl element = (JSONObjectImpl) 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 = (JSONNodeImpl) 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 = (JSONNodeImpl) 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 + JSONNodeImpl ownerNode = null; + JSONObjectImpl 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; +// } +// } + + IJSONNode child = this.nextNode.getFirstChild(); + if (child != null) { + this.parentNode = this.nextNode; + this.nextNode = (JSONNodeImpl) child; + continue; + } + + if (this.nextNode.getNodeType() == IJSONNode.OBJECT_NODE) { + this.parentNode = this.nextNode; + this.nextNode = null; + continue; + } + + this.nextNode = (JSONNodeImpl) this.nextNode.getNextSibling(); + if (this.nextNode != null) + continue; + } + + if (this.parentNode.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl element = (JSONObjectImpl) this.parentNode; + if (element.getEndStructuredDocumentRegion() == oldStructuredDocumentRegion) { + ownerNode = this.parentNode; + ownerEndTag = element; + break; + } + } + + this.nextNode = (JSONNodeImpl) this.parentNode.getNextSibling(); + this.parentNode = (JSONNodeImpl) 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 ownerIJSONObject + * org.w3c.dom.IJSONObject + * @param newAttr + * org.w3c.dom.Attr + * @param oldAttr + * org.w3c.dom.Attr + */ + void replaceAttr(IJSONObject ownerIJSONObject, IJSONNode newAttr, IJSONNode oldAttr) { + if (ownerIJSONObject == null) + return; + if (getStructuredDocument() == null) + return; + + JSONObjectImpl element = (JSONObjectImpl) ownerIJSONObject; + 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 == JSONRegionContexts.JSON_TAG_CLOSE +// || regionType == JSONRegionContexts.JSON_EMPTY_TAG_CLOSE +// || isNestedTagClose(regionType)) { +// isLastAttr = true; +// } +// } +// if (isLastAttr && prevRegion != null) { +// start += prevRegion.getTextEnd(); +// } else { +// start += nameRegion.getStart(); +// } +// +// // impl.resetRegions(ownerIJSONObject); +// 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 == JSONRegionContexts.JSON_TAG_CLOSE +// || regionType == JSONRegionContexts.JSON_EMPTY_TAG_CLOSE +// || isNestedTagClose(regionType)) +// 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); + } + + protected boolean isNestedTagClose(String regionType) { + boolean result = false; + return result; + } + + /** + * replaceChild method + * + * @param parentNode + * org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + * @param oldChild + * org.w3c.dom.Node + */ + void replaceChild(IJSONNode parentNode, IJSONNode newChild, IJSONNode 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; + JSONObjectImpl postIJSONObject = null; + if (oldChild != null) { + JSONNodeImpl node = (JSONNodeImpl) oldChild; + start = node.getStartOffset(); + end = node.getEndOffset(); +// if (oldChild.getNodeType() == Node.TEXT_NODE) { +// this.gapStructuredDocumentRegion = node +// .getStructuredDocumentRegion(); +// } + node.resetStructuredDocumentRegions(); // reset values from + // IStructuredDocumentRegion + } else { + JSONNodeImpl prev = (JSONNodeImpl) newChild.getPreviousSibling(); + if (prev != null) { + start = prev.getEndOffset(); + end = start; + //preTag = getCloseTag(prev); + } else { + // first child + JSONNodeImpl next = (JSONNodeImpl) newChild.getNextSibling(); + if (next != null) { + start = next.getStartOffset(); + end = start; + if (parentNode.getNodeType() == IJSONNode.OBJECT_NODE) { + // preTag = getStartCloseTag((IJSONIJSONObject) parentNode); + } + } else { + // newly having a child + if (parentNode.getNodeType() == IJSONNode.OBJECT_NODE) { + IJSONObject element = (IJSONObject) parentNode; +// if (element.isEmptyTag() +// || isSelfClosedContainer(element)) { // 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); +// postIJSONObject = 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); +// postIJSONObject = 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); +// postIJSONObject = 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); + } + } + + JSONNodeImpl node = (JSONNodeImpl) newChild; + while (node != null) { + if (node.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl element = (JSONObjectImpl) 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 = JSONNodeImpl.EMPTY_STRING; + int length = content.length(); + IStructuredDocumentRegion flatNode = null; + if (length > 0) { + buffer.append(content); + flatNode = new StructuredDocumentRegionProxy(offset, + length); + offset += length; + } + node.setStructuredDocumentRegion(flatNode); + } + + JSONNodeImpl child = (JSONNodeImpl) node.getFirstChild(); + if (child != null) { + node = child; + continue; + } + + if (node.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl element = (JSONObjectImpl) 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; + } + JSONNodeImpl next = (JSONNodeImpl) node.getNextSibling(); + if (next != null) { + node = next; + break; + } + + node = (JSONNodeImpl) node.getParentNode(); + if (node.getNodeType() != IJSONNode.OBJECT_NODE) + continue; + JSONObjectImpl element = (JSONObjectImpl) 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 (postIJSONObject != null) { + IStructuredDocumentRegion flatNode = new StructuredDocumentRegionProxy( + offset, length); + postIJSONObject.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 + + JSONNodeImpl root = (JSONNodeImpl) this.model.getDocument(); + this.parentNode = root; + this.nextNode = (JSONNodeImpl) 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 = JSONNodeImpl.EMPTY_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 JSON 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) { + JSONNodeImpl root = (JSONNodeImpl) this.model.getDocument(); + + if (oldStructuredDocumentRegions != null) { + this.parentNode = root; + this.nextNode = (JSONNodeImpl) 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 = (JSONNodeImpl) 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 = (JSONNodeImpl) root.getFirstChild(); + + insertGapStructuredDocumentRegionBefore(this.gapOffset); + // make sure to restore all backuped StructuredDocumentRegions + insertGapStructuredDocumentRegionAfter(this.gapOffset); + } + } + + /** + */ + private void updateAttrRegions(IJSONObject 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 == JSONRegionContexts.JSON_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 == JSONRegionContexts.JSON_TAG_ATTRIBUTE_EQUALS) { +// if (attr != null) +// attr.setEqualRegion(region); +// } else if (regionType == JSONRegionContexts.JSON_TAG_ATTRIBUTE_VALUE) { +// if (attr != null) { +// attr.setValueRegion(region); +// attr = null; +// } +// } +// } + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNodeImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNodeImpl.java new file mode 100644 index 0000000000..2681e3ae23 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNodeImpl.java @@ -0,0 +1,844 @@ +/******************************************************************************* + * Copyright (c) 2016 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 + * + * Balazs Banfai: Bug 154737 getUserData/setUserData support for Node + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=154737 + * + * David Carver (STAR) - bug 295127 - implement isSameNode and compareDocumentPosition methods. + * Unit Tests covered in wst.xsl XPath 2.0 tests. + * David Carver (STAR) - bug 296999 - Inefficient use of new String() + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.NodeImpl + * modified in order to process JSON Objects. + * Alina Marin <alina@mx1.ibm.com> - fixed some stuff to improve the synch between the editor and the model. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.Platform; +import org.eclipse.json.jsonpath.IJSONPath; +import org.eclipse.json.jsonpath.JSONPath; +import org.eclipse.wst.json.core.document.IJSONDocument; +import org.eclipse.wst.json.core.document.IJSONModel; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONPair; +import org.eclipse.wst.json.core.document.IJSONValue; +import org.eclipse.wst.json.core.document.JSONException; +import org.eclipse.wst.sse.core.internal.model.FactoryRegistry; +import org.eclipse.wst.sse.core.internal.provisional.AbstractNotifier; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.w3c.dom.NamedNodeMap; + +/** + * NodeImpl class + */ +public abstract class JSONNodeImpl extends AbstractNotifier implements + IJSONNode, IAdaptable { + // define one empty nodelist, for repeated use + // private final static NodeList EMPTY_NODE_LIST = new NodeListImpl(); + // DocumentPosition + // private final static short DOCUMENT_POSITION_DISCONNECTED = 0x01; + private final static short DOCUMENT_POSITION_PRECEDING = 0x02; + private final static short DOCUMENT_POSITION_FOLLOWING = 0x04; + // private final static short DOCUMENT_POSITION_CONTAINS = 0x08; + // private final static short DOCUMENT_POSITION_CONTAINED_BY = 0x10; + private final static short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; + + private boolean fDataEditable = true; + private IStructuredDocumentRegion flatNode = null; + private JSONNodeImpl nextSibling = null; + + private JSONDocumentImpl ownerDocument = null; + private JSONNodeImpl parentNode = null; + private JSONNodeImpl previousSibling = null; + // define one empty String constant for repeated use + static final String EMPTY_STRING = ""; + + /** + * NodeImpl constructor + */ + protected JSONNodeImpl() { + super(); + } + + /** + * NodeImpl constructor + * + * @param that + * NodeImpl + */ + protected JSONNodeImpl(JSONNodeImpl that) { + if (that != null) { + this.ownerDocument = that.ownerDocument; + } + } + + @Override + public Object getAdapter(Class adapter) { + final IStructuredModel model = getModel(); + return model != null ? Platform.getAdapterManager().getAdapter(model, + adapter) : null; + } + + /** + * contains method + * + * @return boolean + * @param offset + * int + */ + @Override + public boolean contains(int offset) { + return (offset >= getStartOffset() && offset < getEndOffset()); + } + + /** + * @param s + * @param tagName + * @return + */ + protected String createJSONExceptionMessage(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) { + JSONDocumentImpl document = (JSONDocumentImpl) getOwnerDocument(); + if (document == null) + return null; + return document.getCharValue(name); + } + + /** + * getChildNodes method + * + * @return org.w3c.dom.NodeList + */ + // public NodeList getChildNodes() { + // // As per JSON 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; + // } + + /** + * getContainerDocument method + * + * @return org.w3c.dom.Document + */ + public IJSONDocument getContainerDocument() { + IJSONNode parent = null; + for (IJSONNode node = this; node != null; node = parent) { + if (node.getNodeType() == IJSONNode.DOCUMENT_NODE) { + return (IJSONDocument) node; + } + /* Break out of a bad hierarchy */ + if ((parent = node.getParentNode()) == node) + break; + } + return null; + } + + /** + * getEndOffset method + * + * @return int + */ + @Override + public int getEndOffset() { + IJSONNode node = this; + while (node != null) { + if (node.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl element = (JSONObjectImpl) node; + IStructuredDocumentRegion endStructuredDocumentRegion = element + .getEndStructuredDocumentRegion(); + if (endStructuredDocumentRegion != null) + return endStructuredDocumentRegion.getEnd(); + } + + IJSONNode last = node.getLastChild(); + if (last != null) { // dig into the last + node = last; + continue; + } + + IStructuredDocumentRegion lastStructuredDocumentRegion = ((JSONNodeImpl) node) + .getStructuredDocumentRegion(); + if (lastStructuredDocumentRegion != null) + return lastStructuredDocumentRegion.getEnd(); + + IJSONNode prev = node.getPreviousSibling(); + if (prev != null) { // move to the previous + node = prev; + continue; + } + + IJSONNode parent = node.getParentNode(); + node = null; + while (parent != null) { + if (parent.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl element = (JSONObjectImpl) parent; + IStructuredDocumentRegion startStructuredDocumentRegion = element + .getStartStructuredDocumentRegion(); + if (startStructuredDocumentRegion != null) + return startStructuredDocumentRegion.getEnd(); + } + IJSONNode parentPrev = parent.getPreviousSibling(); + if (parentPrev != null) { // move to the previous + node = parentPrev; + break; + } + parent = parent.getParentNode(); + } + } + return 0; + } + + @Override + public IStructuredDocumentRegion getEndStructuredDocumentRegion() { + return null; + } + + /** + */ + @Override + public FactoryRegistry getFactoryRegistry() { + IJSONModel model = getModel(); + if (model != null) { + FactoryRegistry reg = model.getFactoryRegistry(); + if (reg != null) + return reg; + } + return null; + } + + @Override + public IJSONNode getFirstChild() { + return null; + } + + /** + * getFirstStructuredDocumentRegion method + * + */ + @Override + public IStructuredDocumentRegion getFirstStructuredDocumentRegion() { + return StructuredDocumentRegionUtil + .getStructuredDocumentRegion(this.flatNode); + } + + /** + */ + public int getIndex() { + IJSONNode parent = getParentNode(); + if (parent == null) + return -1; // error + int index = 0; + for (IJSONNode child = parent.getFirstChild(); child != null; child = child + .getNextSibling()) { + if (child == this) + return index; + index++; + } + return -1; // error + } + + @Override + public IJSONNode getLastChild() { + return null; + } + + /** + * getLastStructuredDocumentRegion method + * + */ + @Override + public IStructuredDocumentRegion getLastStructuredDocumentRegion() { + return StructuredDocumentRegionUtil + .getStructuredDocumentRegion(this.flatNode); + } + + /** + */ + public String getLocalName() { + return null; + } + + /** + * the default implementation can just refer to the owning document + */ + @Override + public IJSONModel 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; + } + + @Override + public IJSONNode getNextSibling() { + return this.nextSibling; + } + + /** + * getNodeAt method + * + * @return org.w3c.dom.Node + * @param offset + * int + */ + IJSONNode getNodeAt(int offset) { + IJSONNode parent = this; + IJSONNode child = getFirstChild(); + while (child != null) { + if (child.getEndOffset() == offset) { + return child; + } + if (child.getEndOffset() <= offset) { + child = 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; + // In case parent is a JSONPair ensure the value is not a container + // JSONNode like JSONObject or JSONArray + if (parent instanceof JSONPairImpl) { + IJSONValue value = ((JSONPairImpl) parent).getValue(); + if (value instanceof JSONObjectImpl || value instanceof JSONArrayImpl) { + parent = value; + } + } + child = parent.getFirstChild(); + } + + return parent; + } + + @Override + public IJSONDocument getOwnerDocument() { + return this.ownerDocument; + } + + @Override + public IJSONNode getParentNode() { + return this.parentNode; + } + + /** + */ + public String getPrefix() { + return null; + } + + @Override + public IJSONNode getPreviousSibling() { + return this.previousSibling; + } + + /** + */ + public String getSource() { + if (this.flatNode == null) + return JSONNodeImpl.EMPTY_STRING; + return this.flatNode.getText(); + } + + /** + * getStartOffset method + * + * @return int + */ + @Override + public int getStartOffset() { + if (this.flatNode != null) + return this.flatNode.getStart(); + JSONNodeImpl prev = (JSONNodeImpl) getPreviousSibling(); + if (prev != null) + return prev.getEndOffset(); + IJSONNode parent = getParentNode(); + if (parent != null && parent.getNodeType() == IJSONNode.OBJECT_NODE) { + JSONObjectImpl element = (JSONObjectImpl) parent; + if (element.hasStartTag()) + return element.getStartEndOffset(); + return element.getStartOffset(); + } + // final fallback to look into first child + JSONNodeImpl child = (JSONNodeImpl) getFirstChild(); + while (child != null) { + IStructuredDocumentRegion childStructuredDocumentRegion = child + .getStructuredDocumentRegion(); + if (childStructuredDocumentRegion != null) + return childStructuredDocumentRegion.getStart(); + child = (JSONNodeImpl) child.getFirstChild(); + } + return 0; + } + + @Override + public IStructuredDocumentRegion getStartStructuredDocumentRegion() { + return getFirstStructuredDocumentRegion(); + } + + /** + * Every node (indirectly) knows its structuredDocument + */ + @Override + public IStructuredDocument getStructuredDocument() { + return getModel().getStructuredDocument(); + } + + /** + */ + IStructuredDocumentRegion getStructuredDocumentRegion() { + return this.flatNode; + } + + /** + * all but attr return null + */ + public ITextRegion getValueRegion() { + return null; + } + + /** + * @throws JSONException + */ + public String getValueSource() throws JSONException { + return getNodeValue(); + } + + /* + * (non-Javadoc) + * + * @see org.w3c.dom.Node#hasAttributes() + */ + public boolean hasAttributes() { + return false; + } + + /* + * (non-Javadoc) + * + * @see org.w3c.dom.Node#hasChildNodes() + */ + @Override + 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 + */ + @Override + public IJSONNode insertBefore(IJSONNode newChild, IJSONNode refChild) + throws JSONException { + // throw new JSONException(JSONException.HIERARCHY_REQUEST_ERR, + // JSONMessages.HIERARCHY_REQUEST_ERR); + throw new JSONException(); + } + + public boolean isChildEditable() { + return false; + } + + /** + * + */ + public boolean isClosed() { + return true; + } + + /** + * isContainer method + * + * @return boolean + */ + public boolean isContainer() { + return false; + } + + public boolean isDataEditable() { + if (!fDataEditable) { + JSONModelImpl model = (JSONModelImpl) getModel(); + if (model != null && model.isReparsing()) { + return true; + } + } + return fDataEditable; + } + + /** + * @param s + * @return + */ + private String lookupMessage(short s) { + // TODO: make localized version + String result = null; + switch (s) { + // case JSONException.JSONSTRING_SIZE_ERR: + // result = JSONMessages.JSONSTRING_SIZE_ERR; + // break; + // case JSONException.HIERARCHY_REQUEST_ERR: + // result = JSONMessages.HIERARCHY_REQUEST_ERR; + // break; + // case JSONException.INDEX_SIZE_ERR: + // result = JSONMessages.INDEX_SIZE_ERR; + // break; + // case JSONException.INUSE_ATTRIBUTE_ERR: + // result = JSONMessages.INUSE_ATTRIBUTE_ERR; + // break; + // case JSONException.INVALID_ACCESS_ERR: + // result = JSONMessages.INVALID_ACCESS_ERR; + // break; + // case JSONException.INVALID_CHARACTER_ERR: + // result = JSONMessages.INVALID_CHARACTER_ERR; + // break; + // case JSONException.INVALID_MODIFICATION_ERR: + // result = JSONMessages.INVALID_MODIFICATION_ERR; + // break; + // case JSONException.INVALID_STATE_ERR: + // result = JSONMessages.INVALID_STATE_ERR; + // break; + // case JSONException.NAMESPACE_ERR: + // result = JSONMessages.NAMESPACE_ERR; + // break; + // case JSONException.NO_DATA_ALLOWED_ERR: + // result = JSONMessages.NO_DATA_ALLOWED_ERR; + // break; + // case JSONException.NO_MODIFICATION_ALLOWED_ERR: + // result = JSONMessages.NO_MODIFICATION_ALLOWED_ERR; + // break; + // case JSONException.NOT_FOUND_ERR: + // result = JSONMessages.NOT_FOUND_ERR; + // break; + // case JSONException.NOT_SUPPORTED_ERR: + // result = JSONMessages.NOT_SUPPORTED_ERR; + // break; + // case JSONException.SYNTAX_ERR: + // result = JSONMessages.SYNTAX_ERR; + // break; + // case 17:// JSONException.TYPE_MISMATCH_ERR : + // result = JSONMessages.TYPE_MISMATCH_ERR; + // break; + // case 16:// JSONException.VALIDATION_ERR : + // result = JSONMessages.VALIDATION_ERR; + // break; + // case JSONException.WRONG_DOCUMENT_ERR: + // result = JSONMessages.WRONG_DOCUMENT_ERR; + // break; + default: + result = JSONNodeImpl.EMPTY_STRING; + break; + } + return result; + } + + // protected void notifyEditableChanged() { + // JSONDocumentImpl document = (JSONDocumentImpl) getContainerDocument(); + // if (document == null) + // return; + // JSONModelImpl model = (JSONModelImpl) document.getModel(); + // if (model == null) + // return; + // model.editableChanged(this); + // } + + /** + * notifyValueChanged method + */ + protected void notifyValueChanged() { + JSONDocumentImpl document = (JSONDocumentImpl) getContainerDocument(); + if (document == null) + return; + + // syncDataEditableState(); + + JSONModelImpl model = (JSONModelImpl) document.getModel(); + if (model == null) + return; + model.valueChanged(this); + } + + /** + * removeChild method + * + * @return org.w3c.dom.Node + * @param oldChild + * org.w3c.dom.Node + */ + @Override + public IJSONNode removeChild(IJSONNode oldChild) throws JSONException { + throw new JSONException(); + // throw new JSONException(JSONException.NOT_FOUND_ERR, + // JSONMessages.NOT_FOUND_ERR); + } + + /** + * 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 IJSONNode replaceChild(IJSONNode newChild, IJSONNode oldChild) + throws JSONException { + // throw new JSONException(JSONException.HIERARCHY_REQUEST_ERR, + // JSONMessages.HIERARCHY_REQUEST_ERR); + return null; + } + + /** + * Resets children values from IStructuredDocumentRegion. + */ + void resetStructuredDocumentRegions() { + for (JSONNodeImpl child = (JSONNodeImpl) getFirstChild(); child != null; child = (JSONNodeImpl) 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) { + // IJSONNode node = (IJSONNode) getFirstChild(); + // while (node != null) { + // node.setEditable(editable, deep); + // node = (IJSONNode) node.getNextSibling(); + // } + // } + // setChildEditable(editable); + // setDataEditable(editable); + // } + + /** + * setNextSibling method + * + * @param nextSibling + * org.w3c.dom.Node + */ + protected void setNextSibling(IJSONNode nextSibling) { + this.nextSibling = (JSONNodeImpl) nextSibling; + } + + /** + * setNodeValue method + * + * @param nodeValue + * java.lang.String + */ + @Override + public void setNodeValue(String nodeValue) { + } + + protected void setOwnerDocument(IJSONDocument ownerDocument) { + this.ownerDocument = (JSONDocumentImpl) ownerDocument; + } + + /** + */ + protected void setOwnerDocument(IJSONDocument ownerDocument, boolean deep) { + this.ownerDocument = (JSONDocumentImpl) ownerDocument; + + if (deep) { + for (JSONNodeImpl child = (JSONNodeImpl) getFirstChild(); child != null; child = (JSONNodeImpl) child + .getNextSibling()) { + child.setOwnerDocument(ownerDocument, deep); + } + } + } + + /** + * setParentNode method + * + * @param parentNode + * org.w3c.dom.Node + */ + protected void setParentNode(IJSONNode parentNode) { + this.parentNode = (JSONNodeImpl) parentNode; + } + + /** + * setPreviousSibling method + * + * @param previousSibling + * org.w3c.dom.Node + */ + protected void setPreviousSibling(IJSONNode previousSibling) { + this.previousSibling = (JSONNodeImpl) previousSibling; + } + + /** + */ + public void setSource(String source) { + // 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); + // } + // } + + @Override + public int getLength() { + int result = -1; + int start = getStartOffset(); + if (start >= 0) { + int end = getEndOffset(); + if (end >= 0) { + result = end - start; + if (result < -1) { + result = -1; + } + } + } + return result; + } + + @Override + public IJSONPath getPath() { + List<String> names = new LinkedList<String>(); + getNames(names, this); + List<String> copy = names.subList(0, names.size()); + Collections.reverse(copy); + String[] segments = copy.toArray(new String[0]); + return new JSONPath(segments); + } + + private void getNames(List<String> names, IJSONNode node) { + if (node == null) { + return; + } + if (node instanceof IJSONPair) { + names.add(((IJSONPair)node).getName()); + } + getNames(names, node.getParentOrPairNode()); + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNullValueImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNullValueImpl.java new file mode 100644 index 0000000000..95f13fa9d5 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNullValueImpl.java @@ -0,0 +1,41 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.document;
+
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONNullValue;
+import org.eclipse.wst.json.core.document.JSONException;
+
+public class JSONNullValueImpl extends JSONStructureImpl implements IJSONNullValue {
+
+ @Override
+ public short getNodeType() {
+ return VALUE_NULL_NODE;
+ }
+
+ @Override
+ public String getNodeName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getNodeValue() throws JSONException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public IJSONNode cloneNode(boolean deep) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNumberValueImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNumberValueImpl.java new file mode 100644 index 0000000000..e1285306a8 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNumberValueImpl.java @@ -0,0 +1,42 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.document;
+
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONNumberValue;
+import org.eclipse.wst.json.core.document.JSONException;
+
+public class JSONNumberValueImpl extends JSONStructureImpl implements
+ IJSONNumberValue {
+
+ @Override
+ public short getNodeType() {
+ return VALUE_NUMBER_NODE;
+ }
+
+ @Override
+ public String getNodeName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getNodeValue() throws JSONException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public IJSONNode cloneNode(boolean deep) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONObjectImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONObjectImpl.java new file mode 100644 index 0000000000..c512097344 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONObjectImpl.java @@ -0,0 +1,284 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.document;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONObject;
+import org.eclipse.wst.json.core.document.IJSONPair;
+import org.eclipse.wst.json.core.document.JSONException;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+
+public class JSONObjectImpl extends JSONStructureImpl implements IJSONObject {
+
+ public JSONObjectImpl() {
+ this(null);
+ }
+
+ public JSONObjectImpl(JSONObjectImpl object) {
+ super(object);
+ }
+
+ @Override
+ public IJSONNode cloneNode(boolean deep) {
+ JSONObjectImpl cloned = new JSONObjectImpl(this);
+
+ if (deep)
+ cloneChildNodes(cloned, deep);
+
+ return cloned;
+ }
+
+ @Override
+ public short getNodeType() {
+ return IJSONNode.OBJECT_NODE;
+ }
+
+ public boolean hasEndTag() {
+ return (getLastStructuredDocumentRegion() != null);
+ }
+
+ public boolean isContainer() {
+ return true;
+ }
+
+ public boolean isEmptyTag() {
+ return false;
+ }
+
+ public boolean hasStartTag() {
+ return (getStructuredDocumentRegion() != null);
+ }
+
+ @Override
+ public String getNodeName() {
+ return "object";
+ }
+
+ @Override
+ public String getNodeValue() throws JSONException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public IJSONObject add(IJSONPair newAttr) {
+ if (newAttr == null)
+ return null;
+ JSONPairImpl attr = (JSONPairImpl) newAttr;
+ if (attr.getOwnerObject() != null)
+ return null;
+
+ // if (this.attrNodes == null)
+ // this.attrNodes = new NodeListImpl();
+ // this.attrNodes.appendNode(attr);
+ attr.setOwnerObject(this);
+ //pairs.add(newAttr);
+ notifyPairReplaced(attr, null);
+ return this;
+ }
+
+ protected void notifyPairReplaced(IJSONPair newPair, IJSONPair oldPair) {
+ JSONDocumentImpl document = (JSONDocumentImpl) getContainerDocument();
+ if (document == null)
+ return;
+ JSONModelImpl model = (JSONModelImpl) document.getModel();
+ if (model == null)
+ return;
+ model.pairReplaced(this, newPair, oldPair);
+ }
+
+ @Override
+ public IJSONObject remove(IJSONPair pair) {
+ notifyPairReplaced(null, pair);
+ return this;
+ }
+
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ String tagName = null;//getName();
+ 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();
+ }
+ // @Override
+ // public boolean getBoolean(String paramString) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public boolean getBoolean(String paramString, boolean paramBoolean) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public int getInt(String paramString) {
+ // // TODO Auto-generated method stub
+ // return 0;
+ // }
+ //
+ // @Override
+ // public int getInt(String paramString, int paramInt) {
+ // // TODO Auto-generated method stub
+ // return 0;
+ // }
+ //
+ // @Override
+ // public JsonArray getJsonArray(String paramString) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public JsonNumber getJsonNumber(String paramString) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public JsonObject getJsonObject(String paramString) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public JsonString getJsonString(String paramString) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public String getString(String paramString) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public String getString(String paramString1, String paramString2) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public boolean isNull(String paramString) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public ValueType getValueType() {
+ // return ValueType.OBJECT;
+ // }
+ //
+ // @Override
+ // public void clear() {
+ // // TODO Auto-generated method stub
+ //
+ // }
+ //
+ // @Override
+ // public boolean containsKey(Object arg0) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public boolean containsValue(Object arg0) {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public Set<java.util.Map.Entry<String, JsonValue>> entrySet() {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public JsonValue get(Object arg0) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public boolean isEmpty() {
+ // // TODO Auto-generated method stub
+ // return false;
+ // }
+ //
+ // @Override
+ // public Set<String> keySet() {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public JsonValue put(String key, JsonValue value) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public void putAll(Map<? extends String, ? extends JsonValue> arg0) {
+ // // TODO Auto-generated method stub
+ //
+ // }
+ //
+ // @Override
+ // public JsonValue remove(Object arg0) {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+ //
+ // @Override
+ // public int size() {
+ // // TODO Auto-generated method stub
+ // return 0;
+ // }
+ //
+ // @Override
+ // public Collection<JsonValue> values() {
+ // // TODO Auto-generated method stub
+ // return null;
+ // }
+
+ @Override
+ public String getSimpleValue() {
+ return null;
+ }
+
+ @Override
+ public String getValueRegionType() {
+ return null;
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONPairImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONPairImpl.java new file mode 100644 index 0000000000..857078dc24 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONPairImpl.java @@ -0,0 +1,202 @@ +/**
+ * Copyright (c) 2016 Angelo ZERR 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ * Alina Marin <alina@mx1.ibm.com> - fixed some stuff to improve the synch between the editor and the model.
+ */
+package org.eclipse.wst.json.core.internal.document;
+
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONPair;
+import org.eclipse.wst.json.core.document.IJSONStructure;
+import org.eclipse.wst.json.core.document.IJSONValue;
+import org.eclipse.wst.json.core.document.JSONException;
+import org.eclipse.wst.json.core.regions.JSONRegionContexts;
+import org.eclipse.wst.json.core.util.JSONUtil;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+
+public class JSONPairImpl extends JSONStructureImpl implements IJSONPair {
+
+ private String name;
+ private ITextRegion nameRegion = null;
+ private ITextRegion equalRegion = null;
+ private JSONObjectImpl ownerObject = null;
+ private IJSONValue value;
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ protected void setName(String name) {
+ name = name.substring(1, name.length() - 1); // remove start/end quote
+ String oldName = this.name;
+ this.name = name;
+ notify(CHANGE, this, oldName, name, getStartOffset());
+ }
+
+ @Override
+ public String getNodeName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getNodeValue() throws JSONException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public IJSONNode cloneNode(boolean deep) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public short getNodeType() {
+ return PAIR_NODE;
+ }
+
+ public void setOwnerObject(JSONObjectImpl ownerObject) {
+ this.ownerObject = ownerObject;
+ }
+
+ public JSONObjectImpl getOwnerObject() {
+ return ownerObject;
+ }
+
+ @Override
+ public IJSONValue getValue() {
+ return value;
+ }
+
+ public void setNameRegion(ITextRegion nameRegion) {
+ this.nameRegion = nameRegion;
+ }
+
+ @Override
+ public ITextRegion getNameRegion() {
+ return nameRegion;
+ }
+
+ @Override
+ public int getStartOffset() {
+ if (this.getStartStructuredDocumentRegion() == null)
+ return 0;
+ int offset = this.getStartStructuredDocumentRegion().getStartOffset();
+ // if (this.value != null) {
+ // if (value.getNodeType() == IJSONNode.ARRAY_NODE)
+ // return (offset + this.value.getStartOffset());
+ // //return (offset);
+ // }
+ // if (this.nameRegion != null) {
+ // return (offset + this.nameRegion.getStart());
+ // }
+ // if (this.equalRegion != null) {
+ // return (offset + this.equalRegion.getStart());
+ // }
+ // if (this.value != null) {
+ // return (offset + this.value.getStartOffset());
+ // }
+ return offset;
+ }
+
+ @Override
+ public int getEndOffset() {
+ if (this.getStartStructuredDocumentRegion() == null)
+ return 0;
+ int offset = this.getStartStructuredDocumentRegion().getEndOffset();
+ if (this.value != null) {
+ // if (value.getNodeType() == IJSONNode.OBJECT_NODE ||
+ // value.getNodeType() == IJSONNode.ARRAY_NODE )
+ return (this.value.getEndOffset());
+ }
+ // if (this.equalRegion != null) {
+ // return (offset + this.equalRegion.getEnd());
+ // }
+ // if (this.nameRegion != null) {
+ // return (offset + this.nameRegion.getEnd());
+ // }
+ return offset;
+ }
+
+ public void setEqualRegion(ITextRegion equalRegion) {
+ this.equalRegion = equalRegion;
+ }
+
+ @Override
+ public short getNodeValueType() {
+ if (value != null) {
+ return value.getNodeType();
+ }
+ return -1;
+ }
+
+ @Override
+ public String getSimpleValue() {
+ if (value == null) {
+ return null;
+ }
+ return value.getSimpleValue();
+ }
+
+ @Override
+ public String getValueRegionType() {
+ if (value != null) {
+ if (value.getStartStructuredDocumentRegion() == null) {
+ return null;
+ }
+ return value.getStartStructuredDocumentRegion().getType();
+ }
+ return null;
+ }
+
+ public void setValue(IJSONValue value) {
+ ((JSONValueImpl) value).setParentNode(ownerObject);
+ ((JSONValueImpl) value).setOwnerPairNode(this);
+ this.value = value;
+ notify(CHANGE, this, null, this.value, getStartOffset());
+ }
+
+ public void updateValue(IJSONValue value) {
+ IJSONValue oldValue = this.value;
+ ((JSONValueImpl) value).setParentNode(ownerObject);
+ ((JSONValueImpl) value).setOwnerPairNode(this);
+ this.value = value;
+ notify(CHANGE, null, oldValue, this.value, getStartOffset());
+ }
+
+ @Override
+ public IStructuredDocumentRegion getEndStructuredDocumentRegion() {
+ if (value != null) {
+ return ((IJSONStructure) value).getStartStructuredDocumentRegion();
+ }
+ return super.getEndStructuredDocumentRegion();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ String tagName = getName();
+ 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/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONStringValueImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONStringValueImpl.java new file mode 100644 index 0000000000..dde989c279 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONStringValueImpl.java @@ -0,0 +1,42 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.document;
+
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONStringValue;
+import org.eclipse.wst.json.core.document.JSONException;
+
+public class JSONStringValueImpl extends JSONStructureImpl implements
+ IJSONStringValue {
+
+ @Override
+ public short getNodeType() {
+ return VALUE_STRING_NODE;
+ }
+
+ @Override
+ public String getNodeName() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public String getNodeValue() throws JSONException {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public IJSONNode cloneNode(boolean deep) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONStructureImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONStructureImpl.java new file mode 100644 index 0000000000..5840d3f7f2 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONStructureImpl.java @@ -0,0 +1,554 @@ +/******************************************************************************* + * Copyright (c) 2001, 2011 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Jens Lukowski/Innoopract - initial renaming/restructuring + * David Carver (STAR) - bug 296999 - Inefficient use of new String() + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.NodeContainer + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + +import org.eclipse.wst.json.core.document.IJSONDocument; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONStructure; +import org.eclipse.wst.json.core.document.JSONException; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; + +/** + * Base class for JSON structure (Object and Array). + * + */ +public abstract class JSONStructureImpl extends JSONValueImpl implements + IJSONStructure { + + private IStructuredDocumentRegion endStructuredDocumentRegion = null; + + /** + */ + private class ChildNodesCache { + private IJSONNode 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 (IJSONNode child = firstChild; child != null; child = child + .getNextSibling()) { + this.length++; + } + } + } + + public IJSONNode 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 ChildNodesCache childNodesCache = null; + + private boolean fChildEditable = true; + JSONNodeImpl firstChild = null; + JSONNodeImpl lastChild = null; + + Object lockObject = new byte[0]; + + /** + * NodeContainer constructor + */ + protected JSONStructureImpl() { + super(); + } + + /** + * NodeContainer constructor + * + * @param that + * NodeContainer + */ + protected JSONStructureImpl(JSONStructureImpl that) { + super(that); + } + + /** + * appendChild method + * + * @return org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + */ + public IJSONNode appendChild(IJSONNode newChild) throws JSONException { + return insertBefore(newChild, null); + } + + /** + * cloneChildNodes method + * + * @param container + * org.w3c.dom.Node + * @param deep + * boolean + */ + protected void cloneChildNodes(IJSONNode newParent, boolean deep) { + if (newParent == null || newParent == this) + return; + if (!(newParent instanceof JSONStructureImpl)) + return; + + JSONStructureImpl container = (JSONStructureImpl) newParent; + container.removeChildNodes(); + + for (IJSONNode child = getFirstChild(); child != null; child = child + .getNextSibling()) { + IJSONNode 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 IJSONNode getFirstChild() { + return this.firstChild; + } + + /** + * getLastChild method + * + * @return org.w3c.dom.Node + */ + public IJSONNode 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 (JSONNodeImpl child = firstChild; child != null; child = (JSONNodeImpl) 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 IJSONNode insertBefore(IJSONNode newChild, IJSONNode refChild) + throws JSONException { + if (newChild == null) + return null; // nothing to do + if (refChild != null && refChild.getParentNode() != this) { + // throw new JSONException(JSONException.NOT_FOUND_ERR, + // JSONMessages.NOT_FOUND_ERR); + } + if (!isChildEditable()) { + // throw new + // JSONException(JSONException.NO_MODIFICATION_ALLOWED_ERR, + // JSONMessages.NO_MODIFICATION_ALLOWED_ERR); + } + if (newChild == refChild) + return newChild; // nothing to do + // new child can not be a parent of this, would cause cycle + if (isParent(newChild)) { + // throw new JSONException(JSONException.HIERARCHY_REQUEST_ERR, + // JSONMessages.HIERARCHY_REQUEST_ERR); + } + + // if (newChild.getNodeType() == DOCUMENT_FRAGMENT_NODE) { + // // insert child nodes instead + // for (IJSONNode 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 + } + + JSONNodeImpl child = (JSONNodeImpl) newChild; + JSONNodeImpl next = (JSONNodeImpl) refChild; + JSONNodeImpl prev = null; + IJSONNode oldParent = child.getParentNode(); + if (oldParent != null) + oldParent.removeChild(child); + if (next == null) { + prev = this.lastChild; + this.lastChild = child; + } else { + prev = (JSONNodeImpl) 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((IJSONDocument) this); + } else { + child.setOwnerDocument(getOwnerDocument()); + } + } + + notifyChildReplaced(child, null); + + return child; + } + + public boolean isChildEditable() { + if (!fChildEditable) { + JSONModelImpl model = (JSONModelImpl) 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 IJSONNode 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(IJSONNode newChild, IJSONNode oldChild) { + JSONDocumentImpl document = (JSONDocumentImpl) getContainerDocument(); + if (document == null) + return; + + // syncChildEditableState(newChild); + + JSONModelImpl model = (JSONModelImpl) 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 IJSONNode removeChild(IJSONNode oldChild) throws JSONException { + if (oldChild == null) + return null; + if (oldChild.getParentNode() != null && oldChild.getParentNode() != this) { + // throw new JSONException(JSONException.NOT_FOUND_ERR, + // JSONMessages.NOT_FOUND_ERR); + throw new JSONException(); + } + + if (!isChildEditable()) { + // throw new + // JSONException(JSONException.NO_MODIFICATION_ALLOWED_ERR, + // JSONMessages.NO_MODIFICATION_ALLOWED_ERR); + throw new JSONException(); + } + + // synchronized in case another thread is getting item, or length + synchronized (lockObject) { + this.childNodesCache = null; // invalidate child nodes cache + } + + JSONNodeImpl child = (JSONNodeImpl) oldChild; + if (oldChild.getParentNode() == this) { + JSONNodeImpl prev = (JSONNodeImpl) child.getPreviousSibling(); + JSONNodeImpl next = (JSONNodeImpl) 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 + // JSONException(JSONException.NO_MODIFICATION_ALLOWED_ERR, + // JSONMessages.NO_MODIFICATION_ALLOWED_ERR); + throw new JSONException(); + } + + IJSONNode nextChild = null; + for (IJSONNode child = getFirstChild(); child != null; child = nextChild) { + nextChild = child.getNextSibling(); + removeChild(child); + } + } + + /** + * replaceChild method + * + * @return org.w3c.dom.Node + * @param newChild + * org.w3c.dom.Node + * @param oldChild + * org.w3c.dom.Node + */ + public IJSONNode replaceChild(IJSONNode newChild, IJSONNode oldChild) + throws JSONException { + if (!isChildEditable()) { + // throw new + // JSONException(JSONException.NO_MODIFICATION_ALLOWED_ERR, + // JSONMessages.NO_MODIFICATION_ALLOWED_ERR); + throw new JSONException(); + } + + if (oldChild == null || oldChild == newChild) + return newChild; + if (newChild != null) + insertBefore(newChild, oldChild); + return removeChild(oldChild); + } + + // public void setChildEditable(boolean editable) { + // if (fChildEditable == editable) { + // return; + // } + // + // ReadOnlyController roc = ReadOnlyController.getInstance(); + // IJSONNode node; + // if (editable) { + // for (node = getFirstChild(); node != null; node = node.getNextSibling()) + // { + // roc.unlockNode(node); + // } + // } else { + // for (node = getFirstChild(); node != null; node = node.getNextSibling()) + // { + // roc.lockNode( node); + // } + // } + // + // fChildEditable = editable; + // notifyEditableChanged(); + // } + // + // protected void syncChildEditableState(IJSONNode child) { + // ReadOnlyController roc = ReadOnlyController.getInstance(); + // if (fChildEditable) { + // roc.unlockNode((JSONNodeImpl) child); + // } else { + // roc.lockNode((JSONNodeImpl) child); + // } + // } + + /** + * <p> + * Checks to see if the given <code>Node</code> is a parent of + * <code>this</code> node + * </p> + * + * @param possibleParent + * the possible parent <code>Node</code> of <code>this</code> + * node + * @return <code>true</code> if <code>possibleParent</code> is the parent of + * <code>this</code>, <code>false</code> otherwise. + */ + private boolean isParent(IJSONNode possibleParent) { + IJSONNode parent = this.getParentNode(); + while (parent != null && parent != possibleParent) { + parent = parent.getParentNode(); + } + + return parent == possibleParent; + } + + void setStartStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + setStructuredDocumentRegion(flatNode); + } + + void setEndStructuredDocumentRegion(IStructuredDocumentRegion flatNode) { + this.endStructuredDocumentRegion = flatNode; + } + + @Override + public int getEndOffset() { + if (this.endStructuredDocumentRegion != null) + return this.endStructuredDocumentRegion.getEnd(); + return super.getEndOffset(); + } + + public IStructuredDocumentRegion getEndStructuredDocumentRegion() { + return this.endStructuredDocumentRegion; + } + + public int getStartEndOffset() { + IStructuredDocumentRegion flatNode = getStructuredDocumentRegion(); + if (flatNode != null) + return flatNode.getEnd(); + return super.getStartOffset(); + } + + @Override + public boolean isClosed() { + return getEndStructuredDocumentRegion() != null; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONValueImpl.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONValueImpl.java new file mode 100644 index 0000000000..94c06a9de0 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONValueImpl.java @@ -0,0 +1,73 @@ +/**
+ * Copyright (c) 2016 Angelo ZERR 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.document;
+
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONPair;
+import org.eclipse.wst.json.core.document.IJSONValue;
+
+public abstract class JSONValueImpl extends JSONNodeImpl implements IJSONValue {
+
+ private IJSONPair ownerPairNode;
+
+ protected JSONValueImpl() {
+ super();
+ }
+
+ /**
+ * NodeImpl constructor
+ *
+ * @param that
+ * NodeImpl
+ */
+ protected JSONValueImpl(JSONValueImpl that) {
+ super(that);
+ }
+
+ @Override
+ public String getSimpleValue() {
+ if (getStartStructuredDocumentRegion() == null) {
+ return null;
+ }
+ return getStartStructuredDocumentRegion().getText();
+ }
+
+ @Override
+ public String getValueRegionType() {
+ if (getStartStructuredDocumentRegion() == null) {
+ return null;
+ }
+ return getStartStructuredDocumentRegion().getType();
+ }
+
+ public void setOwnerPairNode(IJSONPair pairNode) {
+ this.ownerPairNode = pairNode;
+ }
+
+ @Override
+ public IJSONNode getParentOrPairNode() {
+ if (ownerPairNode != null) {
+ return ownerPairNode;
+ }
+ IJSONNode parent = super.getParentNode();
+ return (parent != null && parent.getOwnerPairNode() != null) ? parent
+ .getOwnerPairNode() : parent;
+ }
+
+ public IJSONPair getOwnerPairNode() {
+ return ownerPairNode;
+ }
+
+ public void updateValue(JSONValueImpl value) {
+ notify(CHANGE, this.getParentNode(), this, value, getStartOffset());
+ }
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionContainer.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionContainer.java new file mode 100644 index 0000000000..b1f5090b78 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionContainer.java @@ -0,0 +1,437 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * David Carver (STAR) - bug 296999 - Inefficient use of new String() + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.StructuredDocumentRegionContainer + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + + + +import java.util.Vector; + +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; + + +class StructuredDocumentRegionContainer implements IStructuredDocumentRegion { + + private Vector flatNodes = new Vector(2); + + /** + */ + StructuredDocumentRegionContainer() { + super(); + } + + + public void addRegion(ITextRegion aRegion) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + public void adjust(int i) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + public void adjustLength(int i) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + public void adjustStart(int i) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + public void adjustTextLength(int i) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + /** + */ + 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); + } + } + + public boolean containsOffset(int i) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public boolean containsOffset(ITextRegion region, int i) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void equatePositions(ITextRegion region) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + public ITextRegion getDeepestRegionAtCharacterOffset(int offset) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public int getEnd() { + IStructuredDocumentRegion last = getLastStructuredDocumentRegion(); + if (last == null) + return 0; + return last.getEnd(); + } + + /** + */ + public int getEndOffset() { + return getEnd(); + } + + public int getEndOffset(ITextRegion containedRegion) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public ITextRegion getFirstRegion() { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + IStructuredDocumentRegion getFirstStructuredDocumentRegion() { + if (this.flatNodes.isEmpty()) + return null; + return (IStructuredDocumentRegion) this.flatNodes.elementAt(0); + } + + /** + */ + public String getFullText() { + return getText(); + } + + /** + */ + public String getFullText(ITextRegion aRegion) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public String getFullText(String context) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public ITextRegion getLastRegion() { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + 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()); + } + + public IStructuredDocumentRegion getNext() { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public int getNumberOfRegions() { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public ITextRegionContainer getParent() { + return null; + } + + public IStructuredDocument getParentDocument() { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public IStructuredDocumentRegion getPrevious() { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public ITextRegion getRegionAtCharacterOffset(int offset) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public ITextRegionList getRegions() { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public int getStart() { + IStructuredDocumentRegion first = getFirstStructuredDocumentRegion(); + if (first == null) + return 0; + return first.getStart(); + } + + /** + */ + public int getStartOffset() { + return getStart(); + } + + public int getStartOffset(ITextRegion containedRegion) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + 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 JSONNodeImpl.EMPTY_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) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public String getText(String context) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public int getTextEnd() { + return getEnd(); + } + + /** + */ + public int getTextEndOffset() { + return getTextEnd(); + } + + public int getTextEndOffset(ITextRegion containedRegion) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + * 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 isEnded() { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + 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; + } + + public boolean sameAs(IStructuredDocumentRegion region, int shift) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public boolean sameAs(ITextRegion region, int shift) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public boolean sameAs(ITextRegion oldRegion, IStructuredDocumentRegion documentRegion, ITextRegion newRegion, int shift) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void setEnded(boolean hasEnd) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void setLength(int newLength) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void setNext(IStructuredDocumentRegion newNext) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void setParentDocument(IStructuredDocument document) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void setPrevious(IStructuredDocumentRegion newPrevious) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public void setRegions(ITextRegionList embeddedRegions) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void setStart(int newStart) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + * 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(); + } + + public StructuredDocumentEvent updateRegion(Object requester, IStructuredDocumentRegion flatnode, String changes, int start, int end) { + throw new Error("intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + + public boolean isDeleted() { + // if someone "gets" these temp regions by + // accident, we'll always return "deleted". + return true; + } + + + public void setDeleted(boolean deleted) { + // do nothing + + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionManagementException.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionManagementException.java new file mode 100644 index 0000000000..675b4cdefc --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionManagementException.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.StructuredDocumentRegionManagementException + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + + + + +/** + */ +public class StructuredDocumentRegionManagementException extends RuntimeException { + + /** + * Default <code>serialVersionUID</code> + */ + private static final long serialVersionUID = 1L; + + /** + * StructuredDocumentRegionManagementException constructor + */ + public StructuredDocumentRegionManagementException() { + super("IStructuredDocumentRegion management failed.");//$NON-NLS-1$ + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionProxy.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionProxy.java new file mode 100644 index 0000000000..f8541ae7a2 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionProxy.java @@ -0,0 +1,409 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * David Carver (STAR) - bug 296999 - Inefficient use of new String() + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.StructuredDocumentRegionProxy + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionContainer; +import org.eclipse.wst.sse.core.internal.provisional.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(); + } + + public void addRegion(ITextRegion aRegion) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + public void adjust(int i) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + public void adjustLength(int i) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + public void adjustStart(int i) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + public void adjustTextLength(int i) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + public boolean containsOffset(int i) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public boolean containsOffset(ITextRegion region, int i) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void equatePositions(ITextRegion region) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + + } + + public ITextRegion getDeepestRegionAtCharacterOffset(int offset) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + 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(); + } + + public int getEndOffset(ITextRegion containedRegion) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public ITextRegion getFirstRegion() { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public String getFullText() { + return getText(); + } + + public String getFullText(ITextRegion aRegion) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public String getFullText(String context) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public ITextRegion getLastRegion() { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public int getLength() { + return this.length; + } + + public IStructuredDocumentRegion getNext() { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public int getNumberOfRegions() { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + int getOffset() { + int flatNodeOffset = 0; + if (this.flatNode != null) + flatNodeOffset = this.flatNode.getStart(); + return flatNodeOffset + this.offset; + } + + /** + */ + public ITextRegionContainer getParent() { + return null; + } + + public IStructuredDocument getParentDocument() { + return null; + // throw new Error("intentionally not implemented since should never + // be called"); + } + + public IStructuredDocumentRegion getPrevious() { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public ITextRegion getRegionAtCharacterOffset(int offset) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public ITextRegionList getRegions() { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public int getStart() { + int flatNodeOffset = 0; + if (this.flatNode != null) + flatNodeOffset = this.flatNode.getStart(); + return flatNodeOffset + this.offset; + } + + /** + */ + public int getStartOffset() { + return getStart(); + } + + public int getStartOffset(ITextRegion containedRegion) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public IStructuredDocument getStructuredDocument() { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + IStructuredDocumentRegion getStructuredDocumentRegion() { + return this.flatNode; + } + + /** + */ + public String getText() { + if (this.flatNode == null) + return JSONNodeImpl.EMPTY_STRING; + String text = this.flatNode.getText(); + if (text == null) + return JSONNodeImpl.EMPTY_STRING; + int end = this.offset + this.length; + return text.substring(this.offset, end); + } + + /** + */ + public String getText(ITextRegion aRegion) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public String getText(String context) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public int getTextEnd() { + return getEnd(); + } + + /** + */ + public int getTextEndOffset() { + return getTextEnd(); + } + + public int getTextEndOffset(ITextRegion containedRegion) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + * 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 true; + } + + public boolean isEnded() { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public boolean sameAs(IStructuredDocumentRegion region, int shift) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + public boolean sameAs(ITextRegion region, int shift) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public boolean sameAs(ITextRegion oldRegion, + IStructuredDocumentRegion documentRegion, ITextRegion newRegion, + int shift) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void setDeleted(boolean deleted) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void setEnded(boolean hasEnd) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + * had to make public, due to API transition. + */ + public void setLength(int length) { + this.length = length; + } + + public void setNext(IStructuredDocumentRegion newNext) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + void setOffset(int offset) { + this.offset = offset; + if (this.flatNode != null) + this.offset -= this.flatNode.getStart(); + } + + public void setParentDocument(IStructuredDocument document) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void setPrevious(IStructuredDocumentRegion newPrevious) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void setRegions(ITextRegionList embeddedRegions) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + public void setStart(int newStart) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } + + /** + */ + 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(); + } + + public StructuredDocumentEvent updateRegion(Object requester, + IStructuredDocumentRegion flatnode, String changes, int start, + int end) { + throw new Error( + "intentionally not implemented since should never be called"); //$NON-NLS-1$ + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionUtil.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionUtil.java new file mode 100644 index 0000000000..b5ff7f1cf7 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionUtil.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2001, 2012 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.document.StructuredDocumentRegionUtil + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.document; + + + +import org.eclipse.wst.json.core.regions.JSONRegionContexts; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; + + + +/** + * Provides convenient functions to handle IStructuredDocumentRegion and + * ITextRegion. + */ +class StructuredDocumentRegionUtil { + + /** + * 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 ""; //$NON-NLS-1$ + 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 instanceof StructuredDocumentRegionProxy) { + flatNode = ((StructuredDocumentRegionProxy)flatNode).getStructuredDocumentRegion(); + } + 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 instanceof StructuredDocumentRegionProxy) { + flatNode = ((StructuredDocumentRegionProxy)flatNode).getStructuredDocumentRegion(); + } + if (flatNode == null) + return JSONRegionContexts.UNDEFINED; + ITextRegionList regions = flatNode.getRegions(); + if (regions == null || regions.size() == 0) + return JSONRegionContexts.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 instanceof StructuredDocumentRegionProxy) { + flatNode = ((StructuredDocumentRegionProxy)flatNode).getStructuredDocumentRegion(); + } + 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 instanceof StructuredDocumentRegionProxy) { + flatNode = ((StructuredDocumentRegionProxy)flatNode).getStructuredDocumentRegion(); + } + if (flatNode == null) + return JSONRegionContexts.UNDEFINED; + ITextRegionList regions = flatNode.getRegions(); + if (regions == null || regions.size() == 0) + return JSONRegionContexts.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/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/download/HttpClientProvider.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/download/HttpClientProvider.java new file mode 100644 index 0000000000..9ab49b59fb --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/download/HttpClientProvider.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is 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: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package org.eclipse.wst.json.core.internal.download; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Date; + +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.config.RequestConfig.Builder; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpHead; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.eclipse.core.net.proxy.IProxyData; +import org.eclipse.core.net.proxy.IProxyService; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.json.internal.JSONPlugin; +import org.eclipse.wst.json.core.JSONCorePlugin; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; + +public class HttpClientProvider { + + private static final String JSON_DOWNLOAD_FOLDER = ".jsonDownloadFolder"; //$NON-NLS-1$ + public static final String PROTOCOL_FILE = "file"; //$NON-NLS-1$ + public static final String PROTOCOL_PLATFORM = "platform"; //$NON-NLS-1$ + + public static File getFile(URL url) throws IOException { + if (url == null) { + return null; + } + if (PROTOCOL_FILE.equals(url.getProtocol()) + || PROTOCOL_PLATFORM.equalsIgnoreCase(url.getProtocol())) { + File file; + try { + file = new File(new URI(url.toExternalForm())); + } catch (Exception e) { + file = new File(url.getFile()); + } + if (!file.exists()) { + return null; + } + return file; + } + File file = getCachedFile(url); + long urlLastModified = getLastModified(url); + if (file.exists()) { + long lastModified = file.lastModified(); + if (urlLastModified > lastModified) { + file = download(file, url); + if (file != null) { + file.setLastModified(urlLastModified); + } + } + } else { + file = download(file, url); + if (file != null && urlLastModified > -1) { + file.setLastModified(urlLastModified); + } + } + return file; + } + + private static File download(File file, URL url) { + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + OutputStream out = null; + file.getParentFile().mkdirs(); + try { + HttpHost target = new HttpHost(url.getHost(), url.getPort(), url.getProtocol()); + Builder builder = RequestConfig.custom(); + HttpHost proxy = getProxy(target); + if (proxy != null) { + builder = builder.setProxy(proxy); + } + RequestConfig config = builder.build(); + HttpGet request = new HttpGet(url.toURI()); + request.setConfig(config); + response = httpclient.execute(target, request); + InputStream in = response.getEntity().getContent(); + out = new BufferedOutputStream(new FileOutputStream(file)); + copy(in, out); + return file; + } catch (Exception e) { + logWarning(e); + ; + } finally { + if (out != null) { + try { + out.close(); + } catch (IOException e) { + } + } + if (response != null) { + try { + response.close(); + } catch (IOException e) { + } + } + try { + httpclient.close(); + } catch (IOException e) { + } + } + return null; + } + + private static void copy(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[8192]; + int n = 0; + while ((n = in.read(buffer)) != -1) { + out.write(buffer, 0, n); + } + } + + private static long getLastModified(URL url) { + CloseableHttpClient httpclient = HttpClients.createDefault(); + CloseableHttpResponse response = null; + try { + HttpHost target = new HttpHost(url.getHost(), url.getPort(), url.getProtocol()); + Builder builder = RequestConfig.custom(); + HttpHost proxy = getProxy(target); + if (proxy != null) { + builder = builder.setProxy(proxy); + } + RequestConfig config = builder.build(); + HttpHead request = new HttpHead(url.toURI()); + request.setConfig(config); + response = httpclient.execute(target, request); + Header[] s = response.getHeaders("last-modified"); + if (s != null && s.length > 0) { + String lastModified = s[0].getValue(); + return new Date(lastModified).getTime(); + } + } catch (Exception e) { + logWarning(e); + return -1; + } finally { + if (response != null) { + try { + response.close(); + } catch (IOException e) { + } + } + try { + httpclient.close(); + } catch (IOException e) { + } + } + return -1; + } + + private static HttpHost getProxy(HttpHost target) { + final IProxyService proxyService = getProxyService(); + IProxyData[] select = null; + try { + select = proxyService.select(new URI(target.toURI())); + } catch (URISyntaxException e) { + logWarning(e); + return null; + } + String type = target.getSchemeName(); + for (IProxyData proxyData : select) { + if (proxyData.getType().equals(type)) { + return new HttpHost(proxyData.getHost(), proxyData.getPort()); + } + } + return null; + } + + private static void logWarning(Exception e) { + IStatus status = new Status(IStatus.WARNING, JSONCorePlugin.PLUGIN_ID, e.getMessage(), e); + JSONCorePlugin.getDefault().getLog().log(status); + } + + public static IProxyService getProxyService() { + BundleContext bc = JSONPlugin.getDefault().getBundle().getBundleContext(); + ServiceReference<?> serviceReference = bc.getServiceReference(IProxyService.class.getName()); + IProxyService service = (IProxyService) bc.getService(serviceReference); + return service; + } + + private static File getCachedFile(URL url) { + IPath stateLocation = JSONCorePlugin.getDefault().getStateLocation(); + IPath downloadFolder = stateLocation.append(JSON_DOWNLOAD_FOLDER); + String urlPath = url.getPath(); + IPath filePath = downloadFolder.append(urlPath); + File file = filePath.toFile(); + return file; + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/encoding/JSONDocumentCharsetDetector.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/encoding/JSONDocumentCharsetDetector.java new file mode 100644 index 0000000000..c4a64a77f0 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/encoding/JSONDocumentCharsetDetector.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2013-2014 Angelo ZERR. + * 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: + * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation + */ +package org.eclipse.wst.json.core.internal.encoding; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Iterator; + +import org.eclipse.core.resources.IStorage; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.wst.sse.core.internal.document.DocumentReader; +import org.eclipse.wst.sse.core.internal.document.IDocumentCharsetDetector; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; +import org.eclipse.wst.sse.core.utils.StringUtils; + +public class JSONDocumentCharsetDetector implements IDocumentCharsetDetector { + + @Override + public void set(IStorage paramIStorage) throws CoreException { + // TODO Auto-generated method stub + + } + + @Override + public String getEncoding() throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getSpecDefaultEncoding() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void set(InputStream paramInputStream) { + // TODO Auto-generated method stub + + } + + @Override + public void set(Reader paramReader) { + // TODO Auto-generated method stub + + } + + @Override + public void set(IDocument paramIDocument) { + // TODO Auto-generated method stub + + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/encoding/JSONDocumentLoader.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/encoding/JSONDocumentLoader.java new file mode 100644 index 0000000000..a3050f0b4d --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/encoding/JSONDocumentLoader.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.encoding.XMLDocumentLoader + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.encoding; + +import org.eclipse.core.resources.IFile; +import org.eclipse.jface.text.IDocumentPartitioner; +import org.eclipse.wst.json.core.contenttype.ContentTypeIdForJSON; +import org.eclipse.wst.json.core.internal.parser.JSONSourceParser; +import org.eclipse.wst.json.core.internal.text.JSONStructuredDocumentReParser; +import org.eclipse.wst.json.core.internal.text.StructuredTextPartitionerForJSON; +import org.eclipse.wst.sse.core.internal.document.AbstractDocumentLoader; +import org.eclipse.wst.sse.core.internal.document.IDocumentCharsetDetector; +import org.eclipse.wst.sse.core.internal.document.IDocumentLoader; +import org.eclipse.wst.sse.core.internal.document.StructuredDocumentFactory; +import org.eclipse.wst.sse.core.internal.encoding.ContentTypeEncodingPreferences; +import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser; +import org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.text.BasicStructuredDocument; + +public class JSONDocumentLoader extends AbstractDocumentLoader { + + private final static String JSON_ID = ContentTypeIdForJSON.ContentTypeID_JSON; + + private IDocumentCharsetDetector documentEncodingDetector; + + public JSONDocumentLoader() { + super(); + } + + /* + * protected String getEncodingNameByGuess(byte[] string, int length) { + * String ianaEnc = null; ianaEnc = EncodingGuesser.guessEncoding(string, + * length); return ianaEnc; } + */ + + /** + * Default encoding. For JSON there is no spec'd default. + */ + protected String getSpecDefaultEncoding() { + return null; + } + + @Override + protected IEncodedDocument newEncodedDocument() { + IStructuredDocument structuredDocument = StructuredDocumentFactory + .getNewStructuredDocumentInstance(getParser()); + if (structuredDocument instanceof BasicStructuredDocument) { + ((BasicStructuredDocument) structuredDocument) + .setReParser(new JSONStructuredDocumentReParser()); + } + return structuredDocument; + } + + public RegionParser getParser() { + // return new JSONRegionParser(); + return new JSONSourceParser(); + } + + @Override + protected String getPreferredNewLineDelimiter(IFile file) { + String delimiter = ContentTypeEncodingPreferences + .getPreferredNewLineDelimiter(JSON_ID); + if (delimiter == null) + delimiter = super.getPreferredNewLineDelimiter(file); + return delimiter; + } + + @Override + public IDocumentCharsetDetector getDocumentEncodingDetector() { + if (documentEncodingDetector == null) { + documentEncodingDetector = new JSONDocumentCharsetDetector(); + } + return documentEncodingDetector; + } + + @Override + public IDocumentPartitioner getDefaultDocumentPartitioner() { + return new StructuredTextPartitionerForJSON(); + } + + public IDocumentLoader newInstance() { + return new JSONDocumentLoader(); + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/AbstractJSONSourceFormatter.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/AbstractJSONSourceFormatter.java new file mode 100644 index 0000000000..8d320bd61f --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/AbstractJSONSourceFormatter.java @@ -0,0 +1,903 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.AbstractCSSSourceFormatter + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.eclipse.core.runtime.Preferences; +import org.eclipse.jface.text.DefaultLineTracker; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.TextUtilities; +import org.eclipse.wst.json.core.JSONCorePlugin; +import org.eclipse.wst.json.core.cleanup.IJSONCleanupStrategy; +import org.eclipse.wst.json.core.cleanup.JSONCleanupStrategyImpl; +import org.eclipse.wst.json.core.document.IJSONArray; +import org.eclipse.wst.json.core.document.IJSONDocument; +import org.eclipse.wst.json.core.document.IJSONModel; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONObject; +import org.eclipse.wst.json.core.internal.util.RegionIterator; +import org.eclipse.wst.json.core.preferences.JSONCorePreferenceNames; +import org.eclipse.wst.json.core.regions.JSONRegionContexts; +import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; + +public abstract class AbstractJSONSourceFormatter implements + IJSONSourceFormatter { + + protected final static short GENERATE = 0; + protected final static short FORMAT = 1; + protected final static short CLEANUP = 2; + protected static short strategy; + + AbstractJSONSourceFormatter() { + super(); + } + + protected void appendDelimBefore(IJSONNode node, CompoundRegion toAppend, + StringBuilder source) { + if (node == null || source == null) + return; + if (isCleanup() && !getCleanupStrategy(node).isFormatSource()) + return; // for not formatting case on cleanup action + String delim = getLineDelimiter(node); + + boolean needIndent = !(node instanceof IJSONDocument); + if (toAppend == null) { + source.append(delim); + source.append(getIndent(node)); + if (needIndent) + source.append(getIndentString()); + } else { + String type = toAppend.getType(); + if (type == JSONRegionContexts.JSON_COMMENT) { + RegionIterator it = new RegionIterator( + toAppend.getDocumentRegion(), toAppend.getTextRegion()); + it.prev(); + ITextRegion prev = it.prev(); + int[] result = null; + if (prev == null + || (prev.getType() == JSONRegionContexts.WHITE_SPACE && (result = TextUtilities + .indexOf(DefaultLineTracker.DELIMITERS, + it.getStructuredDocumentRegion() + .getText(prev), 0))[0] >= 0)) { + // Collapse to one empty line if there's more than one. + if (result != null) { + int offset = result[0] + + DefaultLineTracker.DELIMITERS[result[1]] + .length(); + if (offset < it.getStructuredDocumentRegion() + .getText(prev).length()) { + if (TextUtilities.indexOf( + DefaultLineTracker.DELIMITERS, it + .getStructuredDocumentRegion() + .getText(prev), offset)[0] >= 0) { + source.append(delim); + } + } + source.append(delim); + source.append(getIndent(node)); + if (needIndent) + source.append(getIndentString()); + } + } else if (prev.getType() == JSONRegionContexts.JSON_COMMENT) { + String fullText = toAppend.getDocumentRegion().getFullText( + prev); + String trimmedText = toAppend.getDocumentRegion().getText( + prev); + String whiteSpaces = "";//$NON-NLS-1$ + if (fullText != null && trimmedText != null) + whiteSpaces = fullText.substring(trimmedText.length()); + int[] delimiterFound = TextUtilities.indexOf( + DefaultLineTracker.DELIMITERS, whiteSpaces, 0); + if (delimiterFound[0] != -1) { + source.append(delim); + } else { + appendSpaceBefore(node, toAppend.getText(), source); + + /* + * If two comments can't be adjusted in one + * line(combined length exceeds line width), a tab is + * also appended along with next line delimiter , we + * need to remove that. + */ + if (source.toString().endsWith(getIndentString())) { + source.delete((source.length() - getIndentString() + .length()), source.length()); + } + } + } else { + appendSpaceBefore(node, toAppend.getText(), source); + } + } else if (type == JSONRegionContexts.JSON_COMMA) { + RegionIterator it = new RegionIterator( + toAppend.getDocumentRegion(), toAppend.getTextRegion()); + it.prev(); + ITextRegion prev = it.prev(); + + Preferences preferences = JSONCorePlugin.getDefault() + .getPluginPreferences(); + + if (prev.getType() == JSONRegionContexts.WHITE_SPACE + && TextUtilities.indexOf(DefaultLineTracker.DELIMITERS, + it.getStructuredDocumentRegion().getText(prev), + 0)[0] >= 0) { + source.append(delim); + source.append(getIndent(node)); + if (needIndent) + source.append(getIndentString()); + } else if (preferences + .getInt(JSONCorePreferenceNames.LINE_WIDTH) > 0 + && (!preferences + .getBoolean(JSONCorePreferenceNames.WRAPPING_PROHIBIT_WRAP_ON_ATTR) || node + .getOwnerDocument().getNodeType() != IJSONNode.PAIR_NODE)) { + int length = getLastLineLength(node, source); + int append = 1; + if (length + append > preferences + .getInt(JSONCorePreferenceNames.LINE_WIDTH)) { + source.append(getLineDelimiter(node)); + source.append(getIndent(node)); + if (needIndent) + source.append(getIndentString()); + } + } + } else + if (type == JSONRegionContexts.JSON_OBJECT_OPEN + || type == JSONRegionContexts.JSON_OBJECT_CLOSE + || type == JSONRegionContexts.JSON_ARRAY_OPEN + || type == JSONRegionContexts.JSON_ARRAY_CLOSE) { + source.append(delim); + source.append(getIndent(node)); + } else { + source.append(delim); + source.append(getIndent(node)); + if (needIndent) + source.append(getIndentString()); + } + } + } + + protected void appendSpaceBefore(IJSONNode node, CompoundRegion toAppend, + StringBuilder source) { + if (node == null || toAppend == null || source == null) + return; + if (isCleanup() && !getCleanupStrategy(node).isFormatSource()) + return; // for not formatting case on cleanup action + String type = toAppend.getType(); + + Preferences preferences = JSONCorePlugin.getDefault() + .getPluginPreferences(); + + boolean needIndent = !(node instanceof IJSONDocument); + /*if (type == JSONRegionContexts.JSON_COMMENT) { + // check whether previous region is 'S' and has CR-LF + String delim = getLineDelimiter(node); + RegionIterator it = new RegionIterator( + toAppend.getDocumentRegion(), toAppend.getTextRegion()); + it.prev(); + ITextRegion prev = it.prev(); + // bug390904 + if (prev.getType() == JSONRegionContexts.JSON_LBRACE + && TextUtilities + .indexOf(DefaultLineTracker.DELIMITERS, + it.getStructuredDocumentRegion() + .getFullText(prev), 0)[0] > 0) { + source.append(delim); + source.append(getIndent(node)); + source.append(getIndentString()); + } else if (prev.getType() == JSONRegionContexts.WHITE_SPACE + && TextUtilities.indexOf(DefaultLineTracker.DELIMITERS, it + .getStructuredDocumentRegion().getText(prev), 0)[0] >= 0) { + source.append(delim); + source.append(getIndent(node)); + if (needIndent) + source.append(getIndentString()); + } else { + appendSpaceBefore(node, toAppend.getText(), source); + } + }*/ + if ((type == JSONRegionContexts.JSON_OBJECT_OPEN || type == JSONRegionContexts.JSON_ARRAY_OPEN) + && preferences + .getBoolean(JSONCorePreferenceNames.WRAPPING_NEWLINE_ON_OPEN_BRACE)) { + String delim = getLineDelimiter(node); + source.append(delim); + source.append(getIndent(node)); + // } else if (type == JSONRegionContexts.JSON_CURLY_BRACE_CLOSE) { + // } else if (type == JSONRegionContexts.JSON_INCLUDES || type == + // JSONRegionContexts.JSON_DASHMATCH) { + /*} else if (type == JSONRegionContexts.JSON_DECLARATION_SEPARATOR + && node instanceof IJSONStyleDeclItem) { + int n = preferences + .getInt(JSONCorePreferenceNames.FORMAT_PROP_PRE_DELIM); + // no delimiter case + while (n-- > 0) + source.append(" ");//$NON-NLS-1$ + } else if (type == JSONRegionContexts.JSON_DECLARATION_VALUE_OPERATOR + || type == JSONRegionContexts.JSON_DECLARATION_VALUE_PARENTHESIS_CLOSE) { + if (preferences.getInt(JSONCorePreferenceNames.LINE_WIDTH) > 0 + && (!preferences + .getBoolean(JSONCorePreferenceNames.WRAPPING_PROHIBIT_WRAP_ON_ATTR) || node + .getOwnerDocument().getNodeType() != IJSONNode.STYLEDECLARATION_NODE)) { + int length = getLastLineLength(node, source); + int append = 1; + if (length + append > preferences + .getInt(JSONCorePreferenceNames.LINE_WIDTH)) { + source.append(getLineDelimiter(node)); + source.append(getIndent(node)); + if (needIndent) + source.append(getIndentString()); + } + } + } else if (JSONRegionContexts.JSON_FOREIGN_ELEMENT == type + || JSONRegionContexts.JSON_DECLARATION_DELIMITER == type) { + return; + */ + } else + appendSpaceBefore(node, toAppend.getText(), source); + } + + protected void appendSpaceBefore(IJSONNode node, String toAppend, + StringBuilder source) { + if (node == null || source == null) + return; + if (isCleanup() && !getCleanupStrategy(node).isFormatSource()) + return; // for not formatting case on cleanup action + + Preferences preferences = JSONCorePlugin.getDefault() + .getPluginPreferences(); + if (toAppend != null + && toAppend.startsWith("{") && preferences.getBoolean(JSONCorePreferenceNames.WRAPPING_NEWLINE_ON_OPEN_BRACE)) {//$NON-NLS-1$ + source.append(getLineDelimiter(node)); + source.append(getIndent(node)); + return; + } else if (/* ! mgr.isOnePropertyPerLine() && */preferences + .getInt(JSONCorePreferenceNames.LINE_WIDTH) > 0 + && (!preferences + .getBoolean(JSONCorePreferenceNames.WRAPPING_PROHIBIT_WRAP_ON_ATTR) + /*|| node + .getOwnerDocument().getNodeType() != IJSONNode.STYLEDECLARATION_NODE*/)) { + int n = getLastLineLength(node, source); + int append = (toAppend != null) ? TextUtilities.indexOf( + DefaultLineTracker.DELIMITERS, toAppend, 0)[0] : 0; + if (toAppend != null) + append = (append < 0) ? toAppend.length() : append; + if (n + append + 1 > preferences + .getInt(JSONCorePreferenceNames.LINE_WIDTH)) { + source.append(getLineDelimiter(node)); + source.append(getIndent(node)); + source.append(getIndentString()); + return; + } + } + // bug412395 + // just verify if the source and the toAppend strings do not end with a + // whitespace to avoid the whitespace duplication. + if (!(source.length() > 0 && source.toString().charAt( + source.length() - 1) == ' ') + && !(toAppend.length() > 0 && toAppend + .charAt(toAppend.length() - 1) == ' ')) { + source.append(" ");//$NON-NLS-1$ + } + } + + @Override + public final StringBuilder cleanup(IJSONNode node) { + short oldStrategy = strategy; + strategy = CLEANUP; + StringBuilder source = formatProc(node); + strategy = oldStrategy; + return source; + } + + @Override + public final StringBuilder cleanup(IJSONNode node, IRegion region) { + short oldStrategy = strategy; + strategy = CLEANUP; + StringBuilder source = formatProc(node, region); + strategy = oldStrategy; + + return source; + } + + protected String decoratedIdentRegion(CompoundRegion region, + IJSONCleanupStrategy stgy) { + if (isFormat()) + return region.getText(); + + String text = null; + if (!stgy.isFormatSource()) + text = region.getFullText(); + else + text = region.getText(); + +// if (region.getType() == JSONRegionContexts.WHITE_SPACETRING +// || region.getType() == JSONRegionContexts.JSON_URI) +// return decoratedRegion(region, 0, stgy); + + if (isCleanup()) { + if (stgy.getIdentCase() == IJSONCleanupStrategy.ASIS + /*|| region.getType() == JSONRegionContexts.JSON_COMMENT*/) + return text; + else if (stgy.getIdentCase() == IJSONCleanupStrategy.UPPER) + return text.toUpperCase(); + else + return text.toLowerCase(); + } + + Preferences preferences = JSONCorePlugin.getDefault() + .getPluginPreferences(); +// if (region.getType() == JSONRegionContexts.JSON_COMMENT) +// return text; +// else if (preferences.getInt(JSONCorePreferenceNames.CASE_IDENTIFIER) == JSONCorePreferenceNames.UPPER) +// return text.toUpperCase(); +// else + return text.toLowerCase(); + } + + protected String decoratedPropNameRegion(CompoundRegion region, + IJSONCleanupStrategy stgy) { + if (isFormat()) + return region.getText(); + + String text = null; + if (!stgy.isFormatSource()) + text = region.getFullText(); + else + text = region.getText(); + +// if (region.getType() == JSONRegionContexts.WHITE_SPACETRING +// || region.getType() == JSONRegionContexts.JSON_URI) +// return decoratedRegion(region, 1, stgy); + if (isCleanup()) { + /*if (stgy.getPropNameCase() == JSONCleanupStrategy.ASIS + || region.getType() != JSONRegionContexts.JSON_DECLARATION_PROPERTY) + return text; + else*/ + if (stgy.getPropNameCase() == IJSONCleanupStrategy.UPPER) + return text.toUpperCase(); + else + return text.toLowerCase(); + } + Preferences preferences = JSONCorePlugin.getDefault() + .getPluginPreferences(); + +// if (region.getType() != JSONRegionContexts.JSON_DECLARATION_PROPERTY) +// return text; + //else + if (preferences.getInt(JSONCorePreferenceNames.CASE_PROPERTY_NAME) == JSONCorePreferenceNames.UPPER) + return text.toUpperCase(); + else + return text.toLowerCase(); + } + + protected String decoratedPropValueRegion(CompoundRegion region, + IJSONCleanupStrategy stgy) { + if (isFormat()) + return region.getText(); + + String text = null; + if (!stgy.isFormatSource()) + text = region.getFullText(); + else + text = region.getText(); + + String type = region.getType(); +// if (type == JSONRegionContexts.WHITE_SPACETRING +// || type == JSONRegionContexts.JSON_URI +// || type == JSONRegionContexts.JSON_DECLARATION_VALUE_URI) +// return decoratedRegion(region, 2, stgy); + if (isCleanup()) { + if (stgy.getPropValueCase() != IJSONCleanupStrategy.ASIS) { + //if (type == JSONRegionContexts.JSON_COMMENT) { + //} else { + if (stgy.getPropValueCase() == IJSONCleanupStrategy.UPPER) + text = text.toUpperCase(); + else + text = text.toLowerCase(); + //} + } + } + return text; + } + + protected String decoratedRegion(CompoundRegion region, int type, + IJSONCleanupStrategy stgy) { + if (isFormat()) + return region.getText(); + + Preferences preferences = JSONCorePlugin.getDefault() + .getPluginPreferences(); + + String text = null; + if (!stgy.isFormatSource()) + text = region.getFullText(); + else + text = region.getText(); + return text; + } + + @Override + public final StringBuilder format(IJSONNode node) { + short oldStrategy = strategy; + strategy = FORMAT; + StringBuilder source = formatProc(node); + strategy = oldStrategy; + + return source; + } + + @Override + public final StringBuilder format(IJSONNode node, IRegion region) { + short oldStrategy = strategy; + strategy = FORMAT; + StringBuilder source = formatProc(node, region); + strategy = oldStrategy; + return source; + } + + protected void formatChildren(IJSONNode node, StringBuilder source) { + IJSONNode child = node.getFirstChild(); + IJSONNode last = null; + while (child != null) { + // append child + IJSONSourceFormatter formatter = (IJSONSourceFormatter) ((INodeNotifier) child) + .getAdapterFor(IJSONSourceFormatter.class); + if (formatter == null) { + formatter = JSONSourceFormatterFactory.getInstance() + .getSourceFormatter(child); + } + StringBuilder childSource = ((AbstractJSONSourceFormatter) formatter) + .formatProc(child); + source.append(childSource); + last = child; + child = child.getNextSibling(); + } + } + + protected void formatChildren(IJSONNode node, IRegion region, + StringBuilder source) { + IJSONNode child = node.getFirstChild(); + int start = region.getOffset(); + int end = region.getOffset() + region.getLength(); + while (child != null) { + int curEnd = child.getEndOffset(); + StringBuilder childSource = null; + boolean toFinish = false; + if (start < curEnd) { + int curStart = child.getStartOffset(); + if (curStart < end) { + // append child + IJSONSourceFormatter formatter = (IJSONSourceFormatter) ((INodeNotifier) child) + .getAdapterFor(IJSONSourceFormatter.class); + if (formatter == null) { + formatter = JSONSourceFormatterFactory.getInstance() + .getSourceFormatter(child); + } + if (includes(region, curStart, curEnd)) + childSource = ((AbstractJSONSourceFormatter) formatter) + .formatProc(child); + else + childSource = ((AbstractJSONSourceFormatter) formatter) + .formatProc( + child, + overlappedRegion(region, curStart, + curEnd)); + } else + toFinish = true; + } + if (childSource != null) { + source.append(childSource); + } + if (toFinish) + break; + child = child.getNextSibling(); + } + } + + /** + * Generate or format source after the last child and append to string + * buffer + */ + protected abstract void formatPost(IJSONNode node, StringBuilder source); + + /** + * Generate or format source after the last child and append to string + * buffer + */ + protected abstract void formatPost(IJSONNode node, IRegion region, + StringBuilder source); + + /** + * Generate or format source before the first child and append to string + * buffer + */ + protected abstract void formatPre(IJSONNode node, StringBuilder source); + + /** + * Generate or format source before the first child and append to string + * buffer + */ + abstract protected void formatPre(IJSONNode node, IRegion region, + StringBuilder source); + + /** + * + * @return java.lang.StringBuilder + * @param node + * org.eclipse.wst.css.core.model.interfaces.IJSONNode + */ + protected final StringBuilder formatProc(IJSONNode node) { + StringBuilder source = new StringBuilder(); + formatPre(node, source); + formatChildren(node, source); + formatPost(node, source); + return source; + } + + /** + * + * @return java.lang.StringBuilder + * @param node + * org.eclipse.wst.css.core.model.interfaces.IJSONNode + * @param region + * org.eclipse.jface.text.IRegion + */ + protected StringBuilder formatProc(IJSONNode node, IRegion region) { + StringBuilder source = new StringBuilder(); + int curStart = node.getStartOffset(); + int curEnd = node.getEndOffset(); + if (node.hasChildNodes()) { + curEnd = node.getFirstChild().getStartOffset(); + if (overlaps(region, curStart, curEnd)) { + if (includes(region, curStart, curEnd)) + formatPre(node, source); + else + formatPre(node, overlappedRegion(region, curStart, curEnd), source); + } + curStart = curEnd; + curEnd = node.getLastChild().getEndOffset(); + if (overlaps(region, curStart, curEnd)) { + if (includes(region, curStart, curEnd)) + formatChildren(node, source); + else + formatChildren(node, overlappedRegion(region, curStart, curEnd), source); + } + curStart = curEnd; + curEnd = node.getEndOffset(); + if (overlaps(region, curStart, curEnd)) { + if (includes(region, curStart, curEnd)) + formatPost(node, source); + else + formatPost(node, overlappedRegion(region, curStart, curEnd), source); + } + } else if (node instanceof IJSONArray || node instanceof IJSONObject) { + curStart = node.getStartOffset(); + curEnd = node.getEndOffset(); + if (overlaps(region, curStart, curEnd)) { + if (includes(region, curStart, curEnd)) { + formatPre(node, source); + formatPost(node, source); + } else { + formatPre(node, overlappedRegion(region, curStart, curEnd), source); + formatPost(node, overlappedRegion(region, curStart, curEnd), source); + } + } + } else { + // curEnd = getChildInsertPos(node); + curEnd = node.getEndOffset() > 0 ? node.getEndOffset() : -1; + if (overlaps(region, curStart, curEnd)) { + if (includes(region, curStart, curEnd)) + formatPre(node, source); + else + formatPre(node, overlappedRegion(region, curStart, curEnd), source); + } + curStart = curEnd; + curEnd = node.getEndOffset(); + if (overlaps(region, curStart, curEnd)) { + if (includes(region, curStart, curEnd)) + formatPost(node, source); + else + formatPost(node, overlappedRegion(region, curStart, curEnd), source); + } + } + return source; + } + + /** + * Insert the method's description here. + * + * @return org.eclipse.wst.css.core.internal.cleanup.JSONCleanupStrategy + * @param node + * org.eclipse.wst.css.core.model.interfaces.IJSONNode + */ + protected IJSONCleanupStrategy getCleanupStrategy(IJSONNode node) { + IJSONCleanupStrategy currentStrategy = JSONCleanupStrategyImpl + .getInstance(); + IJSONDocument doc = node.getOwnerDocument(); + if (doc == null) + return currentStrategy; + IJSONModel model = doc.getModel(); + if (model == null) + return currentStrategy; + return currentStrategy; + } + + protected String getIndent(IJSONNode node) { + if (node == null) + return "";//$NON-NLS-1$ + IJSONNode parent = node.getParentNode(); + if (parent == null || parent instanceof IJSONDocument) + return "";//$NON-NLS-1$ + String parentIndent = getIndent(parent); + return parentIndent + getIndentString(); + } + + protected int getLastLineLength(IJSONNode node, StringBuilder source) { + if (node == null || source == null) + return 0; + String delim = getLineDelimiter(node); + String str = new String(source); + int n = str.lastIndexOf(delim); + if (n < 0) + return str.length(); + + return str.length() - n - delim.length(); + } + + String getLineDelimiter(IJSONNode node) { + IJSONModel model = node != null ? + node.getOwnerDocument().getModel() : + null; + IStructuredDocument structuredDocument = model != null ? + model.getStructuredDocument() : + null; + return structuredDocument != null ? + structuredDocument.getLineDelimiter() : + "\n"; //$NON-NLS-1$ + } + + protected CompoundRegion[] getOutsideRegions(IStructuredDocument model, + IRegion reg) { + CompoundRegion[] ret = new CompoundRegion[2]; + RegionIterator it = new RegionIterator(model, reg.getOffset()); + it.prev(); + if (it.hasPrev()) { + ITextRegion textRegion = it.prev(); + IStructuredDocumentRegion documentRegion = it + .getStructuredDocumentRegion(); + ret[0] = new CompoundRegion(documentRegion, textRegion); + } else { + ret[0] = null; + } + it.reset(model, reg.getOffset() + reg.getLength()); + if (it.hasNext()) { + ITextRegion textRegion = it.next(); + IStructuredDocumentRegion documentRegion = it + .getStructuredDocumentRegion(); + ret[1] = new CompoundRegion(documentRegion, textRegion); + } else { + ret[1] = null; + } + return ret; + } + + protected IJSONSourceFormatter getParentFormatter(IJSONNode node) { + IJSONNode parent = node.getParentNode(); + if (parent != null) { + IJSONSourceFormatter formatter = (IJSONSourceFormatter) ((INodeNotifier) parent) + .getAdapterFor(IJSONSourceFormatter.class); + if (formatter == null) { + formatter = JSONSourceFormatterFactory.getInstance() + .getSourceFormatter(parent); + } + return formatter; + } + return null; + } + + protected CompoundRegion[] getRegions(IStructuredDocument model, + IRegion reg, IRegion exceptFor, String pickupType) { + int start = reg.getOffset(); + int end = reg.getOffset() + reg.getLength(); + int startE = (exceptFor != null) ? exceptFor.getOffset() : -1; + int endE = (exceptFor != null) ? exceptFor.getOffset() + + exceptFor.getLength() : 0; + + ArrayList list = new ArrayList(); + IStructuredDocumentRegion flatNode = model + .getRegionAtCharacterOffset(start); + boolean pickuped = false; + while (flatNode != null && flatNode.getStartOffset() < end) { + ITextRegionList regionList = flatNode.getRegions(); + Iterator it = regionList.iterator(); + while (it.hasNext()) { + ITextRegion region = (ITextRegion) it.next(); + if (flatNode.getStartOffset(region) < start) + continue; + if (end <= flatNode.getStartOffset(region)) + break; + if (startE >= 0 && startE <= flatNode.getStartOffset(region) + && flatNode.getEndOffset(region) <= endE) + continue; +// if (region.getType() == JSONRegionContexts.JSON_COMMENT +// || region.getType() == JSONRegionContexts.JSON_CDC +// || region.getType() == JSONRegionContexts.JSON_CDO) +// list.add(new CompoundRegion(flatNode, region)); +// else + if (!pickuped && region.getType() == pickupType) { + list.add(new CompoundRegion(flatNode, region)); + pickuped = true; + } + } + flatNode = flatNode.getNext(); + } + if (list.size() > 0) { + CompoundRegion[] regions = new CompoundRegion[list.size()]; + list.toArray(regions); + return regions; + } + return new CompoundRegion[0]; + } + + protected CompoundRegion[] getRegionsWithoutWhiteSpaces( + IStructuredDocument model, IRegion reg, IJSONCleanupStrategy stgy) { + int start = reg.getOffset(); + int end = reg.getOffset() + reg.getLength() - 1; + ArrayList list = new ArrayList(); + IStructuredDocumentRegion flatNode = model + .getRegionAtCharacterOffset(start); + while (flatNode != null && flatNode.getStartOffset() <= end) { + ITextRegionList regionList = flatNode.getRegions(); + Iterator it = regionList.iterator(); + while (it.hasNext()) { + ITextRegion region = (ITextRegion) it.next(); + if (flatNode.getStartOffset(region) < start) + continue; + if (end < flatNode.getStartOffset(region)) + break; + if (region.getType() != JSONRegionContexts.WHITE_SPACE + || (isCleanup() && !stgy.isFormatSource())) // for + // not + // formatting + // case + // on + // cleanup + // action + list.add(new CompoundRegion(flatNode, region)); + } + flatNode = flatNode.getNext(); + } + if (list.size() > 0) { + CompoundRegion[] regions = new CompoundRegion[list.size()]; + list.toArray(regions); + return regions; + } + return new CompoundRegion[0]; + } + + public static boolean includes(IRegion region, int start, int end) { + if (region == null) + return false; + + return (region.getOffset() <= start) + && (end <= region.getOffset() + region.getLength()); + } + + /** + * + * @return boolean + */ + protected static boolean isCleanup() { + return strategy == CLEANUP; + } + + /** + * + * @return boolean + */ + protected static boolean isFormat() { + return strategy == FORMAT; + } + + protected boolean isIncludesPreEnd(IJSONNode node, IRegion region) { + return (node.getFirstChild() != null && ((IndexedRegion) node + .getFirstChild()).getStartOffset() == (region.getOffset() + region + .getLength())); + } + + static protected boolean needS(CompoundRegion region) { + return (region != null && region.getType() != JSONRegionContexts.WHITE_SPACE); + } + + public static IRegion overlappedRegion(IRegion region, int start, int end) { + if (overlaps(region, start, end)) { + int offset = (region.getOffset() <= start) ? start : region + .getOffset(); + int length = ((end <= region.getOffset() + region.getLength()) ? end + : region.getOffset() + region.getLength()) + - offset; + return new FormatRegion(offset, length); + } + return null; + } + + public static boolean overlaps(IRegion region, int start, int end) { + if (region == null) + return false; + + return (start < region.getOffset() + region.getLength()) + && (region.getOffset() < end); + } + + protected String getIndentString() { + StringBuilder indent = new StringBuilder(); + + Preferences preferences = JSONCorePlugin.getDefault() + .getPluginPreferences(); + if (preferences != null) { + char indentChar = ' '; + String indentCharPref = preferences + .getString(JSONCorePreferenceNames.INDENTATION_CHAR); + if (JSONCorePreferenceNames.TAB.equals(indentCharPref)) { + indentChar = '\t'; + } + int indentationWidth = preferences + .getInt(JSONCorePreferenceNames.INDENTATION_SIZE); + + for (int i = 0; i < indentationWidth; i++) { + indent.append(indentChar); + } + } + return indent.toString(); + } + + protected void formatValue(IJSONNode node, StringBuilder source, IJSONNode value) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + IStructuredDocument structuredDocument = node.getOwnerDocument().getModel().getStructuredDocument(); + int start = node.getStartOffset(); + int end = node.getEndOffset(); + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces(structuredDocument, + new FormatRegion(start, end - start), stgy); + if (regions.length > 2) { + for (int i = 2; i < regions.length; i++) { + source.append(decoratedRegion(regions[i], 0, stgy)); + } + } + } + + protected void formatObject(IJSONNode node, StringBuilder source, IJSONNode jsonObject) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + IStructuredDocument structuredDocument = node.getOwnerDocument().getModel().getStructuredDocument(); + IStructuredDocumentRegion[] structuredRegions = structuredDocument + .getStructuredDocumentRegions(node.getStartOffset(), node.getEndOffset()); + if (structuredRegions.length >= 2) { + int start = structuredRegions[1].getStartOffset(); + int end = node.getEndOffset(); + IJSONSourceFormatter formatter = (IJSONSourceFormatter) ((INodeNotifier) jsonObject) + .getAdapterFor(IJSONSourceFormatter.class); + if (formatter == null) { + formatter = JSONSourceFormatterFactory.getInstance().getSourceFormatter(jsonObject); + } + StringBuilder objectSource = formatter.format(jsonObject, new FormatRegion(start, end - start)); + if (objectSource != null) { + source.append(objectSource); + } + } + } + +}
\ No newline at end of file diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/CompoundRegion.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/CompoundRegion.java new file mode 100644 index 0000000000..4d72d36e97 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/CompoundRegion.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.CompoundRegion + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; + +class CompoundRegion { + + CompoundRegion(IStructuredDocumentRegion documentRegion, ITextRegion textRegion) { + super(); + this.fDocumentRegion = documentRegion; + this.fTextRegion = textRegion; + } + + IStructuredDocumentRegion getDocumentRegion() { + return fDocumentRegion; + } + + ITextRegion getTextRegion() { + return fTextRegion; + } + + String getType() { + return fTextRegion.getType(); + } + + String getText() { + return fDocumentRegion.getText(fTextRegion); + } + + // Bug 218993: Added to get text with whitespace for cleanup + // without formatting + String getFullText() { + return fDocumentRegion.getFullText(fTextRegion); + } + + int getStartOffset() { + return fDocumentRegion.getStartOffset(fTextRegion); + } + + int getEndOffset() { + return fDocumentRegion.getEndOffset(fTextRegion); + } + + + private IStructuredDocumentRegion fDocumentRegion; + private ITextRegion fTextRegion; + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/DefaultJSONSourceFormatter.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/DefaultJSONSourceFormatter.java new file mode 100644 index 0000000000..e9b6016639 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/DefaultJSONSourceFormatter.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.DefaultCSSSourceFormatter + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + + + +import org.eclipse.jface.text.IRegion; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.regions.JSONRegionContexts; + +public class DefaultJSONSourceFormatter extends AbstractJSONSourceFormatter { + + DefaultJSONSourceFormatter() { + super(); + } + + protected void appendSpaceBetween(IJSONNode node, CompoundRegion prev, CompoundRegion next, StringBuilder source) { + if (isCleanup() && !getCleanupStrategy(node).isFormatSource()) + return; // for not formatting case on cleanup action + String prevType = prev.getType(); + String nextType = next.getType(); + if (prevType == JSONRegionContexts.JSON_COLON) { + // appendSpaceBefore(node, next, source); + source.append(' '); + } + if (prevType == JSONRegionContexts.JSON_OBJECT_OPEN) { + String delim = getLineDelimiter(node); + source.append(delim); + } + if (prevType == JSONRegionContexts.JSON_ARRAY_OPEN) { + String delim = getLineDelimiter(node); + source.append(delim); + } + + } + + @Override + protected void formatPost(IJSONNode node, StringBuilder source) { + } + + @Override + protected void formatPost(IJSONNode node, IRegion region, StringBuilder source) { + } + + @Override + protected void formatPre(IJSONNode node, StringBuilder source) { + } + + @Override + protected void formatPre(IJSONNode node, IRegion region, StringBuilder source) { + } + +}
\ No newline at end of file diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/FormatRegion.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/FormatRegion.java new file mode 100644 index 0000000000..d06964448b --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/FormatRegion.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.FormatRegion + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +import org.eclipse.jface.text.IRegion; + +class FormatRegion implements IRegion { + + private int fOffset, fLength; + + FormatRegion(int offset, int length) { + super(); + set(offset, length); + } + + @Override + public int getLength() { + return fLength; + } + + @Override + public int getOffset() { + return fOffset; + } + + void set(int offset, int length) { + this.fOffset = offset; + this.fLength = length; + } + + void setLength(int newLength) { + fLength = newLength; + } + + void setOffset(int newOffset) { + fOffset = newOffset; + } +}
\ No newline at end of file diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/IJSONSourceFormatter.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/IJSONSourceFormatter.java new file mode 100644 index 0000000000..4629dff75a --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/IJSONSourceFormatter.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.CSSSourceFormatter + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +import org.eclipse.jface.text.IRegion; +import org.eclipse.wst.json.core.document.IJSONNode; + +public interface IJSONSourceFormatter { + + StringBuilder cleanup(IJSONNode node); + + StringBuilder cleanup(IJSONNode node, IRegion region); + + StringBuilder format(IJSONNode node); + + StringBuilder format(IJSONNode node, IRegion region); +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONArrayFormatter.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONArrayFormatter.java new file mode 100644 index 0000000000..6075964fc0 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONArrayFormatter.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.StyleDeclarationFormatter + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +import org.eclipse.jface.text.IRegion; +import org.eclipse.wst.json.core.cleanup.IJSONCleanupStrategy; +import org.eclipse.wst.json.core.document.IJSONArray; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONObject; +import org.eclipse.wst.json.core.regions.JSONRegionContexts; +import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; + +public class JSONArrayFormatter extends JSONStructureFormatter { + + private static JSONArrayFormatter instance; + + JSONArrayFormatter() { + super(); + } + + @Override + protected void formatChildren(IJSONNode node, StringBuilder source) { + if (node instanceof IJSONArray) { + IJSONArray array = (IJSONArray) node; + IJSONNode child = array.getFirstChild(); + while (child != null) { + if (child instanceof IJSONObject || child instanceof IJSONArray) { + formatObject(node, source, child); + if (child.getNextSibling() != null) { + int start = child.getEndOffset(); + int end = child.getNextSibling().getStartOffset(); + if (end > start) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + IStructuredDocument structuredDocument = node.getOwnerDocument().getModel() + .getStructuredDocument(); + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces(structuredDocument, + new FormatRegion(start, end - start), stgy); + for (int i = 0; i < regions.length; i++) { + source.append(decoratedRegion(regions[i], 0, stgy)); + } + } + } + String delim = getLineDelimiter(node); + source.append(delim); + source.append(getIndent(node)); + } else { + formatValue(node, source, child); + } + child = child.getNextSibling(); + if (child != null) { + source.append(getIndentString()); + } + } + } + } + + @Override + protected void formatValue(IJSONNode node, StringBuilder source, IJSONNode value) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + IStructuredDocument structuredDocument = node.getOwnerDocument().getModel().getStructuredDocument(); + int start = value.getStartOffset(); + int end = value.getEndOffset(); + if (value.getNextSibling() != null) { + int s = value.getEndOffset(); + int e = value.getNextSibling().getStartOffset(); + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces(structuredDocument, new FormatRegion(s, e - s), + stgy); + if (regions.length > 0) { + end++; + } + } + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces(structuredDocument, + new FormatRegion(start, end - start), stgy); + for (int i = 0; i < regions.length; i++) { + source.append(decoratedRegion(regions[i], 0, stgy)); + } + source.append(getLineDelimiter(node)); + source.append(getIndent(node)); + } + + @Override + protected void formatObject(IJSONNode node, StringBuilder source, IJSONNode jsonObject) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + IStructuredDocument structuredDocument = node.getOwnerDocument().getModel().getStructuredDocument(); + IStructuredDocumentRegion[] structuredRegions = structuredDocument + .getStructuredDocumentRegions(node.getStartOffset(), node.getEndOffset()); + if (structuredRegions.length >= 2) { + int start = structuredRegions[1].getStartOffset(); + int end = node.getEndOffset(); + IJSONSourceFormatter formatter = (IJSONSourceFormatter) ((INodeNotifier) jsonObject) + .getAdapterFor(IJSONSourceFormatter.class); + if (formatter == null) { + formatter = JSONSourceFormatterFactory.getInstance().getSourceFormatter(jsonObject); + } + StringBuilder objectSource = formatter.format(jsonObject, new FormatRegion(start, end - start)); + if (objectSource != null) { + source.append(objectSource); + } + } + } + + @Override + protected void formatChildren(IJSONNode node, IRegion region, StringBuilder source) { + IJSONNode child = node.getFirstChild(); + int start = region.getOffset(); + int end = region.getOffset() + region.getLength(); + while (child != null) { + int curEnd = child.getEndOffset(); + StringBuilder childSource = null; + boolean toFinish = false; + if (start < curEnd) { + int curStart = child.getStartOffset(); + if (curStart < end) { + // append child + IJSONSourceFormatter formatter = (IJSONSourceFormatter) ((INodeNotifier) child) + .getAdapterFor(IJSONSourceFormatter.class); + if (formatter == null) { + formatter = JSONSourceFormatterFactory.getInstance().getSourceFormatter(child); + } + if (includes(region, curStart, curEnd)) + childSource = ((AbstractJSONSourceFormatter) formatter).formatProc(child); + else + childSource = ((AbstractJSONSourceFormatter) formatter).formatProc(child, + overlappedRegion(region, curStart, curEnd)); + } else + toFinish = true; + } + if (childSource != null) { + source.append(childSource); + } + if (toFinish) + break; + child = child.getNextSibling(); + } + } + + @Override + protected void formatPre(IJSONNode node, StringBuilder source) { + formatPre(node, new FormatRegion(node.getStartOffset(), node.getEndOffset() - node.getStartOffset()), source); + } + + @Override + protected void formatPre(IJSONNode node, IRegion region, StringBuilder source) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + + if (region.getOffset() >= 0 && region.getLength() >= 0) { + IStructuredDocument document = node.getOwnerDocument().getModel().getStructuredDocument(); + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces(document, region, stgy); + if (regions.length > 0 && regions[0] != null + && regions[0].getType() == JSONRegionContexts.JSON_ARRAY_OPEN) { + source.append(decoratedRegion(regions[0], 0, stgy)); + } + } + if (node instanceof IJSONArray && node.hasChildNodes()) { + source.append(getLineDelimiter(node)); + source.append(getIndent(node)); + source.append(getIndentString()); + } + } + + @Override + protected void formatPost(IJSONNode node, StringBuilder source) { + formatPost(node, new FormatRegion(node.getStartOffset(), node.getEndOffset() - node.getStartOffset()), source); + } + + @Override + protected void formatPost(IJSONNode node, IRegion region, StringBuilder source) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + + if (region.getOffset() >= 0 && region.getLength() >= 0) { + IStructuredDocument document = node.getOwnerDocument().getModel().getStructuredDocument(); + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces(document, region, stgy); + if (regions.length > 0 && regions[regions.length - 1] != null) { + CompoundRegion r = regions[regions.length - 1]; + if (r != null && r.getType() == JSONRegionContexts.JSON_ARRAY_CLOSE) { + source.append(decoratedRegion(r, 0, stgy)); + } + } + } + } + public synchronized static JSONArrayFormatter getInstance() { + if (instance == null) + instance = new JSONArrayFormatter(); + return instance; + } + +}
\ No newline at end of file diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONDocumentFormatter.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONDocumentFormatter.java new file mode 100644 index 0000000000..ab0e140242 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONDocumentFormatter.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.StyleSheetFormatter + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +import org.eclipse.jface.text.IRegion; +import org.eclipse.wst.json.core.document.IJSONNode; + +public class JSONDocumentFormatter extends AbstractJSONSourceFormatter { + + private static JSONDocumentFormatter instance; + + JSONDocumentFormatter() { + super(); + } + + public synchronized static JSONDocumentFormatter getInstance() { + if (instance == null) + instance = new JSONDocumentFormatter(); + return instance; + } + + + @Override + protected void formatPost(IJSONNode node, StringBuilder source) { + } + + + @Override + protected void formatPost(IJSONNode node, IRegion region, StringBuilder source) { + } + + + @Override + protected void formatPre(IJSONNode node, StringBuilder source) { + } + + + @Override + protected void formatPre(IJSONNode node, IRegion region, StringBuilder source) { + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONFormatUtil.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONFormatUtil.java new file mode 100644 index 0000000000..cb93b7ff4f --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONFormatUtil.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.JSONFormatUtil + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.wst.json.core.document.IJSONModel; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.w3c.dom.Node; + +public class JSONFormatUtil { + + public List collectJSONNodes(IStructuredModel model, int start, int length) { + List nodes = new ArrayList(); + + IndexedRegion startNode = model.getIndexedRegion(start); + IndexedRegion endNode = model.getIndexedRegion(start + length - 1); + + if (startNode == null || endNode == null) { + return nodes; + } + + if (model instanceof IJSONModel && startNode instanceof IJSONNode + && endNode instanceof IJSONNode) { + // JSON model + IJSONNode ca = getCommonAncestor((IJSONNode) startNode, + (IJSONNode) endNode); + if (ca != null) { + for (IJSONNode node = ca.getFirstChild(); node != null + && start + length < ((IndexedRegion) node) + .getStartOffset(); node = node.getNextSibling()) { + if (start < ((IndexedRegion) node).getEndOffset()) { + nodes.add(node); + } + } + } + } + return nodes; + } + + /** + * getCommonAncestor method + * + * @return org.w3c.dom.Node + * @param node + * org.w3c.dom.Node + */ + private Node getCommonAncestor(Node node1, Node node2) { + if (node1 == null || node2 == null) + return null; + + for (Node na = node2; na != null; na = na.getParentNode()) { + for (Node ta = node1; ta != null; ta = ta.getParentNode()) { + if (ta == na) + return ta; + } + } + return null; // not found + } + + private IJSONNode getCommonAncestor(IJSONNode nodeA, IJSONNode nodeB) { + if (nodeA == null || nodeB == null) { + return null; + } + + for (IJSONNode na = nodeA; na != null; na = na.getParentNode()) { + for (IJSONNode ta = nodeB; ta != null; ta = ta.getParentNode()) { + if (ta == na) { + return ta; + } + } + } + + return null; // not found + } + + /** + */ + public void replaceSource(IStructuredModel model, int offset, int length, + String source) { + if (model == null) + return; + IStructuredDocument structuredDocument = model.getStructuredDocument(); + if (structuredDocument == null) + return; + if (offset >= 0 && length >= 0 + && offset + length <= structuredDocument.getLength()) { + if (structuredDocument.containsReadOnly(offset, length)) + return; + if (source == null) + source = new String(); + // We use 'structuredDocument' as the requester object just so + // this and the other + // format-related 'repalceText' (in replaceSource) can use the + // same requester. + // Otherwise, if requester is not identical, + // the undo group gets "broken" into multiple pieces based + // on the requesters being different. Technically, any unique, + // common + // requester object would work. + structuredDocument.replaceText(structuredDocument, offset, length, + source); + } + } + + public synchronized static JSONFormatUtil getInstance() { + if (fInstance == null) { + fInstance = new JSONFormatUtil(); + } + return fInstance; + } + + private JSONFormatUtil() { + super(); + } + + private static JSONFormatUtil fInstance; +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONObjectFormatter.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONObjectFormatter.java new file mode 100644 index 0000000000..d0db7257eb --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONObjectFormatter.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.StyleRuleFormatter + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +import org.eclipse.jface.text.IRegion; +import org.eclipse.wst.json.core.cleanup.IJSONCleanupStrategy; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.regions.JSONRegionContexts; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + +public class JSONObjectFormatter extends JSONStructureFormatter { + + private static JSONObjectFormatter instance; + + JSONObjectFormatter() { + super(); + } + + @Override + protected void formatPost(IJSONNode node, StringBuilder source) { + formatPost(node, new FormatRegion(node.getStartOffset(), node.getEndOffset() - node.getStartOffset()), source); + } + + @Override + protected void formatPost(IJSONNode node, IRegion region, StringBuilder source) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + + if (region.getOffset() >= 0 && region.getLength() >= 0) { + IStructuredDocument document = node.getOwnerDocument().getModel().getStructuredDocument(); + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces(document, region, stgy); + if (regions.length > 0 && regions[regions.length - 1] != null) { + CompoundRegion r = regions[regions.length - 1]; + if (r != null && r.getType() == JSONRegionContexts.JSON_OBJECT_CLOSE) { + source.append(getLineDelimiter(node)); + source.append(getIndent(node)); + source.append(decoratedRegion(r, 0, stgy)); + } + } + } + } + + @Override + protected void formatPre(IJSONNode node, StringBuilder source) { + formatPre(node, new FormatRegion(node.getStartOffset(), node.getEndOffset() - node.getStartOffset()), source); + } + + @Override + protected void formatPre(IJSONNode node, IRegion region, StringBuilder source) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + + if (region.getOffset() >= 0 && region.getLength() >= 0) { + IStructuredDocument document = node.getOwnerDocument().getModel().getStructuredDocument(); + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces(document, region, stgy); + if (regions.length > 0 && regions[0] != null + && regions[0].getType() == JSONRegionContexts.JSON_OBJECT_OPEN) { + source.append(decoratedRegion(regions[0], 0, stgy)); + } + } + source.append(getLineDelimiter(node)); + source.append(getIndent(node)); + source.append(getIndentString()); + } + + public synchronized static JSONObjectFormatter getInstance() { + if (instance == null) + instance = new JSONObjectFormatter(); + return instance; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONPairFormatter.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONPairFormatter.java new file mode 100644 index 0000000000..39b3e07af2 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONPairFormatter.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.StyleDeclarationFormatter + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +import org.eclipse.jface.text.IRegion; +import org.eclipse.wst.json.core.cleanup.IJSONCleanupStrategy; +import org.eclipse.wst.json.core.document.IJSONArray; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.document.IJSONObject; +import org.eclipse.wst.json.core.document.IJSONPair; +import org.eclipse.wst.json.core.document.IJSONValue; +import org.eclipse.wst.json.core.regions.JSONRegionContexts; +import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + +public class JSONPairFormatter extends JSONStructureFormatter { + + private static AbstractJSONSourceFormatter instance; + + JSONPairFormatter() { + super(); + } + + @Override + protected void formatChildren(IJSONNode node, StringBuilder source) { + if (node instanceof IJSONPair) { + IJSONPair pair = (IJSONPair) node; + IJSONValue value = pair.getValue(); + if (value instanceof IJSONObject || value instanceof IJSONArray) { + formatObject(node, source, value); + } else { + formatValue(node, source, value); + } + } + } + + @Override + protected void formatChildren(IJSONNode node, IRegion region, StringBuilder source) { + IJSONNode child = node.getFirstChild(); + int start = region.getOffset(); + int end = region.getOffset() + region.getLength(); + while (child != null) { + int curEnd = child.getEndOffset(); + StringBuilder childSource = null; + boolean toFinish = false; + if (start < curEnd) { + int curStart = child.getStartOffset(); + if (curStart < end) { + // append child + IJSONSourceFormatter formatter = (IJSONSourceFormatter) ((INodeNotifier) child) + .getAdapterFor(IJSONSourceFormatter.class); + if (formatter == null) { + formatter = JSONSourceFormatterFactory.getInstance().getSourceFormatter(child); + } + if (includes(region, curStart, curEnd)) + childSource = ((AbstractJSONSourceFormatter) formatter).formatProc(child); + else + childSource = ((AbstractJSONSourceFormatter) formatter).formatProc(child, + overlappedRegion(region, curStart, curEnd)); + } else + toFinish = true; + } + if (childSource != null) { + source.append(childSource); + } + if (toFinish) + break; + child = child.getNextSibling(); + } + } + + @Override + protected void formatPre(IJSONNode node, StringBuilder source) { + formatPre(node, new FormatRegion(node.getStartOffset(), node.getEndOffset() - node.getStartOffset()), source); + } + + @Override + protected void formatPre(IJSONNode node, IRegion region, StringBuilder source) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + + IStructuredDocument structuredDocument = node.getOwnerDocument().getModel().getStructuredDocument(); + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces(structuredDocument, region, stgy); + int length = (regions.length >= 2) ? 2 : regions.length; + for (int i = 0; i < length; i++) { + source.append(decoratedRegion(regions[i], 0, stgy)); + if (regions[i].getType() == JSONRegionContexts.JSON_COLON) { + source.append(' '); + } + } + } + + @Override + protected void formatPost(IJSONNode node, IRegion region, StringBuilder source) { + if (node.getNextSibling() != null) { + int start = node.getEndOffset(); + int end = node.getNextSibling().getStartOffset(); + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + IStructuredDocument structuredDocument = node.getOwnerDocument().getModel().getStructuredDocument(); + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces(structuredDocument, + new FormatRegion(start, end - start), stgy); + for (int i = 0; i < regions.length; i++) { + source.append(decoratedRegion(regions[i], 0, stgy)); + } + String delim = getLineDelimiter(node); + source.append(delim); + if (node.getNextSibling() != null) { + source.append(getIndent(node)); + } else { + source.append(getIndent(node.getParentNode())); + } + } + } + + @Override + protected void formatPost(IJSONNode node, StringBuilder source) { + formatPost(node, new FormatRegion(node.getStartOffset(), node.getEndOffset() - node.getStartOffset()), source); + } + + public synchronized static AbstractJSONSourceFormatter getInstance() { + if (instance == null) + instance = new JSONPairFormatter(); + return instance; + } + +}
\ No newline at end of file diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONSourceFormatterFactory.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONSourceFormatterFactory.java new file mode 100644 index 0000000000..8a4cbecb05 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONSourceFormatterFactory.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.CSSSourceFormatterFactory + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier; + +public class JSONSourceFormatterFactory { + + private JSONSourceFormatterFactory() { + super(); + } + + public IJSONSourceFormatter getSourceFormatter(INodeNotifier target) { + IJSONNode node = (IJSONNode) target; + short type = node.getNodeType(); + switch (type) { + case IJSONNode.DOCUMENT_NODE: + return JSONDocumentFormatter.getInstance(); + case IJSONNode.OBJECT_NODE: + return JSONObjectFormatter.getInstance(); + case IJSONNode.ARRAY_NODE: + return JSONArrayFormatter.getInstance(); + case IJSONNode.PAIR_NODE: + return JSONPairFormatter.getInstance(); + default: + return UnknownRuleFormatter.getInstance(); + } + } + + public synchronized static JSONSourceFormatterFactory getInstance() { + if (fInstance == null) { + fInstance = new JSONSourceFormatterFactory(); + } + return fInstance; + } + + private static JSONSourceFormatterFactory fInstance; +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONStructureFormatter.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONStructureFormatter.java new file mode 100644 index 0000000000..f72a657420 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONStructureFormatter.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.DeclContainerFormatter + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +public class JSONStructureFormatter extends DefaultJSONSourceFormatter { + + JSONStructureFormatter() { + super(); + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/UnknownRuleFormatter.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/UnknownRuleFormatter.java new file mode 100644 index 0000000000..1e0a6a50d5 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/UnknownRuleFormatter.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2004, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.formatter.UnknownRuleFormatter + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.format; + +import org.eclipse.jface.text.IRegion; +import org.eclipse.wst.json.core.cleanup.IJSONCleanupStrategy; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + +/** + * + */ +public class UnknownRuleFormatter extends DefaultJSONSourceFormatter { + + private static UnknownRuleFormatter instance; + + UnknownRuleFormatter() { + super(); + } + + @Override + protected void formatPre(IJSONNode node, StringBuilder source) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + + int start = ((IndexedRegion) node).getStartOffset(); + int end = ((IndexedRegion) node).getEndOffset(); + if (end > 0) { // format source + IStructuredDocument structuredDocument = node.getOwnerDocument() + .getModel().getStructuredDocument(); + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces( + structuredDocument, new FormatRegion(start, end - start), + stgy); + for (int i = 0; i < regions.length; i++) { + if (i != 0) + appendSpaceBefore(node, regions[i], source); + source.append(decoratedPropValueRegion(regions[i], stgy)); + } + } +// else { // generate source +// JSONUnknownRule rule = (JSONUnknownRule) node; +// source.append(rule.getCssText()); +// } + } + + @Override + protected void formatPre(IJSONNode node, IRegion region, StringBuilder source) { + IJSONCleanupStrategy stgy = getCleanupStrategy(node); + + IStructuredDocument structuredDocument = node.getOwnerDocument() + .getModel().getStructuredDocument(); + CompoundRegion[] regions = getRegionsWithoutWhiteSpaces( + structuredDocument, region, stgy); + CompoundRegion[] outside = getOutsideRegions(structuredDocument, region); + for (int i = 0; i < regions.length; i++) { + if (i != 0 || needS(outside[0])) + appendSpaceBefore(node, regions[i], source); + source.append(decoratedPropValueRegion(regions[i], stgy)); + } + if (needS(outside[1]) && !isIncludesPreEnd(node, region)) + appendSpaceBefore(node, outside[1], source); + } + + public synchronized static UnknownRuleFormatter getInstance() { + if (instance == null) + instance = new UnknownRuleFormatter(); + return instance; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/modelhandler/JSONModelLoader.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/modelhandler/JSONModelLoader.java new file mode 100644 index 0000000000..2bbaa5d83d --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/modelhandler/JSONModelLoader.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2013-2014 Angelo ZERR. + * 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: + * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation + */ +package org.eclipse.wst.json.core.internal.modelhandler; + +import org.eclipse.wst.json.core.internal.document.JSONModelImpl; +import org.eclipse.wst.json.core.internal.encoding.JSONDocumentLoader; +import org.eclipse.wst.sse.core.internal.document.IDocumentLoader; +import org.eclipse.wst.sse.core.internal.model.AbstractModelLoader; +import org.eclipse.wst.sse.core.internal.provisional.IModelLoader; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; + +/** + * JSON Model loader. + */ +public class JSONModelLoader extends AbstractModelLoader { + + public JSONModelLoader() { + super(); + } + + @Override + public IStructuredModel newModel() { + return new JSONModelImpl(); + } + + @Override + public IModelLoader newInstance() { + return new JSONModelLoader(); + } + + @Override + public IDocumentLoader getDocumentLoader() { + if (documentLoaderInstance == null) { + documentLoaderInstance = new JSONDocumentLoader(); + } + return documentLoaderInstance; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/modelhandler/ModelHandlerForJSON.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/modelhandler/ModelHandlerForJSON.java new file mode 100644 index 0000000000..9c2c6a2c4f --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/modelhandler/ModelHandlerForJSON.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.modelhandler.ModelHandlerForXML + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.modelhandler; + +import org.eclipse.wst.json.core.internal.encoding.JSONDocumentCharsetDetector; +import org.eclipse.wst.json.core.internal.encoding.JSONDocumentLoader; +import org.eclipse.wst.json.core.modelhandler.IIModelHandlerForJSON; +import org.eclipse.wst.sse.core.internal.document.IDocumentCharsetDetector; +import org.eclipse.wst.sse.core.internal.document.IDocumentLoader; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.AbstractModelHandler; +import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler; +import org.eclipse.wst.sse.core.internal.provisional.IModelLoader; + +public class ModelHandlerForJSON extends AbstractModelHandler implements + IIModelHandlerForJSON { + + /** + * Needs to match what's in plugin registry. In fact, can be overwritten at + * run time with what's in registry! (so should never be 'final') + */ + private static final String AssociatedContentTypeID = "org.eclipse.wst.json.core.jsonsource"; //$NON-NLS-1$ + + /** + * Needs to match what's in plugin registry. In fact, can be overwritten at + * run time with what's in registry! (so should never be 'final') + */ + private static final String ModelHandlerID = "org.eclipse.wst.json.core.modelhandler"; //$NON-NLS-1$ + + public ModelHandlerForJSON() { + super(); + setId(ModelHandlerID); + setAssociatedContentTypeId(AssociatedContentTypeID); + } + + @Override + public IDocumentCharsetDetector getEncodingDetector() { + return new JSONDocumentCharsetDetector(); + } + + @Override + public IDocumentLoader getDocumentLoader() { + return new JSONDocumentLoader(); + } + + @Override + public IModelLoader getModelLoader() { + return new JSONModelLoader(); + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/AbstractJSONTokenizer.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/AbstractJSONTokenizer.java new file mode 100644 index 0000000000..e2dff15068 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/AbstractJSONTokenizer.java @@ -0,0 +1,68 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.parser;
+
+import java.util.Stack;
+
+import org.eclipse.wst.json.core.regions.JSONRegionContexts;
+
+public abstract class AbstractJSONTokenizer implements JSONRegionContexts,
+ IJSONTokenizer {
+
+ protected final Stack<Boolean> jsonContextStack = new Stack<Boolean>();
+
+ protected String startElement(boolean isArray) {
+ jsonContextStack.push(isArray);
+ if (isArray) {
+ setJSONArrayState(); // yybegin(ST_JSON_ARRAY);
+ return JSON_ARRAY_OPEN;
+ }
+ setJSONObjectState(); // yybegin(ST_JSON_OBJECT);
+ return JSON_OBJECT_OPEN;
+ }
+
+ protected String endElement(boolean isArray) {
+ boolean arrayContent = isArrayParsing();
+ if (!jsonContextStack.isEmpty()) {
+ jsonContextStack.pop();
+ }
+ if (isArray) {
+ if (arrayContent) {
+ if (!jsonContextStack.isEmpty()) {
+ setJSONValueState(); // yybegin(ST_JSON_VALUE);
+ }
+ return JSON_ARRAY_CLOSE;
+ }
+ return UNDEFINED;
+ }
+ if (!arrayContent) {
+ if (!jsonContextStack.isEmpty()) {
+ setJSONValueState(); // yybegin(ST_JSON_VALUE);
+ }
+ return JSON_OBJECT_CLOSE;
+ }
+ return UNDEFINED;
+ }
+
+ public boolean isArrayParsing() {
+ if (jsonContextStack.isEmpty()) {
+ return false;
+ }
+ return jsonContextStack.peek();
+ }
+
+ protected abstract void setJSONArrayState();
+
+ protected abstract void setJSONObjectState();
+
+ protected abstract void setJSONValueState();
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/IJSONLineTokenizer.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/IJSONLineTokenizer.java new file mode 100644 index 0000000000..1cd07c5e89 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/IJSONLineTokenizer.java @@ -0,0 +1,16 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.parser;
+
+public interface IJSONLineTokenizer extends IJSONTokenizer {
+
+ int getLine();
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/IJSONTokenizer.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/IJSONTokenizer.java new file mode 100644 index 0000000000..5afca66cb6 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/IJSONTokenizer.java @@ -0,0 +1,33 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.parser;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+
+public interface IJSONTokenizer {
+
+ void setInitialState(int initialState);
+
+ void setInitialBufferSize(int bufsize);
+
+ boolean isEOF();
+
+ void reset(Reader reader, int i);
+
+ ITextRegion getNextToken() throws IOException;
+
+ void reset(char[] cs);
+
+ int getOffset();
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONLineTokenizer.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONLineTokenizer.java new file mode 100644 index 0000000000..95428ca5f8 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONLineTokenizer.java @@ -0,0 +1,968 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+/* The following code was generated by JFlex 1.4.2 on 18/03/15 22:51 */
+
+/*nlsXXX*/
+package org.eclipse.wst.json.core.internal.parser;
+
+import java.io.CharArrayReader;
+import java.io.IOException;
+import java.util.Stack;
+
+import org.eclipse.wst.json.core.internal.parser.regions.JSONTextRegionFactory;
+import org.eclipse.wst.json.core.regions.JSONRegionContexts;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+
+
+/**
+ * This class is a scanner generated by
+ * <a href="http://www.jflex.de/">JFlex</a> 1.4.2
+ * on 18/03/15 22:51 from the specification file
+ * <tt>JSONLineTokenizer.jflex</tt>
+ */
+public class JSONLineTokenizer extends AbstractJSONTokenizer {
+
+ /** This character denotes the end of file */
+ public static final int YYEOF = -1;
+
+ /** initial size of the lookahead buffer */
+ private static final int ZZ_BUFFERSIZE = 16384;
+
+ /** lexical states */
+ public static final int ST_JSON_OBJECT_COLON = 6;
+ public static final int ST_JSON_ARRAY = 4;
+ public static final int YYINITIAL = 0;
+ public static final int ST_JSON_COMMENT = 10;
+ public static final int ST_JSON_VALUE = 8;
+ public static final int ST_JSON_OBJECT = 2;
+
+ /**
+ * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
+ * ZZ_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 static final int ZZ_LEXSTATE[] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5
+ };
+
+ /**
+ * Translates characters to character classes
+ */
+ private static final String ZZ_CMAP_PACKED =
+ "\10\0\1\0\1\16\1\20\1\0\1\14\1\7\22\0\1\16\1\12"+
+ "\1\17\4\12\1\15\2\12\1\27\1\6\1\25\1\1\1\4\1\26"+
+ "\1\2\11\3\1\30\6\12\1\37\3\12\1\5\1\35\5\12\1\40"+
+ "\1\12\1\42\3\12\1\32\1\41\1\31\1\33\5\12\1\23\1\13"+
+ "\1\24\1\12\1\12\1\12\1\36\3\10\1\11\1\34\5\12\1\40"+
+ "\1\12\1\42\3\12\1\32\1\41\1\31\1\33\5\12\1\21\1\12"+
+ "\1\22\1\12\1\0\uff80\12";
+
+ /**
+ * 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\4\1\1\0\1\2\1\1\1\3\1\4\1\5"+
+ "\1\2\1\1\2\2\2\6\2\2\1\7\3\2\1\10"+
+ "\1\11\1\12\3\0\1\13\4\0\1\14\10\0\1\13"+
+ "\1\6\1\0\1\6\2\0\1\14\2\0\1\14\3\0"+
+ "\1\12\3\0\1\15\1\16\14\0";
+
+ private static int [] zzUnpackAction() {
+ int [] result = new int[74];
+ 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\43\0\106\0\151\0\214\0\257\0\322\0\322"+
+ "\0\322\0\322\0\322\0\365\0\u0118\0\u013b\0\u015e\0\u0181"+
+ "\0\u01a4\0\u01c7\0\u01ea\0\322\0\u020d\0\u0230\0\u0253\0\322"+
+ "\0\322\0\u0276\0\u0299\0\u013b\0\u02bc\0\322\0\u02df\0\u0302"+
+ "\0\u01c7\0\u0325\0\322\0\u01ea\0\u0348\0\u036b\0\u038e\0\u03b1"+
+ "\0\u03d4\0\u03f7\0\u041a\0\u013b\0\u043d\0\u0460\0\u0460\0\u0483"+
+ "\0\u04a6\0\u01c7\0\u04c9\0\u04ec\0\u01ea\0\u050f\0\u0532\0\u0555"+
+ "\0\322\0\u0578\0\u059b\0\u05be\0\322\0\322\0\u05e1\0\u0604"+
+ "\0\u0627\0\u064a\0\u066d\0\u0690\0\u06b3\0\u06d6\0\u06f9\0\u071c"+
+ "\0\u073f\0\u0762";
+
+ private static int [] zzUnpackRowMap() {
+ int [] result = new int[74];
+ 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 = zzUnpackTrans();
+
+ private static final String ZZ_TRANS_PACKED_0 =
+ "\20\7\1\10\1\11\1\12\1\13\2\7\1\14\23\7"+
+ "\1\15\6\7\1\15\1\16\1\15\1\7\1\12\3\7"+
+ "\1\14\15\7\1\17\1\20\1\21\3\7\1\15\5\7"+
+ "\1\22\1\15\1\23\1\15\1\11\1\12\1\13\1\24"+
+ "\1\7\1\14\2\7\1\25\2\7\2\26\4\7\1\27"+
+ "\7\7\1\15\6\7\1\15\1\7\1\15\1\7\1\12"+
+ "\3\7\1\14\1\7\1\30\13\7\1\17\1\20\1\21"+
+ "\3\7\1\15\5\7\1\22\1\15\1\23\1\15\1\11"+
+ "\1\12\1\13\1\24\1\31\1\14\2\7\1\25\2\7"+
+ "\2\26\4\7\1\27\20\7\1\10\1\7\1\12\3\7"+
+ "\1\14\14\7\71\0\1\32\1\33\22\0\1\15\6\0"+
+ "\1\15\1\0\1\15\23\0\6\34\1\0\3\34\1\35"+
+ "\1\0\2\34\1\36\1\0\22\34\2\0\1\20\1\21"+
+ "\43\0\1\37\1\40\3\0\1\40\33\0\2\21\1\37"+
+ "\1\40\3\0\1\40\32\0\6\41\1\0\3\41\1\42"+
+ "\1\0\1\43\2\41\1\0\22\41\1\0\6\44\1\0"+
+ "\3\44\1\45\1\0\2\44\1\43\1\0\22\44\32\0"+
+ "\1\46\46\0\2\47\36\0\1\50\7\0\20\32\1\0"+
+ "\22\32\27\33\1\51\13\33\1\0\1\34\2\52\3\34"+
+ "\1\53\2\52\1\34\1\35\3\34\1\54\14\34\1\52"+
+ "\1\34\1\52\4\34\2\0\2\55\40\0\1\56\2\57"+
+ "\2\0\1\56\35\0\1\41\2\60\3\41\1\61\2\60"+
+ "\1\41\1\42\1\41\1\62\16\41\1\60\1\41\1\60"+
+ "\4\41\1\0\1\44\2\63\3\44\1\64\2\63\1\44"+
+ "\1\45\3\44\1\65\14\44\1\63\1\44\1\63\4\44"+
+ "\33\0\1\66\47\0\1\67\42\0\1\70\2\0\26\33"+
+ "\1\71\1\51\13\33\1\0\1\34\2\72\4\34\2\72"+
+ "\1\34\1\35\3\34\1\36\14\34\1\72\1\34\1\72"+
+ "\4\34\1\0\6\34\1\0\3\34\1\35\1\0\2\34"+
+ "\1\36\23\34\2\0\2\55\1\0\1\40\3\0\1\40"+
+ "\33\0\2\57\40\0\1\41\2\73\4\41\2\73\1\41"+
+ "\1\42\1\41\1\43\16\41\1\73\1\41\1\73\4\41"+
+ "\1\0\6\41\1\0\3\41\1\42\1\0\1\43\25\41"+
+ "\1\0\1\44\2\74\4\44\2\74\1\44\1\45\3\44"+
+ "\1\43\14\44\1\74\1\44\1\74\4\44\1\0\6\44"+
+ "\1\0\3\44\1\45\1\0\2\44\1\43\23\44\5\0"+
+ "\1\75\3\0\1\75\72\0\1\66\41\0\1\76\3\0"+
+ "\1\34\2\77\4\34\2\77\1\34\1\35\3\34\1\36"+
+ "\14\34\1\77\1\34\1\77\4\34\1\0\1\41\2\100"+
+ "\4\41\2\100\1\41\1\42\1\41\1\43\16\41\1\100"+
+ "\1\41\1\100\4\41\1\0\1\44\2\101\4\44\2\101"+
+ "\1\44\1\45\3\44\1\43\14\44\1\101\1\44\1\101"+
+ "\4\44\1\0\1\34\2\102\4\34\2\102\1\34\1\35"+
+ "\3\34\1\36\14\34\1\102\1\34\1\102\4\34\1\0"+
+ "\1\41\2\103\4\41\2\103\1\41\1\42\1\41\1\43"+
+ "\16\41\1\103\1\41\1\103\4\41\1\0\1\44\2\104"+
+ "\4\44\2\104\1\44\1\45\3\44\1\43\14\44\1\104"+
+ "\1\44\1\104\4\44\1\0\1\34\2\105\4\34\2\105"+
+ "\1\34\1\35\3\34\1\36\14\34\1\105\1\34\1\105"+
+ "\4\34\1\0\1\41\2\106\4\41\2\106\1\41\1\42"+
+ "\1\41\1\43\16\41\1\106\1\41\1\106\4\41\1\0"+
+ "\1\44\2\107\4\44\2\107\1\44\1\45\3\44\1\43"+
+ "\14\44\1\107\1\44\1\107\4\44\1\0\1\34\2\110"+
+ "\4\34\2\110\1\34\1\35\3\34\1\36\14\34\1\110"+
+ "\1\34\1\110\4\34\1\0\1\41\2\111\4\41\2\111"+
+ "\1\41\1\42\1\41\1\43\16\41\1\111\1\41\1\111"+
+ "\4\41\1\0\1\44\2\112\4\44\2\112\1\44\1\45"+
+ "\3\44\1\43\14\44\1\112\1\44\1\112\4\44\1\0"+
+ "\12\34\1\35\3\34\1\36\23\34\1\0\12\41\1\42"+
+ "\1\41\1\43\25\41\1\0\12\44\1\45\3\44\1\43"+
+ "\23\44";
+
+ private static int [] zzUnpackTrans() {
+ int [] result = new int[1925];
+ int offset = 0;
+ offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackTrans(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++);
+ value--;
+ do result[j++] = value; while (--count > 0);
+ }
+ return j;
+ }
+
+
+ /* 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\4\1\1\0\5\11\10\1\1\11\3\1\2\11"+
+ "\1\1\3\0\1\11\4\0\1\11\10\0\2\1\1\0"+
+ "\1\1\2\0\1\1\2\0\1\1\3\0\1\11\3\0"+
+ "\2\11\14\0";
+
+ private static int [] zzUnpackAttribute() {
+ int [] result = new int[74];
+ 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 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 */
+ 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;
+
+ /**
+ * zzAtBOL == true <=> the scanner is currently at the beginning of a line
+ */
+ private boolean zzAtBOL = true;
+
+ /** zzAtEOF == true <=> the scanner is at the EOF */
+ private boolean zzAtEOF;
+
+ /* user code: */
+ private final static String UNDEFINED = "undefined";
+ private String fBufferedContext = null;
+ private int fBufferedStart;
+// private int fBufferedTextLength;
+ private int fBufferedLength;
+// private StringBuilder fBufferedText = null;
+ private JSONTextRegionFactory fRegionFactory = JSONTextRegionFactory.getInstance();
+ private int fInitialState = YYINITIAL;
+ public final static int BUFFER_SIZE_NORMAL = 16384;
+ public final static int BUFFER_SIZE_SMALL = 256;
+ private int fInitialBufferSize = BUFFER_SIZE_NORMAL;
+
+ public void setInitialState(int state) {
+ fInitialState = state;
+ }
+
+ public void setInitialBufferSize(int size) {
+ fInitialBufferSize = size;
+ }
+
+ @Override
+ protected void setJSONArrayState() {
+ yybegin(ST_JSON_ARRAY);
+ }
+
+ @Override
+ protected void setJSONObjectState() {
+ yybegin(ST_JSON_OBJECT);
+ }
+
+ @Override
+ protected void setJSONValueState() {
+ yybegin(ST_JSON_VALUE);
+ }
+
+ /* user method */
+ public final ITextRegion getNextToken() throws IOException {
+ String context;
+ String nextTokenType;
+ boolean spaceFollows;
+// StringBuilder text;
+ int start;
+ int textLength;
+ int length;
+ if (fBufferedContext != null) {
+ context = fBufferedContext;
+// text = fBufferedText;
+ start = fBufferedStart;
+ textLength = length = fBufferedLength;
+
+ fBufferedContext = null;
+ } else {
+ context = primGetNextToken();
+// text = new StringBuilder(yytext());
+ start = yychar;
+ textLength = length = yylength();
+ }
+
+ if (context != null) {
+ if (context == UNDEFINED) {
+ // undef -> concatenate undef's
+ nextTokenType = primGetNextToken();
+ while (nextTokenType == UNDEFINED) {
+// text.append(yytext());
+ textLength += yylength();
+ length = textLength;
+ nextTokenType = primGetNextToken();
+ }
+ fBufferedContext = nextTokenType;
+// fBufferedText = new StringBuilder(yytext());
+ fBufferedStart = yychar;
+ fBufferedLength = yylength();
+ } else {
+ nextTokenType = null;
+ spaceFollows = false;
+ /*if (JSONRegionUtil.isDeclarationValueType(context)) { // declaration value can contain VALUE_S
+ nextTokenType = primGetNextToken();
+ spaceFollows = (nextTokenType == JSON_DECLARATION_VALUE_S);
+ } else
+ */
+ //if (canContainSpace(context)) {
+ nextTokenType = primGetNextToken();
+ //spaceFollows = (nextTokenType == JSON_S);
+ //}
+ if (nextTokenType != null) { // nextToken is retrieved
+ if (spaceFollows && (context != JSON_COMMENT)) {
+ // next is space -> append
+// text.append(yytext());
+ length += yylength();
+ } else {
+ // next is NOT space -> push this for next time, return itself
+ fBufferedContext = nextTokenType;
+// fBufferedText = new StringBuilder(yytext());
+ fBufferedStart = yychar;
+ fBufferedLength = yylength();
+ }
+ }
+ }
+ }
+
+ if (context != null) {
+ if (context == UNDEFINED) {
+ context = JSON_UNKNOWN;
+ }
+ return fRegionFactory.createRegion(context, start, textLength, length);
+ } else {
+ return null;
+ }
+ }
+
+ /* user method */
+ /* for standalone use */
+ /*public final List parseText() throws IOException {
+ List tokens = new ArrayList();
+
+ JSONTextToken token;
+ for (String kind = primGetNextToken(); kind != null; kind = primGetNextToken()) {
+ token = new JSONTextToken();
+ token.kind = kind;
+ token.start = yychar;
+ token.length = yylength();
+ token.image = yytext();
+ tokens.add(token);
+ }
+
+ return tokens;
+ }*/
+
+ /* user method */
+ /*private boolean canContainSpace(String type) {
+ if (type == JSON_DELIMITER || type == JSON_RBRACE || type == JSON_DECLARATION_DELIMITER) {
+ return false;
+ } else {
+ return true;
+ }
+ }*/
+
+ /* user method */
+ public final int getOffset() {
+ return yychar;
+ }
+
+ /* user method */
+ public final int getLine() {
+ return yyline;
+ }
+
+ /* user method */
+ public final boolean isEOF() {
+ return zzAtEOF;
+ }
+
+ /* user method */
+ public void reset(char[] charArray) {
+ reset(new CharArrayReader(charArray), 0);
+ }
+
+ /* user method */
+ public final void reset(java.io.Reader in, int newOffset) {
+ /** the input device */
+ zzReader = in;
+
+ /** the current state of the DFA */
+ zzState = 0;
+
+ /** the current lexical state */
+ zzLexicalState = fInitialState; //YYINITIAL;
+
+ /** this buffer contains the current text to be matched and is
+ the source of the yytext() string */
+ if (zzBuffer.length != fInitialBufferSize) {
+ zzBuffer = new char[fInitialBufferSize];
+ }
+ java.util.Arrays.fill(zzBuffer, (char)0);
+
+ /** the textposition at the last accepting state */
+ zzMarkedPos = 0;
+
+ /** the textposition at the last state to be included in yytext */
+// yy_pushbackPos = 0;
+
+ /** the current text position in the buffer */
+ zzCurrentPos = 0;
+
+ /** startRead marks the beginning of the yytext() string in the buffer */
+ zzStartRead = 0;
+
+ /** endRead marks the last character in the buffer, that has been read
+ from input */
+ zzEndRead = 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;
+
+ /** zzAtEOF == true <=> the scanner has returned a value for EOF */
+ zzAtEOF = false;
+
+ /* user variables */
+ // fUndefined.delete(0, fUndefined.length());
+ jsonContextStack.clear();
+ }
+
+ /* user method */
+ public JSONLineTokenizer() {
+ super();
+ }
+
+ /**
+ * Added to workaround stricter compilation options without creating
+ * an alternate skeleton file
+ */
+ void _usePrivates() {
+ System.out.print(yycolumn);
+ System.out.print(yyline);
+ System.out.print(Boolean.toString(zzAtBOL));
+ }
+
+
+ /**
+ * 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 JSONLineTokenizer(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 JSONLineTokenizer(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 < 128) {
+ 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;
+ 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) {
+ zzEndRead+= numRead;
+ return false;
+ }
+ // unlikely but not impossible: read 0 characters, but not at end of stream
+ if (numRead == 0) {
+ int c = zzReader.read();
+ if (c == -1) {
+ return true;
+ } else {
+ zzBuffer[zzEndRead++] = (char) c;
+ return false;
+ }
+ }
+
+ // numRead < 0
+ return true;
+ }
+
+
+ /**
+ * Closes the input stream.
+ */
+ public 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
+ */
+ public final void yyreset(java.io.Reader reader) {
+ zzReader = reader;
+ zzAtBOL = true;
+ zzAtEOF = false;
+ zzEndRead = zzStartRead = 0;
+ zzCurrentPos = zzMarkedPos = 0;
+ yyline = yychar = yycolumn = 0;
+ zzLexicalState = YYINITIAL;
+ }
+
+
+ /**
+ * Returns the current lexical state.
+ */
+ public final int yystate() {
+ return zzLexicalState;
+ }
+
+
+ /**
+ * Enters a new lexical state
+ *
+ * @param newState the new lexical state
+ */
+ public final void yybegin(int newState) {
+ zzLexicalState = newState;
+ }
+
+
+ /**
+ * Returns the text matched by the current regular expression.
+ */
+ public 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
+ */
+ public final char yycharat(int pos) {
+ return zzBuffer[zzStartRead+pos];
+ }
+
+
+ /**
+ * Returns the length of the matched text region.
+ */
+ public 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()!
+ */
+ public 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
+ */
+ public String primGetNextToken() 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;
+
+ yychar+= zzMarkedPosL-zzStartRead;
+
+ boolean zzR = false;
+ for (zzCurrentPosL = zzStartRead; zzCurrentPosL < zzMarkedPosL;
+ zzCurrentPosL++) {
+ switch (zzBufferL[zzCurrentPosL]) {
+ case '\u000B':
+ case '\u000C':
+ case '\u0085':
+ case '\u2028':
+ case '\u2029':
+ yyline++;
+ zzR = false;
+ break;
+ case '\r':
+ yyline++;
+ zzR = true;
+ break;
+ case '\n':
+ if (zzR)
+ zzR = false;
+ else {
+ yyline++;
+ }
+ break;
+ default:
+ zzR = false;
+ }
+ }
+
+ if (zzR) {
+ // peek one character ahead if it is \n (if we have counted one line too much)
+ boolean zzPeek;
+ if (zzMarkedPosL < zzEndReadL)
+ zzPeek = zzBufferL[zzMarkedPosL] == '\n';
+ else if (zzAtEOF)
+ zzPeek = false;
+ else {
+ boolean eof = zzRefill();
+ zzEndReadL = zzEndRead;
+ zzMarkedPosL = zzMarkedPos;
+ zzBufferL = zzBuffer;
+ if (eof)
+ zzPeek = false;
+ else
+ zzPeek = zzBufferL[zzMarkedPosL] == '\n';
+ }
+ if (zzPeek) yyline--;
+ }
+ zzAction = -1;
+
+ zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
+
+ zzState = ZZ_LEXSTATE[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 10:
+ { return JSON_COMMENT;
+ }
+ case 15: break;
+ case 1:
+ { return WHITE_SPACE;
+ }
+ case 16: break;
+ case 8:
+ { yybegin(ST_JSON_VALUE); return JSON_COLON;
+ }
+ case 17: break;
+ case 7:
+ { return endElement(true);
+ }
+ case 18: break;
+ case 13:
+ { yybegin(ST_JSON_VALUE); return JSON_VALUE_BOOLEAN;
+ }
+ case 19: break;
+ case 14:
+ { yybegin(ST_JSON_VALUE); return JSON_VALUE_NULL;
+ }
+ case 20: break;
+ case 2:
+ { return UNDEFINED;
+ }
+ case 21: break;
+ case 9:
+ { if (!isArrayParsing()) {
+ yybegin(ST_JSON_OBJECT);
+ }
+ return JSON_COMMA;
+ }
+ case 22: break;
+ case 5:
+ { return startElement(true);
+ }
+ case 23: break;
+ case 12:
+ { yybegin(ST_JSON_VALUE); return JSON_VALUE_STRING;
+ }
+ case 24: break;
+ case 4:
+ { return endElement(false);
+ }
+ case 25: break;
+ case 3:
+ { return startElement(false);
+ }
+ case 26: break;
+ case 6:
+ { yybegin(ST_JSON_VALUE); return JSON_VALUE_NUMBER;
+ }
+ case 27: break;
+ case 11:
+ { yybegin(ST_JSON_OBJECT_COLON); return JSON_OBJECT_KEY;
+ }
+ case 28: break;
+ default:
+ if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
+ zzAtEOF = true;
+ return null;
+ }
+ else {
+ zzScanError(ZZ_NO_MATCH);
+ }
+ }
+ }
+ }
+
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONSourceParser.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONSourceParser.java new file mode 100644 index 0000000000..ce5fd87fe4 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONSourceParser.java @@ -0,0 +1,260 @@ +/*******************************************************************************
+ * Copyright (c) 2004, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.parser.CSSSourceParser
+ * modified in order to process JSON Objects.
+ *******************************************************************************/
+package org.eclipse.wst.json.core.internal.parser;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.wst.json.core.internal.Logger;
+import org.eclipse.wst.json.core.regions.JSONRegionContexts;
+import org.eclipse.wst.json.core.util.JSONUtil;
+import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser;
+import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
+import org.eclipse.wst.sse.core.internal.util.Debug;
+
+/**
+ * JSON source parser.
+ *
+ */
+public class JSONSourceParser implements RegionParser {
+
+ private long fStartTime;
+ private long fStopTime;
+ private JSONTokenizer fTokenizer;
+
+ @Override
+ public IStructuredDocumentRegion getDocumentRegions() {
+ IStructuredDocumentRegion headnode = null;
+ if (headnode == null) {
+ if (Debug.perfTest) {
+ fStartTime = System.currentTimeMillis();
+ }
+ headnode = parseNodes();
+ if (Debug.perfTest) {
+ fStopTime = System.currentTimeMillis();
+ System.out
+ .println(" -- creating nodes of IStructuredDocument -- "); //$NON-NLS-1$
+ System.out
+ .println(" Time parse and init all regions: " + (fStopTime - fStartTime) + " (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;
+ }
+
+ private 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;
+ String currentRegionType = null;
+
+ try {
+ while ((region = getNextRegion()) != null) {
+ type = region.getType();
+ if (mustBeStart(type, currentRegionType) && currentNode != null) {
+ currentNode.setEnded(true);
+ }
+
+ if ((currentNode != null && currentNode.isEnded())
+ || currentNode == null) {
+ if (currentNode != null && !currentNode.isEnded()) {
+ currentNode.setEnded(true);
+ }
+ lastNode = currentNode;
+ currentNode = createStructuredDocumentRegion(type);
+ currentRegionType = 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());
+
+ if (mustBeEnd(type)) {
+ currentNode.setEnded(true);
+ }
+
+ if (headNode == null && currentNode != null) {
+ headNode = currentNode;
+ }
+ }
+
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ if (currentNode != null && !currentNode.isEnded()) {
+ currentNode.setEnded(true);
+ }
+
+ primReset();
+ return headNode;
+ }
+
+ /**
+ * Return the full list of known regions. Typically getNodes should be used
+ * instead of this method.
+ */
+ @Override
+ 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;
+ }
+
+ @Override
+ public RegionParser newInstance() {
+ return new JSONSourceParser();
+ }
+
+ @Override
+ public void reset(Reader reader) {
+ primReset();
+ getTokenizer().reset(reader, 0);
+ }
+
+ @Override
+ public void reset(Reader reader, int offset) {
+ reset(reader);
+ }
+
+ @Override
+ public void reset(String input) {
+ reset(new StringReader(input));
+ }
+
+ @Override
+ public void reset(String input, int offset) {
+ reset(input);
+ }
+
+ private IStructuredDocumentRegion createStructuredDocumentRegion(String type) {
+ return JSONStructuredDocumentRegionFactory.createRegion(type);
+ }
+
+ /**
+ * currently public but may be made default access protected in future.
+ */
+ protected boolean mustBeStart(String type, String docRegionType) {
+ return JSONUtil.isStartJSONStructure(type)
+ || JSONUtil.isEndJSONStructure(type)
+ || JSONUtil.isJSONSimpleValue(type)
+ || type == JSONRegionContexts.JSON_OBJECT_KEY
+ || type == JSONRegionContexts.JSON_COMMA
+ || (type != JSONRegionContexts.WHITE_SPACE && docRegionType == JSONRegionContexts.JSON_ARRAY_OPEN)
+ || (type != JSONRegionContexts.WHITE_SPACE && docRegionType == JSONRegionContexts.JSON_COMMA);
+
+ }
+
+ /**
+ * currently public but may be made default access protected in future.
+ */
+ protected boolean mustBeEnd(String regionType) {
+ return JSONUtil.isEndJSONStructure(regionType);
+ }
+
+ private 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;
+ }
+
+ private void primReset() {
+ getTokenizer().reset(new char[0]);
+ }
+
+ /**
+ * currently public but may be made default access protected in future.
+ */
+ private JSONTokenizer getTokenizer() {
+ if (fTokenizer == null) {
+ fTokenizer = new JSONTokenizer();
+ }
+ return fTokenizer;
+ }
+
+ /**
+ * This is a simple utility to count nodes. Used only for debug statements.
+ */
+ private int _countNodes(IStructuredDocumentRegion nodes) {
+ int result = 0;
+ IStructuredDocumentRegion countNode = nodes;
+ while (countNode != null) {
+ result++;
+ countNode = countNode.getNext();
+ }
+ return result;
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONStructuredDocumentRegionFactory.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONStructuredDocumentRegionFactory.java new file mode 100644 index 0000000000..6317ed91a9 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONStructuredDocumentRegionFactory.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.parser.CSSStructuredDocumentRegionFactory + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.parser; + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.text.BasicStructuredDocumentRegion; + +public class JSONStructuredDocumentRegionFactory { + public static IStructuredDocumentRegion createRegion(String type) { + IStructuredDocumentRegion instance = null; + if (type != null) { + instance = new BasicStructuredDocumentRegion(); + } + else { + + } + return instance; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONTokenizer.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONTokenizer.java new file mode 100644 index 0000000000..21fc0dee3d --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONTokenizer.java @@ -0,0 +1,916 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+/* The following code was generated by JFlex 1.4.2 on 18/03/15 22:44 */
+
+/*nlsXXX*/
+package org.eclipse.wst.json.core.internal.parser;
+
+import java.io.CharArrayReader;
+import java.io.IOException;
+import java.util.Stack;
+
+import org.eclipse.wst.json.core.internal.parser.regions.JSONTextRegionFactory;
+import org.eclipse.wst.json.core.regions.JSONRegionContexts;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+
+
+/**
+ * This class is a scanner generated by
+ * <a href="http://www.jflex.de/">JFlex</a> 1.4.2
+ * on 18/03/15 22:44 from the specification file
+ * <tt>JSONTokenizer.jflex</tt>
+ */
+public class JSONTokenizer extends AbstractJSONTokenizer {
+
+ /** This character denotes the end of file */
+ public static final int YYEOF = -1;
+
+ /** initial size of the lookahead buffer */
+ private static final int ZZ_BUFFERSIZE = 16384;
+
+ /** lexical states */
+ public static final int ST_JSON_OBJECT_COLON = 6;
+ public static final int ST_JSON_ARRAY = 4;
+ public static final int YYINITIAL = 0;
+ public static final int ST_JSON_COMMENT = 10;
+ public static final int ST_JSON_VALUE = 8;
+ public static final int ST_JSON_OBJECT = 2;
+
+ /**
+ * ZZ_LEXSTATE[l] is the state in the DFA for the lexical state l
+ * ZZ_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 static final int ZZ_LEXSTATE[] = {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5
+ };
+
+ /**
+ * Translates characters to character classes
+ */
+ private static final String ZZ_CMAP_PACKED =
+ "\10\0\1\0\1\16\1\20\1\0\1\14\1\7\22\0\1\16\1\12"+
+ "\1\17\4\12\1\15\2\12\1\27\1\6\1\25\1\1\1\4\1\26"+
+ "\1\2\11\3\1\30\6\12\1\37\3\12\1\5\1\35\5\12\1\40"+
+ "\1\12\1\42\3\12\1\32\1\41\1\31\1\33\5\12\1\23\1\13"+
+ "\1\24\1\12\1\12\1\12\1\36\3\10\1\11\1\34\5\12\1\40"+
+ "\1\12\1\42\3\12\1\32\1\41\1\31\1\33\5\12\1\21\1\12"+
+ "\1\22\1\12\1\0\uff80\12";
+
+ /**
+ * 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\4\1\1\0\1\2\1\1\1\3\1\4\1\5"+
+ "\1\2\1\1\2\2\2\6\2\2\1\7\3\2\1\10"+
+ "\1\11\1\12\3\0\1\13\4\0\1\14\10\0\1\13"+
+ "\1\6\1\0\1\6\2\0\1\14\2\0\1\14\3\0"+
+ "\1\12\3\0\1\15\1\16\14\0";
+
+ private static int [] zzUnpackAction() {
+ int [] result = new int[74];
+ 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\43\0\106\0\151\0\214\0\257\0\322\0\322"+
+ "\0\322\0\322\0\322\0\365\0\u0118\0\u013b\0\u015e\0\u0181"+
+ "\0\u01a4\0\u01c7\0\u01ea\0\322\0\u020d\0\u0230\0\u0253\0\322"+
+ "\0\322\0\u0276\0\u0299\0\u013b\0\u02bc\0\322\0\u02df\0\u0302"+
+ "\0\u01c7\0\u0325\0\322\0\u01ea\0\u0348\0\u036b\0\u038e\0\u03b1"+
+ "\0\u03d4\0\u03f7\0\u041a\0\u013b\0\u043d\0\u0460\0\u0460\0\u0483"+
+ "\0\u04a6\0\u01c7\0\u04c9\0\u04ec\0\u01ea\0\u050f\0\u0532\0\u0555"+
+ "\0\322\0\u0578\0\u059b\0\u05be\0\322\0\322\0\u05e1\0\u0604"+
+ "\0\u0627\0\u064a\0\u066d\0\u0690\0\u06b3\0\u06d6\0\u06f9\0\u071c"+
+ "\0\u073f\0\u0762";
+
+ private static int [] zzUnpackRowMap() {
+ int [] result = new int[74];
+ 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 = zzUnpackTrans();
+
+ private static final String ZZ_TRANS_PACKED_0 =
+ "\20\7\1\10\1\11\1\12\1\13\2\7\1\14\23\7"+
+ "\1\15\6\7\1\15\1\16\1\15\1\7\1\12\3\7"+
+ "\1\14\15\7\1\17\1\20\1\21\3\7\1\15\5\7"+
+ "\1\22\1\15\1\23\1\15\1\11\1\12\1\13\1\24"+
+ "\1\7\1\14\2\7\1\25\2\7\2\26\4\7\1\27"+
+ "\7\7\1\15\6\7\1\15\1\7\1\15\1\7\1\12"+
+ "\3\7\1\14\1\7\1\30\13\7\1\17\1\20\1\21"+
+ "\3\7\1\15\5\7\1\22\1\15\1\23\1\15\1\11"+
+ "\1\12\1\13\1\24\1\31\1\14\2\7\1\25\2\7"+
+ "\2\26\4\7\1\27\20\7\1\10\1\7\1\12\3\7"+
+ "\1\14\14\7\71\0\1\32\1\33\22\0\1\15\6\0"+
+ "\1\15\1\0\1\15\23\0\6\34\1\0\3\34\1\35"+
+ "\1\0\2\34\1\36\1\0\22\34\2\0\1\20\1\21"+
+ "\43\0\1\37\1\40\3\0\1\40\33\0\2\21\1\37"+
+ "\1\40\3\0\1\40\32\0\6\41\1\0\3\41\1\42"+
+ "\1\0\1\43\2\41\1\0\22\41\1\0\6\44\1\0"+
+ "\3\44\1\45\1\0\2\44\1\43\1\0\22\44\32\0"+
+ "\1\46\46\0\2\47\36\0\1\50\7\0\20\32\1\0"+
+ "\22\32\27\33\1\51\13\33\1\0\1\34\2\52\3\34"+
+ "\1\53\2\52\1\34\1\35\3\34\1\54\14\34\1\52"+
+ "\1\34\1\52\4\34\2\0\2\55\40\0\1\56\2\57"+
+ "\2\0\1\56\35\0\1\41\2\60\3\41\1\61\2\60"+
+ "\1\41\1\42\1\41\1\62\16\41\1\60\1\41\1\60"+
+ "\4\41\1\0\1\44\2\63\3\44\1\64\2\63\1\44"+
+ "\1\45\3\44\1\65\14\44\1\63\1\44\1\63\4\44"+
+ "\33\0\1\66\47\0\1\67\42\0\1\70\2\0\26\33"+
+ "\1\71\1\51\13\33\1\0\1\34\2\72\4\34\2\72"+
+ "\1\34\1\35\3\34\1\36\14\34\1\72\1\34\1\72"+
+ "\4\34\1\0\6\34\1\0\3\34\1\35\1\0\2\34"+
+ "\1\36\23\34\2\0\2\55\1\0\1\40\3\0\1\40"+
+ "\33\0\2\57\40\0\1\41\2\73\4\41\2\73\1\41"+
+ "\1\42\1\41\1\43\16\41\1\73\1\41\1\73\4\41"+
+ "\1\0\6\41\1\0\3\41\1\42\1\0\1\43\25\41"+
+ "\1\0\1\44\2\74\4\44\2\74\1\44\1\45\3\44"+
+ "\1\43\14\44\1\74\1\44\1\74\4\44\1\0\6\44"+
+ "\1\0\3\44\1\45\1\0\2\44\1\43\23\44\5\0"+
+ "\1\75\3\0\1\75\72\0\1\66\41\0\1\76\3\0"+
+ "\1\34\2\77\4\34\2\77\1\34\1\35\3\34\1\36"+
+ "\14\34\1\77\1\34\1\77\4\34\1\0\1\41\2\100"+
+ "\4\41\2\100\1\41\1\42\1\41\1\43\16\41\1\100"+
+ "\1\41\1\100\4\41\1\0\1\44\2\101\4\44\2\101"+
+ "\1\44\1\45\3\44\1\43\14\44\1\101\1\44\1\101"+
+ "\4\44\1\0\1\34\2\102\4\34\2\102\1\34\1\35"+
+ "\3\34\1\36\14\34\1\102\1\34\1\102\4\34\1\0"+
+ "\1\41\2\103\4\41\2\103\1\41\1\42\1\41\1\43"+
+ "\16\41\1\103\1\41\1\103\4\41\1\0\1\44\2\104"+
+ "\4\44\2\104\1\44\1\45\3\44\1\43\14\44\1\104"+
+ "\1\44\1\104\4\44\1\0\1\34\2\105\4\34\2\105"+
+ "\1\34\1\35\3\34\1\36\14\34\1\105\1\34\1\105"+
+ "\4\34\1\0\1\41\2\106\4\41\2\106\1\41\1\42"+
+ "\1\41\1\43\16\41\1\106\1\41\1\106\4\41\1\0"+
+ "\1\44\2\107\4\44\2\107\1\44\1\45\3\44\1\43"+
+ "\14\44\1\107\1\44\1\107\4\44\1\0\1\34\2\110"+
+ "\4\34\2\110\1\34\1\35\3\34\1\36\14\34\1\110"+
+ "\1\34\1\110\4\34\1\0\1\41\2\111\4\41\2\111"+
+ "\1\41\1\42\1\41\1\43\16\41\1\111\1\41\1\111"+
+ "\4\41\1\0\1\44\2\112\4\44\2\112\1\44\1\45"+
+ "\3\44\1\43\14\44\1\112\1\44\1\112\4\44\1\0"+
+ "\12\34\1\35\3\34\1\36\23\34\1\0\12\41\1\42"+
+ "\1\41\1\43\25\41\1\0\12\44\1\45\3\44\1\43"+
+ "\23\44";
+
+ private static int [] zzUnpackTrans() {
+ int [] result = new int[1925];
+ int offset = 0;
+ offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
+ return result;
+ }
+
+ private static int zzUnpackTrans(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++);
+ value--;
+ do result[j++] = value; while (--count > 0);
+ }
+ return j;
+ }
+
+
+ /* 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\4\1\1\0\5\11\10\1\1\11\3\1\2\11"+
+ "\1\1\3\0\1\11\4\0\1\11\10\0\2\1\1\0"+
+ "\1\1\2\0\1\1\2\0\1\1\3\0\1\11\3\0"+
+ "\2\11\14\0";
+
+ private static int [] zzUnpackAttribute() {
+ int [] result = new int[74];
+ 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 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 */
+ 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;
+
+ /**
+ * zzAtBOL == true <=> the scanner is currently at the beginning of a line
+ */
+ private boolean zzAtBOL = true;
+
+ /** zzAtEOF == true <=> the scanner is at the EOF */
+ private boolean zzAtEOF;
+
+ /* user code: */
+ private final static String UNDEFINED = "undefined";
+ private String fBufferedContext = null;
+ private int fBufferedStart;
+// private int fBufferedTextLength;
+ private int fBufferedLength;
+// private StringBuilder fBufferedText = null;
+ private JSONTextRegionFactory fRegionFactory = JSONTextRegionFactory.getInstance();
+ private int fInitialState = YYINITIAL;
+ public final static int BUFFER_SIZE_NORMAL = 16384;
+ public final static int BUFFER_SIZE_SMALL = 256;
+ private int fInitialBufferSize = BUFFER_SIZE_NORMAL;
+
+ public void setInitialState(int state) {
+ fInitialState = state;
+ }
+
+ public void setInitialBufferSize(int size) {
+ fInitialBufferSize = size;
+ }
+
+ @Override
+ protected void setJSONArrayState() {
+ yybegin(ST_JSON_ARRAY);
+ }
+
+ @Override
+ protected void setJSONObjectState() {
+ yybegin(ST_JSON_OBJECT);
+ }
+
+ @Override
+ protected void setJSONValueState() {
+ yybegin(ST_JSON_VALUE);
+ }
+
+ /* user method */
+ public final ITextRegion getNextToken() throws IOException {
+ String context;
+ String nextTokenType;
+ boolean spaceFollows;
+// StringBuilder text;
+ int start;
+ int textLength;
+ int length;
+ if (fBufferedContext != null) {
+ context = fBufferedContext;
+// text = fBufferedText;
+ start = fBufferedStart;
+ textLength = length = fBufferedLength;
+
+ fBufferedContext = null;
+ } else {
+ context = primGetNextToken();
+// text = new StringBuilder(yytext());
+ start = yychar;
+ textLength = length = yylength();
+ }
+
+ if (context != null) {
+ if (context == UNDEFINED) {
+ // undef -> concatenate undef's
+ nextTokenType = primGetNextToken();
+ while (nextTokenType == UNDEFINED) {
+// text.append(yytext());
+ textLength += yylength();
+ length = textLength;
+ nextTokenType = primGetNextToken();
+ }
+ fBufferedContext = nextTokenType;
+// fBufferedText = new StringBuilder(yytext());
+ fBufferedStart = yychar;
+ fBufferedLength = yylength();
+ } else {
+ nextTokenType = null;
+ spaceFollows = false;
+ /*if (JSONRegionUtil.isDeclarationValueType(context)) { // declaration value can contain VALUE_S
+ nextTokenType = primGetNextToken();
+ spaceFollows = (nextTokenType == JSON_DECLARATION_VALUE_S);
+ } else
+ */
+ //if (canContainSpace(context)) {
+ nextTokenType = primGetNextToken();
+ //spaceFollows = (nextTokenType == JSON_S);
+ //}
+ if (nextTokenType != null) { // nextToken is retrieved
+ if (spaceFollows && (context != JSON_COMMENT)) {
+ // next is space -> append
+// text.append(yytext());
+ length += yylength();
+ } else {
+ // next is NOT space -> push this for next time, return itself
+ fBufferedContext = nextTokenType;
+// fBufferedText = new StringBuilder(yytext());
+ fBufferedStart = yychar;
+ fBufferedLength = yylength();
+ }
+ }
+ }
+ }
+
+ if (context != null) {
+ if (context == UNDEFINED) {
+ context = JSON_UNKNOWN;
+ }
+ return fRegionFactory.createRegion(context, start, textLength, length);
+ } else {
+ return null;
+ }
+ }
+
+ /* user method */
+ /* for standalone use */
+ /*public final List parseText() throws IOException {
+ List tokens = new ArrayList();
+
+ JSONTextToken token;
+ for (String kind = primGetNextToken(); kind != null; kind = primGetNextToken()) {
+ token = new JSONTextToken();
+ token.kind = kind;
+ token.start = yychar;
+ token.length = yylength();
+ token.image = yytext();
+ tokens.add(token);
+ }
+
+ return tokens;
+ }*/
+
+ /* user method */
+ /*private boolean canContainSpace(String type) {
+ if (type == JSON_DELIMITER || type == JSON_RBRACE || type == JSON_DECLARATION_DELIMITER) {
+ return false;
+ } else {
+ return true;
+ }
+ }*/
+
+ /* user method */
+ public final int getOffset() {
+ return yychar;
+ }
+
+ /* user method */
+ public final boolean isEOF() {
+ return zzAtEOF;
+ }
+
+ /* user method */
+ public void reset(char[] charArray) {
+ reset(new CharArrayReader(charArray), 0);
+ }
+
+ /* user method */
+ public final void reset(java.io.Reader in, int newOffset) {
+ /** the input device */
+ zzReader = in;
+
+ /** the current state of the DFA */
+ zzState = 0;
+
+ /** the current lexical state */
+ zzLexicalState = fInitialState; //YYINITIAL;
+
+ /** this buffer contains the current text to be matched and is
+ the source of the yytext() string */
+ if (zzBuffer.length != fInitialBufferSize) {
+ zzBuffer = new char[fInitialBufferSize];
+ }
+ java.util.Arrays.fill(zzBuffer, (char)0);
+
+ /** the textposition at the last accepting state */
+ zzMarkedPos = 0;
+
+ /** the textposition at the last state to be included in yytext */
+// yy_pushbackPos = 0;
+
+ /** the current text position in the buffer */
+ zzCurrentPos = 0;
+
+ /** startRead marks the beginning of the yytext() string in the buffer */
+ zzStartRead = 0;
+
+ /** endRead marks the last character in the buffer, that has been read
+ from input */
+ zzEndRead = 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;
+
+ /** zzAtEOF == true <=> the scanner has returned a value for EOF */
+ zzAtEOF = false;
+
+ /* user variables */
+ // fUndefined.delete(0, fUndefined.length());
+ jsonContextStack.clear();
+ }
+
+ /* user method */
+ public JSONTokenizer() {
+ super();
+ }
+
+ /**
+ * Added to workaround stricter compilation options without creating
+ * an alternate skeleton file
+ */
+ void _usePrivates() {
+ System.out.print(yycolumn);
+ System.out.print(yyline);
+ System.out.print(Boolean.toString(zzAtBOL));
+ }
+
+
+ /**
+ * 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 JSONTokenizer(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 JSONTokenizer(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 < 128) {
+ 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;
+ 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) {
+ zzEndRead+= numRead;
+ return false;
+ }
+ // unlikely but not impossible: read 0 characters, but not at end of stream
+ if (numRead == 0) {
+ int c = zzReader.read();
+ if (c == -1) {
+ return true;
+ } else {
+ zzBuffer[zzEndRead++] = (char) c;
+ return false;
+ }
+ }
+
+ // numRead < 0
+ return true;
+ }
+
+
+ /**
+ * Closes the input stream.
+ */
+ public 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
+ */
+ public final void yyreset(java.io.Reader reader) {
+ zzReader = reader;
+ zzAtBOL = true;
+ zzAtEOF = false;
+ zzEndRead = zzStartRead = 0;
+ zzCurrentPos = zzMarkedPos = 0;
+ yyline = yychar = yycolumn = 0;
+ zzLexicalState = YYINITIAL;
+ }
+
+
+ /**
+ * Returns the current lexical state.
+ */
+ public final int yystate() {
+ return zzLexicalState;
+ }
+
+
+ /**
+ * Enters a new lexical state
+ *
+ * @param newState the new lexical state
+ */
+ public final void yybegin(int newState) {
+ zzLexicalState = newState;
+ }
+
+
+ /**
+ * Returns the text matched by the current regular expression.
+ */
+ public 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
+ */
+ public final char yycharat(int pos) {
+ return zzBuffer[zzStartRead+pos];
+ }
+
+
+ /**
+ * Returns the length of the matched text region.
+ */
+ public 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()!
+ */
+ public 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
+ */
+ public String primGetNextToken() 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;
+
+ yychar+= zzMarkedPosL-zzStartRead;
+
+ zzAction = -1;
+
+ zzCurrentPosL = zzCurrentPos = zzStartRead = zzMarkedPosL;
+
+ zzState = ZZ_LEXSTATE[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 10:
+ { return JSON_COMMENT;
+ }
+ case 15: break;
+ case 1:
+ { return WHITE_SPACE;
+ }
+ case 16: break;
+ case 8:
+ { yybegin(ST_JSON_VALUE); return JSON_COLON;
+ }
+ case 17: break;
+ case 7:
+ { return endElement(true);
+ }
+ case 18: break;
+ case 13:
+ { yybegin(ST_JSON_VALUE); return JSON_VALUE_BOOLEAN;
+ }
+ case 19: break;
+ case 14:
+ { yybegin(ST_JSON_VALUE); return JSON_VALUE_NULL;
+ }
+ case 20: break;
+ case 2:
+ { return UNDEFINED;
+ }
+ case 21: break;
+ case 9:
+ { if (!isArrayParsing()) {
+ yybegin(ST_JSON_OBJECT);
+ }
+ return JSON_COMMA;
+ }
+ case 22: break;
+ case 5:
+ { return startElement(true);
+ }
+ case 23: break;
+ case 12:
+ { yybegin(ST_JSON_VALUE); return JSON_VALUE_STRING;
+ }
+ case 24: break;
+ case 4:
+ { return endElement(false);
+ }
+ case 25: break;
+ case 3:
+ { return startElement(false);
+ }
+ case 26: break;
+ case 6:
+ { yybegin(ST_JSON_VALUE); return JSON_VALUE_NUMBER;
+ }
+ case 27: break;
+ case 11:
+ { yybegin(ST_JSON_OBJECT_COLON); return JSON_OBJECT_KEY;
+ }
+ case 28: break;
+ default:
+ if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
+ zzAtEOF = true;
+ return null;
+ }
+ else {
+ zzScanError(ZZ_NO_MATCH);
+ }
+ }
+ }
+ }
+
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/regions/JSONTextRegionFactory.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/regions/JSONTextRegionFactory.java new file mode 100644 index 0000000000..072f8abb0d --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/regions/JSONTextRegionFactory.java @@ -0,0 +1,40 @@ +/*******************************************************************************
+ * Copyright (c) 2004, 2005 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
+ * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.parser.regions.CSSTextRegionFactory
+ * modified in order to process JSON Objects.
+ *******************************************************************************/
+package org.eclipse.wst.json.core.internal.parser.regions;
+
+import org.eclipse.wst.sse.core.internal.parser.ContextRegion;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+
+public class JSONTextRegionFactory {
+
+ public synchronized static JSONTextRegionFactory getInstance() {
+ if (fInstance == null) {
+ fInstance = new JSONTextRegionFactory();
+ }
+ return fInstance;
+ }
+
+ public ITextRegion createRegion(String context, int start, int textLength,
+ int length) {
+ ITextRegion region = null;
+ region = new ContextRegion(context, start, textLength, length);
+ return region;
+ }
+
+ private JSONTextRegionFactory() {
+ super();
+ }
+
+ private static JSONTextRegionFactory fInstance = null;
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/preferences/JSONCorePreferenceInitializer.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/preferences/JSONCorePreferenceInitializer.java new file mode 100644 index 0000000000..4144811129 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/preferences/JSONCorePreferenceInitializer.java @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2013-2014 Angelo ZERR. + * 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: + * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation + */ +package org.eclipse.wst.json.core.internal.preferences; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.core.runtime.preferences.DefaultScope; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.wst.json.core.JSONCorePlugin; +import org.eclipse.wst.json.core.preferences.JSONCorePreferenceNames; +import org.eclipse.wst.sse.core.internal.encoding.CommonCharsetNames; +import org.eclipse.wst.sse.core.internal.encoding.CommonEncodingPreferenceNames; + +/** + * Sets default values for JSON Core preferences + */ +public class JSONCorePreferenceInitializer extends + AbstractPreferenceInitializer { + + public void initializeDefaultPreferences() { + IEclipsePreferences node = new DefaultScope().getNode(JSONCorePlugin + .getDefault().getBundle().getSymbolicName()); + + // Validation preferences + node.putBoolean(JSONCorePreferenceNames.SYNTAX_VALIDATION, false); + node.putBoolean(JSONCorePreferenceNames.SCHEMA_VALIDATION, false); + node.putInt(JSONCorePreferenceNames.MISSING_BRACKET, 2); + + // formatting preferences + node.putInt(JSONCorePreferenceNames.LINE_WIDTH, 72); + node.putBoolean(JSONCorePreferenceNames.CLEAR_ALL_BLANK_LINES, false); + node.put(JSONCorePreferenceNames.INDENTATION_CHAR, + JSONCorePreferenceNames.TAB); + node.putInt(JSONCorePreferenceNames.INDENTATION_SIZE, 1); + + // cleanup preferences + node.putBoolean(JSONCorePreferenceNames.QUOTE_ATTR_VALUES, true); + node.putBoolean(JSONCorePreferenceNames.FORMAT_SOURCE, true); + + // code generation preferences + node.put(CommonEncodingPreferenceNames.INPUT_CODESET, ""); //$NON-NLS-1$ + String defaultEnc = "UTF-8";//$NON-NLS-1$ + String systemEnc = System.getProperty("file.encoding"); //$NON-NLS-1$ + if (systemEnc != null) { + defaultEnc = CommonCharsetNames.getPreferredDefaultIanaName( + systemEnc, "UTF-8");//$NON-NLS-1$ + } + node.put(CommonEncodingPreferenceNames.OUTPUT_CODESET, defaultEnc); + node.put(CommonEncodingPreferenceNames.END_OF_LINE_CODE, ""); //$NON-NLS-1$ + + // this could be made smarter by actually looking up the content + // type's valid extensions + node.put(JSONCorePreferenceNames.DEFAULT_EXTENSION, "json"); //$NON-NLS-1$ + + // additional css core preferences + node.putInt(JSONCorePreferenceNames.FORMAT_PROP_PRE_DELIM, 0); + node.putInt(JSONCorePreferenceNames.FORMAT_PROP_POST_DELIM, 1); + node.put(JSONCorePreferenceNames.FORMAT_QUOTE, "\"");//$NON-NLS-1$ + node.put(JSONCorePreferenceNames.FORMAT_BETWEEN_VALUE, " ");//$NON-NLS-1$ + node.putBoolean(JSONCorePreferenceNames.FORMAT_SPACE_BETWEEN_SELECTORS, + true); + node.putBoolean(JSONCorePreferenceNames.FORMAT_QUOTE_IN_URI, true); + node.putBoolean(JSONCorePreferenceNames.WRAPPING_ONE_PER_LINE, true); + node.putBoolean(JSONCorePreferenceNames.WRAPPING_PROHIBIT_WRAP_ON_ATTR, + true); + node.putBoolean(JSONCorePreferenceNames.WRAPPING_NEWLINE_ON_OPEN_BRACE, + false); + node.putInt(JSONCorePreferenceNames.CASE_IDENTIFIER, + JSONCorePreferenceNames.UPPER); + node.putInt(JSONCorePreferenceNames.CASE_SELECTOR, + JSONCorePreferenceNames.LOWER); + node.putInt(JSONCorePreferenceNames.CASE_PROPERTY_NAME, + JSONCorePreferenceNames.LOWER); + node.putInt(JSONCorePreferenceNames.CASE_PROPERTY_VALUE, + JSONCorePreferenceNames.LOWER); + + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/SchemaProcessorRegistryReader.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/SchemaProcessorRegistryReader.java new file mode 100644 index 0000000000..fdaec89d31 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/SchemaProcessorRegistryReader.java @@ -0,0 +1,163 @@ +/**
+ * Copyright (c) 2013, 2016 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.schema;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtensionPoint;
+import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.json.schema.IJSONSchemaDocument;
+import org.eclipse.json.schema.IJSONSchemaProcessor;
+import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolverPlugin;
+import org.eclipse.wst.common.uriresolver.internal.util.URIHelper;
+import org.eclipse.wst.json.core.JSONCorePlugin;
+import org.eclipse.wst.json.core.document.IJSONDocument;
+import org.eclipse.wst.json.core.document.IJSONModel;
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONPair;
+import org.eclipse.wst.json.core.document.IJSONValue;
+import org.eclipse.wst.json.core.internal.Logger;
+import org.eclipse.wst.json.core.util.JSONUtil;
+
+public class SchemaProcessorRegistryReader {
+
+ protected static final String EXTENSION_POINT_ID = "schemaProcessors"; //$NON-NLS-1$
+ protected static final String TAG_CONTRIBUTION = "schemaProcessor"; //$NON-NLS-1$
+
+ public static SchemaProcessorRegistryReader INSTANCE = null;
+
+ private IJSONSchemaProcessor defaultProcessor;
+
+ public static SchemaProcessorRegistryReader getInstance() {
+ if (INSTANCE == null) {
+ INSTANCE = new SchemaProcessorRegistryReader();
+ INSTANCE.readRegistry();
+ }
+ return INSTANCE;
+ }
+
+ public IJSONSchemaProcessor getDefaultProcessor() {
+ return defaultProcessor;
+ }
+
+ public IJSONSchemaDocument getSchemaDocument(IJSONNode node)
+ throws IOException {
+ return getSchemaDocument(node.getModel());
+ }
+
+ public IJSONSchemaDocument getSchemaDocument(IJSONModel model)
+ throws IOException {
+ IJSONSchemaProcessor processor = getDefaultProcessor();
+ if (processor == null) {
+ return null;
+ }
+ IJSONDocument document = model.getDocument();
+ IJSONNode jsonObject = document.getFirstChild();
+ if (jsonObject != null) {
+ IJSONNode child = jsonObject.getFirstChild();
+ while (child != null) {
+ if (child instanceof IJSONPair) {
+ IJSONPair pair = (IJSONPair) child;
+ String name = pair.getName();
+ IJSONValue valueNode = pair.getValue();
+ if (valueNode != null && "$schema".equals(name)) { //$NON-NLS-1$
+ String schema = JSONUtil.getString(valueNode);
+ try {
+ if (schema != null) {
+ schema = URIHelper.addImpliedFileProtocol(schema);
+ new URL(schema);
+ return processor.getSchema(schema);
+ }
+ } catch (MalformedURLException e) {
+ }
+ }
+ }
+ child = child.getNextSibling();
+ }
+ }
+ String base = model == null || model.getResolver() == null ?
+ null : model.getResolver().getFileBaseLocation();
+ /**
+ * We shouldn't assert a failure because the catalog does not require a
+ * base location to operate and it will be called from non-file-based
+ * scenarios.
+ *
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=206176
+ */
+ // Assert.isNotNull(base, "Base location is expected to be non null."); //$NON-NLS-1$
+ if (base != null) {
+ base = URIHelper.addImpliedFileProtocol(base);
+ }
+ String schemaURL = resolve(base, null, null);
+ if (schemaURL != null) {
+ return processor.getSchema(schemaURL);
+ }
+ return null;
+ }
+
+ private String getFileMatch(String location) {
+ if (location == null) {
+ return null;
+ }
+ int index = location.lastIndexOf('/');
+ if (index == -1) {
+ index = location.lastIndexOf('\\');
+ }
+ if (index != -1) {
+ return location.substring(index, location.length());
+ }
+ return location;
+ }
+
+ private String resolve(String base, String publicId, String systemId) {
+ String result = systemId;
+ result = URIResolverPlugin.createResolver().resolve(base, publicId,
+ systemId);
+ return result;
+ }
+
+ /**
+ * read from plugin registry and parse it.
+ */
+ protected void readRegistry() {
+ IExtensionRegistry pluginRegistry = Platform.getExtensionRegistry();
+ IExtensionPoint point = pluginRegistry
+ .getExtensionPoint(JSONCorePlugin.getDefault().getBundle()
+ .getSymbolicName(), EXTENSION_POINT_ID);
+ if (point != null) {
+ IConfigurationElement[] elements = point.getConfigurationElements();
+ for (int i = 0; i < elements.length; i++) {
+ readElement(elements[i]);
+ }
+ }
+ }
+
+ protected void readElement(IConfigurationElement element) {
+ if (TAG_CONTRIBUTION.equals(element.getName())) {
+ String id = element.getAttribute("id");
+ String name = element.getAttribute("name");
+ try {
+ IJSONSchemaProcessor schemaProcessor = (IJSONSchemaProcessor) element
+ .createExecutableExtension("class");
+ this.defaultProcessor = schemaProcessor;
+ } catch (CoreException e) {
+ Logger.logException(e);
+ }
+ }
+
+ }
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/Catalog.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/Catalog.java new file mode 100644 index 0000000000..e7cc896ad9 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/Catalog.java @@ -0,0 +1,588 @@ +/** + * Copyright (c) 2013-2014 Angelo ZERR. + * 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: + * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation + */ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.wst.json.core.JSONCorePlugin; +import org.eclipse.wst.json.core.internal.Logger; +import org.eclipse.wst.json.core.schema.catalog.ICatalog; +import org.eclipse.wst.json.core.schema.catalog.ICatalogElement; +import org.eclipse.wst.json.core.schema.catalog.ICatalogEntry; +import org.eclipse.wst.json.core.schema.catalog.ICatalogEvent; +import org.eclipse.wst.json.core.schema.catalog.ICatalogListener; +import org.eclipse.wst.json.core.schema.catalog.IDelegateCatalog; +import org.eclipse.wst.json.core.schema.catalog.INextCatalog; +import org.eclipse.wst.json.core.schema.catalog.IRewriteEntry; +import org.eclipse.wst.json.core.schema.catalog.ISuffixEntry; + +public class Catalog implements ICatalog { + + class CatalogLS { + public void load() { + } + + public synchronized void save() { + // try { + // new CatalogWriter().write(Catalog.this, location); + // } catch (Exception e) { + // Logger.logException(e); + // } + } + } + + class DefaultCatalogLS extends CatalogLS { + public void load() { + NextCatalog userCatalogReference = new NextCatalog(); + userCatalogReference.setId(JSONCorePlugin.USER_CATALOG_ID); + userCatalogReference.setCatalogLocation(USER_CATALOG_FILE); + addCatalogElement(userCatalogReference); + + NextCatalog systemCatalogReference = new NextCatalog(); + systemCatalogReference.setId(JSONCorePlugin.SYSTEM_CATALOG_ID); + systemCatalogReference.setCatalogLocation(SYSTEM_CATALOG_FILE); + addCatalogElement(systemCatalogReference); + + /* + * Here we save the file in order to 'reflect' the catalog that + * we've created from plug-in extensions to disk. The 'default' + * catalog is only ever written to disk and never read from disk. + */ + save(); + } + } + + private static Comparator LONGEST_REWRITE_FIRST = new Comparator() { + public int compare(Object entry1, Object entry2) { + String start1 = ((IRewriteEntry) entry1).getStartString(); + String start2 = ((IRewriteEntry) entry2).getStartString(); + + // Bigger is earlier + return start2.length() - start1.length(); + } + }; + + private static Comparator LONGEST_SUFFIX_FIRST = new Comparator() { + public int compare(Object entry1, Object entry2) { + String suffix1 = ((ISuffixEntry) entry1).getSuffix(); + String suffix2 = ((ISuffixEntry) entry2).getSuffix(); + + // Bigger is earlier + return suffix2.length() - suffix1.length(); + } + }; + + private static Comparator LONGEST_DELEGATE_PREFIX_FIRST = new Comparator() { + public int compare(Object entry1, Object entry2) { + String prefix1 = ((IDelegateCatalog) entry1).getStartString(); + String prefix2 = ((IDelegateCatalog) entry2).getStartString(); + + // Bigger is earlier + return prefix2.length() - prefix1.length(); + } + }; + + class InternalResolver { + protected Map schemaMap = new HashMap(); + + // These are sorted by longest "key" first. + protected List rewriteSystemList = new LinkedList(); + protected List rewriteUriList = new LinkedList(); + protected List suffixSystemList = new LinkedList(); + protected List suffixUriList = new LinkedList(); + protected List delegatePublicList = new LinkedList(); + protected List delegateSystemList = new LinkedList(); + protected List delegateUriList = new LinkedList(); + + InternalResolver() { + synchronized (catalogElements) { + for (Iterator i = catalogElements.iterator(); i.hasNext();) { + ICatalogElement catalogElement = (ICatalogElement) i.next(); + if (catalogElement.getType() == ICatalogElement.TYPE_ENTRY) { + ICatalogEntry entry = (ICatalogEntry) catalogElement; + Map map = getEntryMap(entry.getEntryType()); + String[] names = entry.getKey().split(","); + for (int j = 0; j < names.length; j++) { + map.put(names[j].trim(), entry); + } + } else if (catalogElement.getType() == ICatalogElement.TYPE_REWRITE) { + IRewriteEntry entry = (IRewriteEntry) catalogElement; + if (entry.getEntryType() == IRewriteEntry.REWRITE_TYPE_SYSTEM) { + rewriteSystemList.add(entry); + } else { + rewriteUriList.add(entry); + } + } else if (catalogElement.getType() == ICatalogElement.TYPE_SUFFIX) { + ISuffixEntry entry = (ISuffixEntry) catalogElement; + if (entry.getEntryType() == ISuffixEntry.SUFFIX_TYPE_SYSTEM) { + suffixSystemList.add(entry); + } else { + suffixUriList.add(entry); + } + } else if (catalogElement.getType() == ICatalogElement.TYPE_DELEGATE) { + IDelegateCatalog delegate = (IDelegateCatalog) catalogElement; + if (delegate.getEntryType() == IDelegateCatalog.DELEGATE_TYPE_PUBLIC) { + delegatePublicList.add(delegate); + } else if (delegate.getEntryType() == IDelegateCatalog.DELEGATE_TYPE_SYSTEM) { + delegateSystemList.add(delegate); + } else { + delegateUriList.add(delegate); + } + } + } + } + + Collections.sort(rewriteSystemList, LONGEST_REWRITE_FIRST); + Collections.sort(rewriteUriList, LONGEST_REWRITE_FIRST); + + Collections.sort(suffixSystemList, LONGEST_SUFFIX_FIRST); + Collections.sort(suffixUriList, LONGEST_SUFFIX_FIRST); + + Collections.sort(delegatePublicList, LONGEST_DELEGATE_PREFIX_FIRST); + Collections.sort(delegateSystemList, LONGEST_DELEGATE_PREFIX_FIRST); + Collections.sort(delegateUriList, LONGEST_DELEGATE_PREFIX_FIRST); + } + + private Map getEntryMap(int entryType) { + Map map = schemaMap; + switch (entryType) { + case ICatalogEntry.ENTRY_TYPE_SCHEMA: + map = schemaMap; + break; + default: + break; + } + return map; + } + + protected String getMappedURI(Map map, String key) { + CatalogEntry entry = (CatalogEntry) map.get(key); + if (entry == null) + return null; + String uri = entry.getURI(); + try { + // TODO CS : do we really want to resolve these here? + // I'm guessing we should return the 'platform:' form of the URI + // to the caller. + if (uri.startsWith("platform:")) //$NON-NLS-1$ + { + URL entryURL = new URL(entry.getAbsolutePath(uri)); + uri = Platform.resolve(entryURL).toString(); + + // we need to ensure URI's are of form "file:///D:/XXX" and + // NOT + // "file:D:/XXX". Otherwise the EMF URI class gets confused + // (see bug 103607) + String FILE_SCHEME = "file:"; //$NON-NLS-1$ + if (uri.startsWith(FILE_SCHEME) + && !uri.startsWith(FILE_SCHEME + "/")) //$NON-NLS-1$ + { + uri = FILE_SCHEME + + "///" + uri.substring(FILE_SCHEME.length()); //$NON-NLS-1$ + } + } + return uri; + } catch (IOException e) { + return null; + } + } + + private String resolveRewrite(List rewriteList, String searchString) { + for (Iterator it = rewriteList.iterator(); it.hasNext();) { + IRewriteEntry entry = (IRewriteEntry) it.next(); + String startString = entry.getStartString(); + if (searchString.startsWith(startString)) { + return entry.getRewritePrefix() + + searchString.substring(startString.length()); + } + } + return null; + } + + private String resolveSuffix(List suffixList, String searchString) { + for (Iterator it = suffixList.iterator(); it.hasNext();) { + ISuffixEntry entry = (ISuffixEntry) it.next(); + if (searchString.endsWith(entry.getSuffix())) { + return entry.getURI(); + } + } + return null; + } + + protected String resolveDelegateCatalogs(List delegateCatalogs, + String key, String systemId) throws MalformedURLException, + IOException { + String result = null; + for (Iterator iterator = delegateCatalogs.iterator(); iterator + .hasNext();) { + IDelegateCatalog delegate = (IDelegateCatalog) iterator.next(); + + if (key.startsWith(delegate.getStartString())) { + + ICatalog catalog = delegate.getReferencedCatalog(); + if (catalog != null) { + switch (delegate.getEntryType()) { + case IDelegateCatalog.DELEGATE_TYPE_SCHEMA: + result = catalog.resolveSchema(systemId); + break; + default: + break; + } + if (result != null) { + return result; + } + } + } + } + return null; + } + + public String resolveSchema(String fileName) + throws MalformedURLException, IOException { + String result = getMappedURI(schemaMap, fileName); + // if (result == null) { + // result = resolveRewrite(rewriteUriList, fileName); + // } + // if (result == null) { + // result = resolveSuffix(suffixUriList, fileName); + // } + // if (result == null) { + // result = resolveDelegateCatalogs(delegateUriList, fileName, + // fileName); // uri + // // is + // // treated + // // as + // // the + // // systemId + // } + if (result == null) { + result = resolveSubordinateCatalogs( + ICatalogEntry.ENTRY_TYPE_SCHEMA, null, fileName); + } + return result; + } + } + + class SystemCatalogLS extends CatalogLS { + public void load() { + new CatalogContributorRegistryReader(Catalog.this).readRegistry(); + + new CatalogSchemastoreReader(Catalog.this).readSchemastore(); + /* + * Here we save the file in order to 'reflect' the catalog that + * we've created from plugin extensions to disk. The 'system' + * catalog is only ever written to disk and never read from disk. + */ + save(); + } + } + + class UserCatalogLS extends CatalogLS { + public void load() { + new CatalogUserCatalogReader(Catalog.this).readCatalog(); + + save(); + } + } + + public static final String DEFAULT_CATALOG_FILE = "default_catalog.xml"; //$NON-NLS-1$ + + public static final String SYSTEM_CATALOG_FILE = "system_catalog.xml"; //$NON-NLS-1$ + + public static final String USER_CATALOG_FILE = "user_catalog.xml"; //$NON-NLS-1$ + + protected String base; + + protected List catalogElements = new ArrayList(); + + protected CatalogLS catalogLS; + + protected String id; + + protected InternalResolver internalResolver; + + protected boolean isNotificationEnabled; + + protected List listenerList = new ArrayList(); + + protected String location; + + protected CatalogSet resourceSet; + + public Catalog(CatalogSet catalogResourceSet, String id, String location) { + this.resourceSet = catalogResourceSet; + this.id = id; + this.location = location; + + if (JSONCorePlugin.DEFAULT_CATALOG_ID.equals(id)) { + catalogLS = new DefaultCatalogLS(); + } else if (JSONCorePlugin.SYSTEM_CATALOG_ID.equals(id)) { + catalogLS = new SystemCatalogLS(); + } else { + catalogLS = new UserCatalogLS(); + } + } + + public void addCatalogElement(ICatalogElement element) { + synchronized (catalogElements) { + if (!catalogElements.contains(element)) { + catalogElements.add(element); + } else { + return; + } + } + element.setOwnerCatalog(this); + internalResolver = null; + notifyAddElement(element); + } + + public void addEntriesFromCatalog(ICatalog catalog) { + try { + setNotificationEnabled(false); + if (catalog != null) { + ICatalogElement[] entries = ((Catalog) catalog) + .getCatalogElements(); + for (int i = 0; i < entries.length; i++) { + CatalogElement clone = (CatalogElement) ((CatalogElement) entries[i]) + .clone(); + addCatalogElement(clone); + } + } else { + Logger.log(Logger.ERROR, + "argument was null in Catalog.addEntriesFromCatalog"); //$NON-NLS-1$ + } + } finally { + setNotificationEnabled(true); + } + internalResolver = null; + notifyChanged(); + } + + public void addListener(ICatalogListener listener) { + listenerList.add(listener); + } + + public void clear() { + synchronized (catalogElements) { + catalogElements.clear(); + } + internalResolver = null; + notifyChanged(); + } + + public ICatalogElement createCatalogElement(int type) { + switch (type) { + case ICatalogElement.TYPE_ENTRY: + return new CatalogEntry(); // TODO: Should be kind of deprecated + case ICatalogElement.TYPE_NEXT_CATALOG: + return new NextCatalog(); + case ICatalogEntry.ENTRY_TYPE_SCHEMA: + return new CatalogEntry(type); + // case ICatalogElement.TYPE_REWRITE: + // case IRewriteEntry.REWRITE_TYPE_SYSTEM: + // case IRewriteEntry.REWRITE_TYPE_URI: + // return new RewriteEntry(type); + // case ICatalogElement.TYPE_SUFFIX: + // case ISuffixEntry.SUFFIX_TYPE_SYSTEM: + // case ISuffixEntry.SUFFIX_TYPE_URI: + // return new SuffixEntry(type); + // case ICatalogElement.TYPE_DELEGATE: + // case IDelegateCatalog.DELEGATE_TYPE_PUBLIC: + // case IDelegateCatalog.DELEGATE_TYPE_SYSTEM: + // case IDelegateCatalog.DELEGATE_TYPE_URI: + // return new DelegateCatalog(type); + default: + throw new IllegalArgumentException("Unknown element type " + type);//$NON-NLS-1 // Makes no sense at all! + } + } + + public String getBase() { + return base; + } + + private List getCatalogElements(int type) { + List result = new ArrayList(); + ICatalogElement[] elements = (ICatalogElement[]) catalogElements + .toArray(new ICatalogElement[catalogElements.size()]); + for (int i = 0; i < elements.length; i++) { + ICatalogElement element = elements[i]; + if (element.getType() == type) { + result.add(element); + } + } + return result; + } + + public ICatalogEntry[] getCatalogEntries() { + List result = getCatalogElements(ICatalogElement.TYPE_ENTRY); + return (ICatalogEntry[]) result + .toArray(new ICatalogEntry[result.size()]); + } + + public IDelegateCatalog[] getDelegateCatalogs() { + List result = getCatalogElements(ICatalogElement.TYPE_DELEGATE); + return (IDelegateCatalog[]) result.toArray(new IDelegateCatalog[result + .size()]); + } + + public IRewriteEntry[] getRewriteEntries() { + List result = getCatalogElements(ICatalogElement.TYPE_REWRITE); + return (IRewriteEntry[]) result + .toArray(new IRewriteEntry[result.size()]); + } + + public ISuffixEntry[] getSuffixEntries() { + List result = getCatalogElements(ICatalogElement.TYPE_SUFFIX); + return (ISuffixEntry[]) result.toArray(new ISuffixEntry[result.size()]); + } + + protected CatalogSet getCatalogSet() { + return resourceSet; + } + + public String getId() { + return id; + } + + public String getLocation() { + return location; + } + + public INextCatalog[] getNextCatalogs() { + List result = getCatalogElements(ICatalogElement.TYPE_NEXT_CATALOG); + return (INextCatalog[]) result.toArray(new INextCatalog[result.size()]); + } + + protected InternalResolver getOrCreateInternalResolver() { + if (internalResolver == null) { + internalResolver = new InternalResolver(); + } + return internalResolver; + } + + protected boolean isNotificationEnabled() { + return isNotificationEnabled; + } + + public void load() throws IOException { + catalogLS.load(); + } + + protected void notifyAddElement(ICatalogElement entry) { + if (isNotificationEnabled) { + ICatalogEvent event = new CatalogEvent(this, entry, + ICatalogEvent.ELEMENT_ADDED); + notifyListeners(event); + } + } + + protected void notifyChanged() { + ICatalogEvent event = new CatalogEvent(this, null, + ICatalogEvent.CHANGED); + notifyListeners(event); + } + + protected void notifyListeners(ICatalogEvent event) { + List list = new ArrayList(); + list.addAll(listenerList); + for (Iterator i = list.iterator(); i.hasNext();) { + ICatalogListener listener = (ICatalogListener) i.next(); + listener.catalogChanged(event); + } + } + + protected void notifyRemoveElement(ICatalogElement element) { + if (isNotificationEnabled) { + ICatalogEvent event = new CatalogEvent(this, element, + ICatalogEvent.ELEMENT_REMOVED); + notifyListeners(event); + } + } + + public void removeCatalogElement(ICatalogElement element) { + synchronized (catalogElements) { + catalogElements.remove(element); + } + internalResolver = null; + notifyRemoveElement(element); + + } + + public void removeListener(ICatalogListener listener) { + listenerList.remove(listener); + } + + protected String resolveSubordinateCatalogs(int entryType, String publicId, + String systemId) throws MalformedURLException, IOException { + String result = null; + INextCatalog[] nextCatalogs = getNextCatalogs(); + for (int i = 0; i < nextCatalogs.length; i++) { + INextCatalog nextCatalog = nextCatalogs[i]; + ICatalog catalog = nextCatalog.getReferencedCatalog(); + if (catalog != null) { + switch (entryType) { + case ICatalogEntry.ENTRY_TYPE_SCHEMA: + result = catalog.resolveSchema(systemId); + break; + default: + break; + } + if (result != null) { + return result; + } + } + } + return null; + } + + public void save() throws IOException { + catalogLS.save(); + } + + public void setBase(String base) { + this.base = base; + } + + public void setId(String id) { + this.id = id; + } + + public void setLocation(String location) { + this.location = location; + } + + protected void setNotificationEnabled(boolean b) { + isNotificationEnabled = b; + } + + public ICatalogElement[] getCatalogElements() { + return (ICatalogElement[]) catalogElements + .toArray(new ICatalogElement[catalogElements.size()]); + } + + @Override + public String resolveSchema(String fileMatch) throws MalformedURLException, + IOException { + return getOrCreateInternalResolver().resolveSchema(fileMatch); + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogContributorRegistryReader.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogContributorRegistryReader.java new file mode 100644 index 0000000000..46f82d92de --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogContributorRegistryReader.java @@ -0,0 +1,277 @@ +/******************************************************************************* + * Copyright (c) 2002, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.CatalogContributorRegistryReader + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import java.io.IOException; +import java.net.URL; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.InvalidRegistryObjectException; +import org.eclipse.core.runtime.Platform; +import org.eclipse.wst.json.core.JSONCorePlugin; +import org.eclipse.wst.json.core.internal.JSONCoreMessages; +import org.eclipse.wst.json.core.internal.Logger; +import org.eclipse.wst.json.core.schema.catalog.ICatalog; +import org.eclipse.wst.json.core.schema.catalog.ICatalogElement; +import org.eclipse.wst.json.core.schema.catalog.ICatalogEntry; +import org.osgi.framework.Bundle; + +/** + * <pre> + * <extension point="org.eclipse.wst.xml.core.schemaCatalogContributions"> + * <schemaCatalogContribution> + * <schema name="bower.json" + * description="Bower package description file" + * fileMatch="bower.json,bower.json" + * url="http://json.schemastore.org/bower" + * uri="schemastore/bower" /> + * <schema name=".bowerrc" + * description="Bower configuration file" + * fileMatch=".bowerrc" + * url="http://json.schemastore.org/bowerrc" + * uri="schemastore/bowerrc" /> + * <schema name=".jshintrc" + * description="JSHint configuration file" + * fileMatch=".jshintrc" + * url="http://json.schemastore.org/jshintrc" + * uri="schemastore/jshintrc" /> + * + * </schemaCatalogContribution> + * </extension> + * </pre> + * + */ +public class CatalogContributorRegistryReader { + protected static final String EXTENSION_POINT_ID = "schemaCatalogContributions"; //$NON-NLS-1$ + protected static final String TAG_CONTRIBUTION = "schemaCatalogContribution"; //$NON-NLS-1$ + + protected ICatalog catalog; + + protected String declaringExtensionId; + + protected CatalogContributorRegistryReader(ICatalog catalog) { + this.catalog = catalog; + } + + /** + * read from plugin registry and parse it. + */ + protected void readRegistry() { + IExtensionRegistry pluginRegistry = Platform.getExtensionRegistry(); + IExtensionPoint point = pluginRegistry + .getExtensionPoint(JSONCorePlugin.getDefault().getBundle() + .getSymbolicName(), EXTENSION_POINT_ID); + if (point != null) { + IConfigurationElement[] elements = point.getConfigurationElements(); + for (int i = 0; i < elements.length; i++) { + readElement(elements[i]); + } + } + + } + + public static String resolvePath(URL platformURL, String path) { + String fileLocation = path; + int jarPathStart = path.indexOf("jar:"); //$NON-NLS-1$ + jarPathStart = jarPathStart < 0 ? 0 : jarPathStart + "jar:".length(); //$NON-NLS-1$ + int jarPathEnd = path.indexOf("!"); //$NON-NLS-1$ + jarPathEnd = jarPathEnd < 0 ? path.length() : jarPathEnd; + fileLocation = path.substring(jarPathStart, jarPathEnd); + + String result = path; + String resolvedLocation = fileLocation; + URL resolvedURL = null; + if (fileLocation.startsWith("platform:/plugin")) //$NON-NLS-1$ + { + // this is the speclial case, where the resource is located relative + // to another plugin (not the one that declares the extension point) + // + try { + resolvedURL = Platform.resolve(new URL(fileLocation)); + } catch (IOException e) { + // do nothing + } + } else { + // this is the typical case, where the resource is located relative + // to the plugin that declares the extension point + try { + resolvedURL = new URL(Platform.resolve(platformURL), + fileLocation); + } catch (IOException e) { + // do nothing + } + } + + if (resolvedURL != null) { + resolvedLocation = resolvedURL.toExternalForm().replace('\\', '/'); + result = result.replaceFirst(fileLocation, resolvedLocation); + } + return result; + } + + public static URL getPlatformURL(String pluginId) { + Bundle bundle = Platform.getBundle(pluginId); + if (bundle != null) { + URL bundleEntry = bundle.getEntry("/"); //$NON-NLS-1$ + + if (bundleEntry != null) { + try { + return Platform.resolve(bundleEntry); + } catch (IOException e) { + Logger.logException(e); + } + } + } + return null; + } + + private String resolvePath(String path) { + return resolvePath(getPlatformURL(declaringExtensionId), path); + } + + protected void readElement(IConfigurationElement element) { + try { + declaringExtensionId = element.getDeclaringExtension() + .getNamespace(); + } catch (InvalidRegistryObjectException e) { + Logger.logException(e); + } + + if (TAG_CONTRIBUTION.equals(element.getName())) { + IConfigurationElement[] mappingInfoElementList = element + .getChildren(SchemaStoreCatalogConstants.TAG_SCHEMA); + processMappingInfoElements(mappingInfoElementList); + } + + } + + private void processMappingInfoElements( + IConfigurationElement[] childElementList) { + if (catalog == null) + return; + for (int i = 0; i < childElementList.length; i++) { + IConfigurationElement childElement = childElementList[i]; + String name = childElement.getName(); + int type = ICatalogEntry.ENTRY_TYPE_SCHEMA; + String fileMatch = null; + if (SchemaStoreCatalogConstants.TAG_SCHEMA.equals(name)) { + fileMatch = childElement + .getAttribute(SchemaStoreCatalogConstants.ATTR_SCHEMA_FILEMATCH); +// fileMatch = childElement +// .getAttribute(SchemaStoreCatalogConstants.ATTR_SCHEMA_FILEMATCH); + + } +/*if (SchemaStoreCatalogConstants.TAG_PUBLIC.equals(name)) { + key = childElement + .getAttribute(SchemaStoreCatalogConstants.ATTR_PUBLIC_ID); + } else if (SchemaStoreCatalogConstants.TAG_SYSTEM.equals(name)) { + key = childElement + .getAttribute(SchemaStoreCatalogConstants.ATTR_SYSTEM_ID); + type = ICatalogEntry.ENTRY_TYPE_SYSTEM; + } else if (SchemaStoreCatalogConstants.TAG_URI.equals(name)) { + key = childElement + .getAttribute(SchemaStoreCatalogConstants.ATTR_NAME); + type = ICatalogEntry.ENTRY_TYPE_URI; + } else if (SchemaStoreCatalogConstants.TAG_NEXT_CATALOG + .equals(name)) { + processNextSchemaCatalogElements(new IConfigurationElement[] { childElement }); + continue; + } + if (key == null || key.equals("")) //$NON-NLS-1$ + { + Logger.log(Logger.ERROR, + JSONCoreMessages.SchemaCatalog_entry_key_not_set); + continue; + }*/ + String entryURI = childElement + .getAttribute(SchemaStoreCatalogConstants.ATTR_SCHEMA_URI); // mandatory + if (entryURI == null || entryURI.equals("")) //$NON-NLS-1$ + { + Logger.log(Logger.ERROR, + JSONCoreMessages.Catalog_entry_uri_not_set); + continue; + } + ICatalogElement catalogElement = catalog + .createCatalogElement(type); + if (catalogElement instanceof ICatalogEntry) { + ICatalogEntry entry = (ICatalogEntry) catalogElement; + entry.setKey(fileMatch); + String resolvedPath = resolvePath(entryURI); + entry.setURI(resolvedPath); + String id = childElement + .getAttribute(SchemaStoreCatalogConstants.ATTR_SCHEMA_NAME); // optional + if (id != null && !id.equals("")) //$NON-NLS-1$ + { + entry.setId(id); + } + } + // process any other attributes + for (int j = 0; j < childElement.getAttributeNames().length; j++) { + String attrName = childElement.getAttributeNames()[j]; +// if (!attrName.equals(SchemaStoreCatalogConstants.ATTR_URI) +// && !attrName +// .equals(SchemaStoreCatalogConstants.ATTR_NAME) +// && !attrName +// .equals(SchemaStoreCatalogConstants.ATTR_PUBLIC_ID) +// && !attrName +// .equals(SchemaStoreCatalogConstants.ATTR_SYSTEM_ID) +// && !attrName +// .equals(SchemaStoreCatalogConstants.ATTR_CATALOG) +// && !attrName +// .equals(SchemaStoreCatalogConstants.ATTR_ID) +// && !attrName +// .equals(SchemaStoreCatalogConstants.ATTR_BASE)) { +// String attrValue = childElement.getAttribute(attrName); +// if (attrValue != null && !attrValue.equals("")) //$NON-NLS-1$ +// { +// schemaCatalogElement.setAttributeValue(attrName, +// attrValue); +// } +// } + } + catalog.addCatalogElement(catalogElement); + } + } + +// private void processNextSchemaCatalogElements( +// IConfigurationElement[] childElementList) { +// if (schemaCatalog == null) +// return; +// for (int i = 0; i < childElementList.length; i++) { +// IConfigurationElement childElement = childElementList[i]; +// String location = childElement +// .getAttribute(SchemaStoreCatalogConstants.ATTR_CATALOG); // mandatory +// if (location == null || location.equals("")) //$NON-NLS-1$ +// { +// Logger.log( +// Logger.ERROR, +// JSONCoreMessages.SchemaCatalog_next_schemaCatalog_location_uri_not_set); +// continue; +// } +// INextCatalog nextSchemaCatalog = new NextCatalog(); +// String resolvedPath = resolvePath(location); +// nextSchemaCatalog.setCatalogLocation(resolvedPath); +// String id = childElement +// .getAttribute(SchemaStoreCatalogConstants.ATTR_ID); +// if (id != null && !id.equals("")) //$NON-NLS-1$ +// { +// nextSchemaCatalog.setId(id); +// } +// schemaCatalog.addCatalogElement(nextSchemaCatalog); +// } +// } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogElement.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogElement.java new file mode 100644 index 0000000000..73598b2ed7 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogElement.java @@ -0,0 +1,174 @@ +/******************************************************************************* + * Copyright (c) 2002, 2009 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 + * Jesper Steen Moller - jesper@selskabet.org - bug 112284 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.CatalogElement + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.wst.json.core.schema.catalog.ICatalog; +import org.eclipse.wst.json.core.schema.catalog.ICatalogElement; + +public class CatalogElement implements ICatalogElement { + int type; + + String id; + + String base; + + Map attributes = new HashMap(); + + ICatalog ownerCatalog; + + public CatalogElement(int aType) { + super(); + type = aType; + } + + public int getType() { + return type; + } + + public String getBase() { + return base; + } + + public void setBase(String base) { + this.base = base; + } + + public String getAttributeValue(String name) { + return (String) attributes.get(name); + } + + public void setAttributeValue(String name, String value) { + attributes.put(name, value); + } + + public String[] getAttributes() { + Collection c = attributes.values(); + String[] result = new String[c.size()]; + attributes.keySet().toArray(result); + return result; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ICatalog getOwnerCatalog() { + return ownerCatalog; + } + + public void setOwnerCatalog(ICatalog catalog) { + this.ownerCatalog = catalog; + } + + protected static String makeAbsolute(String baseLocation, String location) { + URL local = null; + location = location.replace('\\', '/'); + try { + URL baseURL = new URL(baseLocation); + local = new URL(baseURL, location); + } catch (MalformedURLException e) { + } + + if (local != null) { + return local.toString(); + } else { + return location; + } + } + + public String getAbsolutePath(String path) { + try { + URI uri = new URI(path); + if (uri.isAbsolute()) { + return path; + } + } catch (URISyntaxException e) { + } + + if (this.base != null && !this.base.equals("")) //$NON-NLS-1$ + { + return makeAbsolute(base, path); + } + + String result = path; + Catalog catalog = (Catalog) getOwnerCatalog(); + if (catalog != null) { + String base = catalog.getBase(); + if (base == null || base.equals("")) //$NON-NLS-1$ + { + base = catalog.getLocation(); + } + result = makeAbsolute(base, path); + } + return result; + } + + /* + * Since we don't have events notifications for entry properties, clone() + * could allow to copy and edit entry and then replace it in catalog. Entry + * replacement will signal ICatalogEvent @return + */ + public Object clone() { + ICatalogElement element = ownerCatalog.createCatalogElement(type); + String[] attributes = getAttributes(); + for (int i = 0; i < attributes.length; i++) { + String attrName = attributes[i]; + String attrValue = getAttributeValue(attrName); + element.setAttributeValue(attrName, attrValue); + } + element.setOwnerCatalog(ownerCatalog); + element.setId(id); + element.setBase(base); + return element; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CatalogElement other = (CatalogElement) obj; + if (id == null) { + if (other.id != null) + return false; + } else if (!id.equals(other.id)) + return false; + return true; + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogEntry.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogEntry.java new file mode 100644 index 0000000000..cdbcdac4aa --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogEntry.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2002, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.CatalogEntry + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import org.eclipse.wst.json.core.schema.catalog.ICatalogElement; +import org.eclipse.wst.json.core.schema.catalog.ICatalogEntry; + +public class CatalogEntry extends CatalogElement implements ICatalogEntry, + Cloneable { + int entryType = ICatalogEntry.ENTRY_TYPE_SCHEMA; + String key; + String uri; + + protected CatalogEntry(int anEntryType) { + super(ICatalogElement.TYPE_ENTRY); + entryType = anEntryType; + } + + protected CatalogEntry() { + super(ICatalogElement.TYPE_ENTRY); + } + + public void setEntryType(int value) { + entryType = value; + } + + public int getEntryType() { + return entryType; + } + + public void setKey(String value) { + key = value; + } + + public String getKey() { + return key; + } + + public String getURI() { + return uri; + } + + public void setURI(String value) { + uri = value; + } + + public Object clone() { + CatalogEntry entry = (CatalogEntry) super.clone(); + entry.setEntryType(entryType); + entry.setKey(key); + entry.setURI(uri); + return entry; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogEvent.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogEvent.java new file mode 100644 index 0000000000..b2552d803d --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogEvent.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2002, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.CatalogEvent + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import org.eclipse.wst.json.core.schema.catalog.ICatalog; +import org.eclipse.wst.json.core.schema.catalog.ICatalogElement; +import org.eclipse.wst.json.core.schema.catalog.ICatalogEvent; + +public class CatalogEvent implements ICatalogEvent { + protected ICatalog catalog; + protected ICatalogElement catalogElement; + protected int eventType; + + public CatalogEvent(Catalog catalog, ICatalogElement element, int eventType) { + this.catalog = catalog; + this.catalogElement = element; + this.eventType = eventType; + } + + public ICatalog getCatalog() { + return catalog; + } + + public ICatalogElement getCatalogElement() { + return catalogElement; + } + + public int getEventType() { + return eventType; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogSchemastoreReader.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogSchemastoreReader.java new file mode 100644 index 0000000000..5a323d7c8d --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogSchemastoreReader.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is 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: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Iterator; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.json.provisonnal.com.eclipsesource.json.JsonArray; +import org.eclipse.json.provisonnal.com.eclipsesource.json.JsonObject; +import org.eclipse.json.provisonnal.com.eclipsesource.json.JsonValue; +import org.eclipse.wst.json.core.JSONCorePlugin; +import org.eclipse.wst.json.core.internal.Logger; +import org.eclipse.wst.json.core.internal.download.HttpClientProvider; +import org.eclipse.wst.json.core.schema.catalog.ICatalog; +import org.eclipse.wst.json.core.schema.catalog.ICatalogElement; +import org.eclipse.wst.json.core.schema.catalog.ICatalogEntry; +import org.osgi.framework.Bundle; + +public class CatalogSchemastoreReader { + + private static final String SCHEMAS = "schemas"; //$NON-NLS-1$ + private static final String SCHEMASTORE_CATALOG = "https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/api/json/catalog.json"; //$NON-NLS-1$ + protected ICatalog catalog; + + protected CatalogSchemastoreReader(ICatalog catalog) { + this.catalog = catalog; + } + + protected void readSchemastore() { + File f = getUrl(); + if (f != null) { + int type = ICatalogEntry.ENTRY_TYPE_SCHEMA; + JsonValue schemas; + try { + InputStreamReader reader = new InputStreamReader(new FileInputStream(f)); + JsonObject json = JsonObject.readFrom(reader); + schemas = json.get(SCHEMAS); + } catch (IOException e) { + Logger.logException(e); + return; + } + if (schemas != null && schemas instanceof JsonArray) { + JsonArray elements = (JsonArray) schemas; + Iterator<JsonValue> iter = elements.iterator(); + while (iter.hasNext()) { + JsonValue value = iter.next(); + if (value instanceof JsonObject) { + JsonObject jsonObject = (JsonObject) value; + JsonValue urlJson = jsonObject.get("url"); //$NON-NLS-1$ + JsonValue fileMatchJson = jsonObject.get("fileMatch"); //$NON-NLS-1$ + if (urlJson != null && fileMatchJson != null && urlJson.isString() && fileMatchJson.isArray()) { + String url = urlJson.asString(); + JsonArray fileMatchArray = fileMatchJson.asArray(); + Iterator<JsonValue> fileIter = fileMatchArray.iterator(); + while (fileIter.hasNext()) { + JsonValue fileMatchValue = fileIter.next(); + if (fileMatchValue.isString()) { + String fileMatch = fileMatchValue.asString(); + ICatalogElement catalogElement = catalog.createCatalogElement(type); + if (catalogElement instanceof ICatalogEntry) { + ICatalogEntry entry = (ICatalogEntry) catalogElement; + entry.setKey(fileMatch); + entry.setURI(url); + entry.setId(fileMatch); + } + catalog.addCatalogElement(catalogElement); + } + } + } + } + } + } + } + } + + private static File getUrl() { + try { + File f = HttpClientProvider.getFile(new URL(SCHEMASTORE_CATALOG)); + if (f == null || !f.exists()) { + URL url = getUrlFromBundle(); + File file; + try { + file = new File(url.toURI()); + } catch(URISyntaxException e) { + file = new File(url.getPath()); + } + return file; + } else { + return f; + } + } catch (Exception e) { + Logger.logException(e); + } + return null; + } + + private static URL getUrlFromBundle() { + Bundle bundle = Platform.getBundle(JSONCorePlugin.PLUGIN_ID); + if (bundle != null) { + URL[] urls = FileLocator.findEntries(bundle, new Path("/schemastore/catalog.json")); //$NON-NLS-1$ + if (urls != null && urls.length > 0) { + try { + return FileLocator.resolve(urls[0]); + } catch (IOException e) { + Logger.logException(e); + } + } + } + return null; + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogSet.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogSet.java new file mode 100644 index 0000000000..6886d34c21 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogSet.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2002, 2011 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.CatalogSet + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.wst.json.core.internal.Logger; + +public class CatalogSet { + protected Map uriResourceMap = new HashMap(); + protected Map catalogPersistenceLocations = new HashMap(); + + public CatalogSet() { + super(); + } + + /** + * Find a Catalog with the given ID. If one is not found, create one at the + * given URI. + * + * @param id + * @param uri + * - the URI, the parent of this file path must already exist + * @return + */ + public Catalog lookupOrCreateCatalog(String id, String uri) { + Catalog catalog = getCatalog(id, uri); + if (catalog == null) { + catalog = new Catalog(this, id, uri); + try { + catalog.load(); + uriResourceMap.put(uri, catalog); + } catch (Exception e) { + // we catch and log all exceptions, to disallow + // one bad extension interfering with others + Logger.logException( + "error loading catalog: " + id + " " + uri, e); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + return catalog; + } + + private Catalog getCatalog(String id, String uri) { + return (Catalog) uriResourceMap.get(uri); + } + + public void putCatalogPersistenceLocation(String logicalURI, + String actualURI) { + catalogPersistenceLocations.put(logicalURI, actualURI); + } + + // Never used? + public String getCatalogPersistenceLocation(String id) { + return (String) catalogPersistenceLocations.get(id); + } + + public void clearResourceCache() {// Clearing only uriResourceMap is + // required + uriResourceMap.clear(); + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogUserCatalogReader.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogUserCatalogReader.java new file mode 100644 index 0000000000..0ca05c1e54 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogUserCatalogReader.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2016 Red Hat, Inc. + * Distributed under license by Red Hat, Inc. All rights reserved. + * This program is 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: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import java.util.Set; + +import org.eclipse.wst.json.core.schema.catalog.ICatalog; +import org.eclipse.wst.json.core.schema.catalog.ICatalogElement; +import org.eclipse.wst.json.core.schema.catalog.ICatalogEntry; + +public class CatalogUserCatalogReader { + + protected ICatalog catalog; + + protected CatalogUserCatalogReader(ICatalog catalog) { + this.catalog = catalog; + } + + protected void readCatalog() { + int type = ICatalogEntry.ENTRY_TYPE_SCHEMA; + Set<UserEntry> entries = EntryParser.getUserEntries(); + for (UserEntry ue : entries) { + ICatalogElement catalogElement = catalog.createCatalogElement(type); + if (catalogElement instanceof ICatalogEntry) { + ICatalogEntry entry = (ICatalogEntry) catalogElement; + entry.setKey(ue.getFileMatch()); + entry.setURI(ue.getUrl().toString()); + entry.setId(ue.getFileMatch()); + } + catalog.addCatalogElement(catalogElement); + } + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/EntryParser.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/EntryParser.java new file mode 100644 index 0000000000..acb659aae8 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/EntryParser.java @@ -0,0 +1,138 @@ +/************************************************************************************* + * Copyright (c) 2014-2015 Red Hat, Inc. 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: + * JBoss by Red Hat - Initial implementation. + ************************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.wst.json.core.JSONCorePlugin; + +@SuppressWarnings("nls") +public class EntryParser { + + public static final String JSON_CATALOG_ENTRIES = "catalogEntries"; //$NON-NLS-1$ + private static final JAXBContext jaxbContext; + + static { + try { + jaxbContext = JAXBContext.newInstance(EntriesWrapper.class); + } catch (JAXBException e) { + throw new RuntimeException(e); + } + } + + public Set<UserEntry> parse(String xml) throws CoreException { + if (xml == null || xml.trim().isEmpty()) { + return null; + } + try { + EntriesWrapper list = (EntriesWrapper) unmarshall(jaxbContext, xml); + return list.entries == null ? Collections.<UserEntry>emptySet() : list.entries; + } catch (Exception e) { + throw new CoreException(new Status(IStatus.ERROR, JSONCorePlugin.PLUGIN_ID, + "Unable to parse entry", e)); + } + } + + public String serialize(Set<UserEntry> entries) throws CoreException { + try { + EntriesWrapper list = new EntriesWrapper(); + list.entries = entries; + Marshaller marshaller = jaxbContext.createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); + StringWriter writer = new StringWriter(); + marshaller.marshal(list, writer); + return writer.toString(); + } catch (Exception shouldntHappen) { + throw new CoreException(new Status(IStatus.ERROR, JSONCorePlugin.PLUGIN_ID, + "Unable to serialize entries ", shouldntHappen)); + } + } + + public static Set<UserEntry> getUserEntries() { + Set<UserEntry> entries = new LinkedHashSet<UserEntry>(); + IEclipsePreferences prefs = getPreferences(); + String xml = prefs.get(JSON_CATALOG_ENTRIES, null); + if (xml != null && !xml.trim().isEmpty()) { + try { + Set<UserEntry> set = new EntryParser().parse(xml); + if (set != null) { + entries.addAll(set); + } + } catch (CoreException e) { + IStatus status = new Status(IStatus.ERROR, JSONCorePlugin.PLUGIN_ID, e + .getLocalizedMessage(), e); + JSONCorePlugin.getDefault().getLog().log(status); + } + } + return entries; + } + + private static IEclipsePreferences getPreferences() { + IEclipsePreferences preferences = InstanceScope.INSTANCE + .getNode("org.eclipse.wst.json.ui"); //$NON-NLS-1$ + return preferences; + } + + @XmlRootElement(name = "entries") + @XmlAccessorType (XmlAccessType.FIELD) + static class EntriesWrapper { + @XmlElement(name = "entry", type=UserEntry.class) + Set<UserEntry> entries; + } + + protected Object unmarshall(JAXBContext jaxbContext, String xml) throws JAXBException, IOException, XMLStreamException { + return unmarshall(jaxbContext, new StringReader(xml)); + } + + protected Object unmarshall(JAXBContext jaxbContext, File file) throws JAXBException, IOException, XMLStreamException { + return unmarshall(jaxbContext, new FileReader(file)); + } + + protected Object unmarshall(JAXBContext jaxbContext, Reader reader) throws JAXBException, IOException, XMLStreamException { + Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller(); + XMLInputFactory xmlif = XMLInputFactory.newInstance(); + Reader r = null; + try { + r = reader; + XMLStreamReader xmler = xmlif.createXMLStreamReader(r); + return jaxbUnmarshaller.unmarshal(xmler); + } finally { + if (r != null) { + r.close(); + } + } + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/JSONCatalogURIResolverExtension.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/JSONCatalogURIResolverExtension.java new file mode 100644 index 0000000000..72a3823433 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/JSONCatalogURIResolverExtension.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2002, 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.XMLCatalogURIResolverExtension + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import java.io.IOException; +import java.net.MalformedURLException; + +import org.eclipse.core.resources.IFile; +import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolverExtension; +import org.eclipse.wst.json.core.JSONCorePlugin; +import org.eclipse.wst.json.core.internal.JSONCoreMessages; +import org.eclipse.wst.json.core.internal.Logger; +import org.eclipse.wst.json.core.schema.catalog.ICatalog; + +/** + * This class is used to inject the JSONCatalog resolution behaviour into the + * Common Extensible URI Resolver. This class is referenced in the JSON Catalog + * plugin's plugin.xml file. + */ +public class JSONCatalogURIResolverExtension implements URIResolverExtension { + public String resolve(IFile file, String baseLocation, String publicId, String systemId) { + if (publicId != null || systemId != null) { + return null; + } + if (file == null) { // Cannot resolve schema with no file + Logger.log(Logger.ERROR_DEBUG, JSONCoreMessages.Catalog_resolution_malformed_url); + return null; + } + + // if we have catalog in a project we may add it + // to the catalog manager first + ICatalog catalog = JSONCorePlugin.getDefault().getDefaultJSONCatalog(); + if (catalog == null) { + Logger.log(Logger.ERROR_DEBUG, JSONCoreMessages.Catalog_resolution_null_catalog); + return null; + } + + try { + return catalog.resolveSchema(file.getName()); + } catch (MalformedURLException me) { + Logger.log(Logger.ERROR_DEBUG, JSONCoreMessages.Catalog_resolution_malformed_url); + } catch (IOException ie) { + Logger.log(Logger.ERROR_DEBUG, JSONCoreMessages.Catalog_resolution_io_exception); + } + return null; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/NextCatalog.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/NextCatalog.java new file mode 100644 index 0000000000..b05e648fb5 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/NextCatalog.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2002, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Raj Mandayam, IBM + * 136400 NextCatalog.getReferencedCatalog() takes a lot of time computing constant information + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.NextCatalog + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import org.eclipse.wst.json.core.schema.catalog.ICatalog; +import org.eclipse.wst.json.core.schema.catalog.ICatalogElement; +import org.eclipse.wst.json.core.schema.catalog.INextCatalog; + + + +public class NextCatalog extends CatalogElement implements INextCatalog +{ + private String location; + private ICatalog referencedCatalog; + + public NextCatalog() + { + super(ICatalogElement.TYPE_NEXT_CATALOG); + } + + public String getCatalogLocation() + { + return location; + } + + public ICatalog getReferencedCatalog() + { + if (referencedCatalog == null) + { + referencedCatalog = ((Catalog)ownerCatalog).getCatalogSet().lookupOrCreateCatalog(getId(), getAbsolutePath(location)); + } + return referencedCatalog; + } + + public void setCatalogLocation(String uri) + { + location = uri; + referencedCatalog = null; + } + + public Object clone() + { + NextCatalog nextCatalog = (NextCatalog)super.clone(); + nextCatalog.setCatalogLocation(location); + return nextCatalog; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/SchemaStoreCatalogConstants.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/SchemaStoreCatalogConstants.java new file mode 100644 index 0000000000..be74110f27 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/SchemaStoreCatalogConstants.java @@ -0,0 +1,31 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.schema.catalog;
+
+/**
+ * SchemaStore constants
+ *
+ * @see See http://schemastore.org/api.html
+ * @see https
+ * ://github.com/SchemaStore/schemastore/blob/master/src/api/json/catalog
+ * .json
+ */
+public interface SchemaStoreCatalogConstants {
+
+ /** Types of the schema entries */
+ /** The SCHEMA element name. */
+ String TAG_SCHEMA = "schema"; //$NON-NLS-1$
+ String ATTR_SCHEMA_NAME = "name"; //$NON-NLS-1$
+ String ATTR_SCHEMA_DESCRIPTION = "description"; //$NON-NLS-1$
+ String ATTR_SCHEMA_FILEMATCH = "fileMatch"; //$NON-NLS-1$
+ String ATTR_SCHEMA_URL = "url"; //$NON-NLS-1$
+ String ATTR_SCHEMA_URI = "uri"; //$NON-NLS-1$
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/UserEntries.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/UserEntries.java new file mode 100644 index 0000000000..23b296850f --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/UserEntries.java @@ -0,0 +1,26 @@ +/************************************************************************************* + * Copyright (c) 2016 Red Hat, Inc. 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: + * JBoss by Red Hat - Initial implementation. + ************************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import java.util.HashSet; +import java.util.Set; + +public class UserEntries { + private Set<UserEntry> entries = new HashSet<UserEntry>(); + + public Set<UserEntry> getEntries() { + return entries; + } + + public void add(UserEntry entry) { + entries.add(entry); + } +}
\ No newline at end of file diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/UserEntry.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/UserEntry.java new file mode 100644 index 0000000000..512fa9cbbc --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/UserEntry.java @@ -0,0 +1,45 @@ +/************************************************************************************* + * Copyright (c) 2016 Red Hat, Inc. 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: + * JBoss by Red Hat - Initial implementation. + ************************************************************************************/ +package org.eclipse.wst.json.core.internal.schema.catalog; + +import java.net.URI; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlAccessorType (XmlAccessType.FIELD) +@XmlRootElement(name="entry") +public class UserEntry { + + @XmlAttribute + private String fileMatch; + + @XmlAttribute + private URI url; + + public String getFileMatch() { + return fileMatch; + } + + public void setFileMatch(String fileMatch) { + this.fileMatch = fileMatch; + } + + public URI getUrl() { + return url; + } + + public void setUrl(URI url) { + this.url = url; + } +}
\ No newline at end of file diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/text/JSONStructuredDocumentReParser.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/text/JSONStructuredDocumentReParser.java new file mode 100644 index 0000000000..e37b9c4080 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/text/JSONStructuredDocumentReParser.java @@ -0,0 +1,439 @@ +/******************************************************************************* + * Copyright (c) 2016 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.text.CSSStructuredDocumentReParser + * modified in order to process JSON Objects. + * Alina Marin <alina@mx.ibm.com> - added a workaround while we fix the JSON lexical analyzer. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.text; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IRegion; +import org.eclipse.wst.json.core.internal.Logger; +import org.eclipse.wst.json.core.internal.util.RegionIterator; +import org.eclipse.wst.json.core.regions.JSONRegionContexts; +import org.eclipse.wst.sse.core.internal.provisional.events.StructuredDocumentEvent; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredTextReParser; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.text.StructuredDocumentReParser; + +/** + * This class provides a centralized place to put "reparsing" logic. This is the + * logic that reparses the text incrementally, as a user types in new + * characters, or JSON nodes are inserted or deleted. Note: it is not a thread + * safe class. + */ +public class JSONStructuredDocumentReParser extends StructuredDocumentReParser { + + class ReparseRange { + ReparseRange() { + reset(); + } + + ReparseRange(int start, int end) { + fRangeStart = start; + fRangeEnd = end; + } + + void setStart(int start) { + fRangeStart = start; + } + + void setEnd(int end) { + fRangeEnd = end; + } + + int getStart() { + return fRangeStart; + } + + int getEnd() { + return fRangeEnd; + } + + boolean isValid() { + return (0 < fRangeEnd - fRangeStart) ? true : false; + } + + void reset() { + fRangeStart = Integer.MAX_VALUE; + fRangeEnd = 0; + } + + void expand(ReparseRange range) { + if (range == null || !range.isValid()) { + return; + } + int start = range.getStart(); + if (start < fRangeStart) { + fRangeStart = start; + } + int end = range.getEnd(); + if (fRangeEnd < end) { + fRangeEnd = end; + } + } + + private int fRangeStart; + private int fRangeEnd; + } + + /** + * + */ + public JSONStructuredDocumentReParser() { + super(); + } + + /** + * + */ + // public StructuredDocumentEvent + // checkForCrossStructuredDocumentRegionBoundryCases2() { + // StructuredDocumentEvent result = specialPositionUpdate(fStart, fStart + + // fLengthToReplace - 1); + // if (result == null) { + // result = checkInsideBrace(); + // } + // return result; + // } + @Override + protected StructuredDocumentEvent checkForCrossStructuredDocumentRegionSyntax() { + int checkStart = fStart; + int checkEnd = fStart + fLengthToReplace - 1; + IStructuredDocumentRegion endRegion = fStructuredDocument + .getRegionAtCharacterOffset(checkEnd); + if (endRegion != null) { + checkEnd = endRegion.getEndOffset(); + } + + ReparseRange range = new ReparseRange(checkStart, checkEnd); + + range.expand(getUpdateRangeForDelimiter(range.getStart(), + range.getEnd())); + range.expand(getUpdateRangeForUnknownRegion(range.getStart(), + range.getEnd())); + + // range.expand(getUpdateRangeForQuotes(range.getStart(), + // range.getEnd())); + // range.expand(getUpdateRangeForComments(range.getStart(), + // range.getEnd())); + range.expand(getUpdateRangeForBraces(range.getStart(), range.getEnd())); + + StructuredDocumentEvent result; + + result = checkInsideBrace(range.getStart(), range.getEnd()); + + if (result == null) { + result = reparse(range.getStart(), range.getEnd()); + } + + return result; + } + + private ReparseRange getUpdateRangeForUnknownRegion(int start, int end) { + int newStart = start; + RegionIterator iterator = new RegionIterator(fStructuredDocument, + start - 1); + if (iterator.hasPrev()) { + iterator.prev(); // skip myself + } + while (iterator.hasPrev()) { + ITextRegion region = iterator.prev(); + if (region != null + && region.getType() == JSONRegionContexts.JSON_UNKNOWN) { + newStart = iterator.getStructuredDocumentRegion() + .getStartOffset(region); + } else { + break; + } + } + + if (start != newStart) { + return new ReparseRange(newStart, end); + } + + return null; + } + + private ReparseRange getUpdateRangeForDelimiter(int start, int end) { + if (dirtyStart != null && dirtyStart.getStart() < start) { + start = dirtyStart.getStart(); + } + IStructuredDocumentRegion docRegion = fStructuredDocument + .getRegionAtCharacterOffset(start); + if (docRegion == null) { + return null; + } + // if (docRegion.getType() == JSONRegionContexts.JSON_DELIMITER) { + // IStructuredDocumentRegion prevRegion = docRegion.getPrevious(); + // if (prevRegion != null) { + // return new ReparseRange(prevRegion.getStart(), end); + // } + // } + + return null; + } + + private ReparseRange getUpdateRangeForQuotes(int start, int end) { + ReparseRange range = new ReparseRange(); + + range.expand(getUpdateRangeForPair(start, end, '\"', '\"')); + range.expand(getUpdateRangeForPair(start, end, '\'', '\'')); + + return (range.isValid()) ? range : null; + } + + private ReparseRange getUpdateRangeForComments(int start, int end) { + ReparseRange range = new ReparseRange(); + + range.expand(getUpdateRangeForPair(start, end, "/*", "*/")); //$NON-NLS-2$//$NON-NLS-1$ + range.expand(getUpdateRangeForPair(start, end, "<%", "%>")); //$NON-NLS-2$//$NON-NLS-1$ + + return (range.isValid()) ? range : null; + } + + private ReparseRange getUpdateRangeForBraces(int start, int end) { + ReparseRange range = new ReparseRange(); + + range.expand(getUpdateRangeForPair(start, end, '[', ']')); + range.expand(getUpdateRangeForPair(start, end, '(', ')')); + range.expand(getUpdateRangeForPair(start, end, '{', '}')); + + return (range.isValid()) ? range : null; + } + + private StructuredDocumentEvent checkInsideBrace(int start, int end) { + StructuredDocumentEvent result = null; + IStructuredDocumentRegion region = fStructuredDocument + .getRegionAtCharacterOffset(start); + if (region == null) { + return null; + } + boolean bDeclaration = false; + String type = region.getType(); + // if (JSONRegionUtil.isDeclarationType(type) || type == + // JSONRegionContexts.JSON_LBRACE || type == + // JSONRegionContexts.JSON_RBRACE) { + // bDeclaration = true; + // } + // if (!bDeclaration) { + // IStructuredDocumentRegion prevRegion = + // JSONUtil.findPreviousSignificantNode(region); + // if (prevRegion != null) { + // type = prevRegion.getType(); + // if (JSONRegionUtil.isDeclarationType(type) || type == + // JSONRegionContexts.JSON_LBRACE) { + // bDeclaration = true; + // } + // } + // } + // if (!bDeclaration) { + // IStructuredDocumentRegion nextRegion = + // JSONUtil.findNextSignificantNode(region); + // if (nextRegion != null) { + // type = nextRegion.getType(); + // if (JSONRegionUtil.isDeclarationType(type) || type == + // JSONRegionContexts.JSON_RBRACE) { + // bDeclaration = true; + // } + // } + // } + bDeclaration = true; + if (bDeclaration) { + IStructuredDocumentRegion startRegion = findRuleStart(region); + if (startRegion != null) { + IStructuredDocumentRegion endRegion = fStructuredDocument + .getRegionAtCharacterOffset(end); + if (endRegion != null) { + result = reparse(startRegion, endRegion); + } + } + } + return result; + } + + private IStructuredDocumentRegion findRuleStart( + IStructuredDocumentRegion startRegion) { + // find '{' +// while (region != null +// && (region.getType() != JSONRegionContexts.JSON_OBJECT_OPEN && region +// .getType() != JSONRegionContexts.JSON_ARRAY_OPEN)) { +// region = region.getPrevious(); +// } + /* + * Workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=491944 + * Look for the parent object to make JSONTokenizer get the right tokens + * for the JSONSourceParser, this change doesn't affect the performance + * or the regions that the JSONModelParser will take + */ + IStructuredDocumentRegion region = startRegion.getParentDocument().getFirstStructuredDocumentRegion(); + if (region != null) { // '{' is found + return region; + } + return null; + } + + private boolean isLeadingDeclarationType(String type) { + return false; // (type == JSONRegionContexts.JSON_PAGE || type == + // JSONRegionContexts.JSON_FONT_FACE || + // JSONRegionUtil.isSelectorType(type)); + } + + // public StructuredDocumentEvent + // checkForCrossStructuredDocumentRegionBoundryCases() { + // return specialPositionUpdate(fStart, fStart + fLengthToReplace - 1); + // } + /** + * + */ + private ReparseRange getUpdateRangeForPair(int start, int end, + String opener, String closer) { + StringBuffer deletionBuf = new StringBuffer(); + StringBuffer insertionBuf = new StringBuffer(); + int quoteLen = Math.max(opener.length(), closer.length()); + if (1 < quoteLen) { + /** + * ex) opener = "ABC", closer = "XYZ" model: ABCDEF.......UVWXYZ + * deletion: CDEF.......UVWX pre/post: AB / YZ + */ + int addStart = start - (quoteLen - 1); + if (0 <= addStart) { + String addStr = fStructuredDocument.get(addStart, quoteLen - 1); + deletionBuf.append(addStr); + insertionBuf.append(addStr); + } + deletionBuf.append(fDeletedText); + insertionBuf.append(fChanges); + if (end + (quoteLen - 1) < fStructuredDocument.getLength()) { + String addStr = fStructuredDocument.get(end + 1, quoteLen - 1); + deletionBuf.append(addStr); + insertionBuf.append(addStr); + } + } else { + deletionBuf.append(fDeletedText); + insertionBuf.append(fChanges); + } + String deletion = deletionBuf.toString(); + String insertion = insertionBuf.toString(); + + int rangeStart = start; + int rangeEnd = end; + + if (0 <= deletion.indexOf(opener) || 0 <= deletion.indexOf(closer) + || 0 <= insertion.indexOf(opener) + || 0 <= insertion.indexOf(closer)) { + int s, e; + try { + if (start <= fStructuredDocument.getLength()) { + IRegion startRegion = getFindReplaceDocumentAdapter().find( + start - 1, opener, false, false, false, false); + if (startRegion != null) { + s = startRegion.getOffset(); + } else { + s = -1; + } + } else { + s = -1; + } + if (end < fStructuredDocument.getLength() - 1) { + IRegion endRegion = getFindReplaceDocumentAdapter().find( + end + 1, closer, true, false, false, false); + if (endRegion != null) { + e = endRegion.getOffset(); + } else { + e = -1; + } + } else { + e = -1; + } + } catch (BadLocationException ex) { + Logger.logException(ex); + return null; + } + if (0 <= s) { + rangeStart = Math.min(rangeStart, s); + } + if (0 <= e) { + rangeEnd = Math.max(rangeEnd, e); + } + } + + if (rangeStart < start || end < rangeEnd) { + return new ReparseRange(rangeStart, rangeEnd); + } else { + return null; + } + } + + private int findChar(char c, int start, boolean forward) + throws BadLocationException { + int stop = forward ? fStructuredDocument.getLength() : 0; + if (forward) { + for (; start < stop; start++) { + if (fStructuredDocument.getChar(start) == c) + return start; + } + } else { + for (; start >= stop; start--) { + if (fStructuredDocument.getChar(start) == c) + return start; + } + } + return -1; + } + + ReparseRange getUpdateRangeForPair(int start, int end, char opener, + char closer) { + StringBuffer deletionBuf = new StringBuffer(); + StringBuffer insertionBuf = new StringBuffer(); + deletionBuf.append(fDeletedText); + insertionBuf.append(fChanges); + String deletion = deletionBuf.toString(); + String insertion = insertionBuf.toString(); + + int rangeStart = start; + int rangeEnd = end; + + if (0 <= deletion.indexOf(opener) || 0 <= deletion.indexOf(closer) + || 0 <= insertion.indexOf(opener) + || 0 <= insertion.indexOf(closer)) { + int s = -1, e = -1; + try { + if (start <= fStructuredDocument.getLength()) { + s = findChar(opener, start - 1, false); + } + if (end < fStructuredDocument.getLength() - 1) { + e = findChar(closer, end + 1, true); + } + } catch (BadLocationException ex) { + Logger.logException(ex); + return null; + } + if (0 <= s) { + rangeStart = Math.min(rangeStart, s); + } + if (0 <= e) { + rangeEnd = Math.max(rangeEnd, e); + } + } + + if (rangeStart < start || end < rangeEnd) { + return new ReparseRange(rangeStart, rangeEnd); + } else { + return null; + } + } + + @Override + public IStructuredTextReParser newInstance() { + return new JSONStructuredDocumentReParser(); + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/text/StructuredTextPartitionerForJSON.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/text/StructuredTextPartitionerForJSON.java new file mode 100644 index 0000000000..f9a2a395d2 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/text/StructuredTextPartitionerForJSON.java @@ -0,0 +1,58 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.text;
+
+import org.eclipse.jface.text.IDocumentPartitioner;
+import org.eclipse.wst.json.core.regions.JSONRegionContexts;
+import org.eclipse.wst.json.core.text.IJSONPartitions;
+import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
+import org.eclipse.wst.sse.core.internal.text.rules.StructuredTextPartitioner;
+import org.eclipse.wst.sse.core.text.IStructuredPartitions;
+
+/**
+ * Structured text partitioner for JSON.
+ *
+ */
+public class StructuredTextPartitionerForJSON extends StructuredTextPartitioner {
+
+ public final static String[] legalTypes = new String[] {
+ IJSONPartitions.JSON, IJSONPartitions.COMMENT,
+ IStructuredPartitions.DEFAULT_PARTITION };
+
+ public StructuredTextPartitionerForJSON() {
+ super();
+ }
+
+ @Override
+ public String getPartitionType(ITextRegion region, int offset) {
+ String regionType = region.getType();
+ if (regionType == JSONRegionContexts.JSON_COMMENT) {
+ return IJSONPartitions.COMMENT;
+ }
+ return super.getPartitionType(region, offset);
+ }
+
+ @Override
+ public String getDefaultPartitionType() {
+ return IJSONPartitions.JSON;
+ }
+
+ @Override
+ public String[] getLegalContentTypes() {
+ return legalTypes;
+ }
+
+ @Override
+ public IDocumentPartitioner newInstance() {
+ return new StructuredTextPartitionerForJSON();
+ }
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/util/RegionIterator.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/util/RegionIterator.java new file mode 100644 index 0000000000..fabf1a6505 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/util/RegionIterator.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2004, 2005 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.css.core.internal.util.RegionIterator + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.util; + + + +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; + +/** + * + */ +public class RegionIterator { + + private IStructuredDocumentRegion documentRegion = null; + private IStructuredDocumentRegion curDocumentRegion = null; + private int current = -1; + + /** + * + */ + public RegionIterator(IStructuredDocument structuredDocument, int index) { + super(); + + reset(structuredDocument, index); + } + + /** + * + */ + public RegionIterator(IStructuredDocumentRegion flatNode, ITextRegion region) { + super(); + reset(flatNode, region); + } + + /** + * + */ + public IStructuredDocumentRegion getStructuredDocumentRegion() { + return curDocumentRegion; + } + + /** + * + */ + public boolean hasNext() { + if (documentRegion == null) + return false; + if (current < 0) + return false; + if (current < documentRegion.getRegions().size()) + return true; + return false; + } + + /** + * + */ + public boolean hasPrev() { + // the same as hasNext() + return hasNext(); + } + + /** + * + */ + public ITextRegion next() { + if (documentRegion == null) + return null; + if (current < 0 || documentRegion.getRegions() == null || documentRegion.getRegions().size() <= current) + return null; + + ITextRegion region = documentRegion.getRegions().get(current); + curDocumentRegion = documentRegion; + + if (current >= documentRegion.getRegions().size() - 1) { + documentRegion = documentRegion.getNext(); + current = -1; + } + current++; + + return region; + } + + /** + * + */ + public ITextRegion prev() { + if (documentRegion == null) + return null; + if (current < 0 || documentRegion.getRegions() == null || documentRegion.getRegions().size() <= current) + return null; + + ITextRegion region = documentRegion.getRegions().get(current); + curDocumentRegion = documentRegion; + + if (current == 0) { + documentRegion = documentRegion.getPrevious(); + if (documentRegion != null) + current = documentRegion.getRegions().size(); + else + current = 0; + } + current--; + + return region; + } + + /** + * + */ + public void reset(IStructuredDocument structuredDocument, int index) { + documentRegion = structuredDocument.getRegionAtCharacterOffset(index); + curDocumentRegion = documentRegion; + if (documentRegion != null) { + ITextRegion region = documentRegion.getRegionAtCharacterOffset(index); + current = documentRegion.getRegions().indexOf(region); + } + } + + /** + * + */ + public void reset(IStructuredDocumentRegion flatNode, ITextRegion region) { + if (region != null && flatNode != null) { + this.documentRegion = flatNode; + curDocumentRegion = flatNode; + current = flatNode.getRegions().indexOf(region); + } + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONNestedValidatorContext.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONNestedValidatorContext.java new file mode 100644 index 0000000000..8611b8c8a6 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONNestedValidatorContext.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2008 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.XMLNestedValidatorContext + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.validation; + +import java.util.HashSet; + +import org.eclipse.wst.json.core.internal.validation.core.NestedValidatorContext; + +/** + * JSONNestedValidatorContext is used to store state data needed during an JSON + * validation session. + */ +public class JSONNestedValidatorContext extends NestedValidatorContext { + /** + * A set of inaccessible locations URIs (String). + */ + private HashSet inaccessibleLocationURIs = new HashSet(); + + /** + * Determines if a location URI was marked as inaccessible. + * + * @param locationURI + * the location URI to test. Must not be null. + * @return true if a location URI was marked as inaccessible, false + * otherwise. + */ + public boolean isURIMarkedInaccessible(String locationURI) { + return locationURI != null + && inaccessibleLocationURIs.contains(locationURI); + } + + /** + * Marks the given location URI as inaccessible. + * + * @param locationURI + * the location URI to mark as inaccessible. Must not be null. + */ + public void markURIInaccessible(String locationURI) { + if (locationURI != null) { + inaccessibleLocationURIs.add(locationURI); + } + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONSyntaxValidator.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONSyntaxValidator.java new file mode 100644 index 0000000000..7ff3778f10 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONSyntaxValidator.java @@ -0,0 +1,257 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.validation;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.util.List;
+import java.util.Locale;
+import java.util.Stack;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceProxy;
+import org.eclipse.core.resources.IResourceProxyVisitor;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Preferences;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.wst.json.core.JSONCorePlugin;
+import org.eclipse.wst.json.core.contenttype.ContentTypeIdForJSON;
+import org.eclipse.wst.json.core.internal.JSONCoreMessages;
+import org.eclipse.wst.json.core.internal.Logger;
+import org.eclipse.wst.json.core.internal.parser.JSONLineTokenizer;
+import org.eclipse.wst.json.core.preferences.JSONCorePreferenceNames;
+import org.eclipse.wst.json.core.regions.JSONRegionContexts;
+import org.eclipse.wst.json.core.util.JSONUtil;
+import org.eclipse.wst.json.core.validation.AnnotationMsg;
+import org.eclipse.wst.json.core.validation.ISeverityProvider;
+import org.eclipse.wst.json.core.validation.JSONSyntaxValidatorHelper;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.document.DocumentReader;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.validation.AbstractValidator;
+import org.eclipse.wst.validation.ValidationResult;
+import org.eclipse.wst.validation.ValidationState;
+import org.eclipse.wst.validation.internal.core.Message;
+import org.eclipse.wst.validation.internal.core.ValidationException;
+import org.eclipse.wst.validation.internal.operations.IWorkbenchContext;
+import org.eclipse.wst.validation.internal.operations.LocalizedMessage;
+import org.eclipse.wst.validation.internal.provisional.core.IMessage;
+import org.eclipse.wst.validation.internal.provisional.core.IReporter;
+import org.eclipse.wst.validation.internal.provisional.core.IValidationContext;
+import org.eclipse.wst.validation.internal.provisional.core.IValidator;
+
+public class JSONSyntaxValidator extends AbstractValidator implements
+ IValidator, ISeverityProvider {
+
+ /** The validation reporter */
+ private IReporter fReporter;
+
+ private IContentType jsonContentType;
+
+ @Override
+ public void cleanup(IReporter reporter) {
+ jsonContentType = null;
+ }
+
+ @Override
+ public void validate(IValidationContext helper, IReporter reporter)
+ throws ValidationException {
+ final String[] uris = helper.getURIs();
+ IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+ if (uris.length > 0) {
+ IFile currentFile = null;
+
+ for (int i = 0; i < uris.length && !reporter.isCancelled(); i++) {
+ // might be called with just project path?
+ IPath path = new Path(uris[i]);
+ if (path.segmentCount() > 1) {
+ currentFile = root.getFile(path);
+ if (shouldValidate(currentFile, true)) {
+ validateFile(currentFile, reporter);
+ }
+ } else if (uris.length == 1) {
+ validateProject(helper, reporter);
+ }
+ }
+ } else
+ validateProject(helper, reporter);
+ }
+
+ public ValidationResult validate(IResource resource, int kind,
+ ValidationState state, IProgressMonitor monitor) {
+ if (resource.getType() != IResource.FILE)
+ return null;
+ ValidationResult result = new ValidationResult();
+ fReporter = result.getReporter(monitor);
+ validateFile((IFile) resource, fReporter);
+ return result;
+ }
+
+ /**
+ * Convenience method for validating a resource and getting back the
+ * reporter
+ *
+ * @param resource
+ * The resource to be validated.
+ * @param kind
+ * The way the resource changed. It uses the same values as the
+ * kind parameter in IResourceDelta.
+ * @param state
+ * A way to pass arbitrary, validator specific, data from one
+ * invocation of a validator to the next, during the validation
+ * phase. At the end of the validation phase, this object will be
+ * cleared, thereby allowing any of this state information to be
+ * garbaged collected.
+ * @return the validator's reporter
+ */
+ public IReporter validate(IResource resource, int kind,
+ ValidationState state) {
+ validate(resource, kind, state, new NullProgressMonitor());
+ return fReporter;
+ }
+
+ /**
+ * Validates the given file. It will stream the contents of the file without
+ * creating a model for the file; it will only use existing
+ *
+ * @param file
+ * the file to validate
+ * @param reporter
+ * the reporter
+ */
+ private void validateFile(IFile file, IReporter reporter) {
+ Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, file
+ .getFullPath().toString().substring(1));
+ reporter.displaySubtask(JSONSyntaxValidator.this, message);
+
+ JSONLineTokenizer tokenizer = null;
+ try {
+ IStructuredModel model = StructuredModelManager.getModelManager()
+ .getExistingModelForRead(file);
+ try {
+ if (model == null) {
+ tokenizer = new JSONLineTokenizer(new BufferedReader(
+ new InputStreamReader(file.getContents(true),
+ getCharset(file))));
+ } else {
+ tokenizer = new JSONLineTokenizer(new BufferedReader(
+ new DocumentReader(model.getStructuredDocument())));
+ }
+ JSONSyntaxValidatorHelper.validate(tokenizer, reporter, this,
+ this);
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ model = null;
+ }
+ }
+ } catch (UnsupportedEncodingException e) {
+ } catch (CoreException e) {
+ } catch (IOException e) {
+ }
+ }
+
+ private void validateProject(IValidationContext helper,
+ final IReporter reporter) {
+ // if uris[] length 0 -> validate() gets called for each project
+ if (helper instanceof IWorkbenchContext) {
+ IProject project = ((IWorkbenchContext) helper).getProject();
+ IResourceProxyVisitor visitor = new IResourceProxyVisitor() {
+ public boolean visit(IResourceProxy proxy) throws CoreException {
+ if (shouldValidate(proxy)) {
+ validateFile((IFile) proxy.requestResource(), reporter);
+ }
+ return true;
+ }
+ };
+ try {
+ // collect all jsp files for the project
+ project.accept(visitor, IResource.DEPTH_INFINITE);
+ } catch (CoreException e) {
+ Logger.logException(e);
+ }
+ }
+ }
+
+ private boolean shouldValidate(IResourceProxy proxy) {
+ if (proxy.getType() == IResource.FILE) {
+ String name = proxy.getName();
+ if (name.toLowerCase(Locale.US).endsWith(".json")) { //$NON-NLS-1$
+ return true;
+ }
+ }
+ return shouldValidate(proxy.requestResource(), false);
+ }
+
+ private boolean shouldValidate(IResource file, boolean checkExtension) {
+ if (file == null || !file.exists() || file.getType() != IResource.FILE)
+ return false;
+ if (checkExtension) {
+ String extension = file.getFileExtension();
+ if (extension != null
+ && "json".endsWith(extension.toLowerCase(Locale.US))) //$NON-NLS-1$
+ return true;
+ }
+
+ IContentDescription contentDescription = null;
+ try {
+ contentDescription = ((IFile) file).getContentDescription();
+ if (contentDescription != null) {
+ IContentType contentType = contentDescription.getContentType();
+ return contentDescription != null
+ && contentType.isKindOf(getJSONContentType());
+ }
+ } catch (CoreException e) {
+ Logger.logException(e);
+ }
+ return false;
+ }
+
+ private IContentType getJSONContentType() {
+ if (jsonContentType == null) {
+ jsonContentType = Platform.getContentTypeManager().getContentType(
+ ContentTypeIdForJSON.ContentTypeID_JSON); //$NON-NLS-1$
+ }
+ return jsonContentType;
+ }
+
+ private String getCharset(IFile file) {
+ if (file != null && file.isAccessible()) {
+ try {
+ return file.getCharset(true);
+ } catch (CoreException e) {
+ }
+ }
+ return ResourcesPlugin.getEncoding();
+ }
+
+ @Override
+ public int getSeverity(String preferenceName) {
+ return getPluginPreference().getInt(preferenceName);
+ }
+
+ private Preferences getPluginPreference() {
+ return JSONCorePlugin.getDefault().getPluginPreferences();
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationConfiguration.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationConfiguration.java new file mode 100644 index 0000000000..fbb8c4b75b --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationConfiguration.java @@ -0,0 +1,15 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.validation;
+
+public class JSONValidationConfiguration {
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationInfo.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationInfo.java new file mode 100644 index 0000000000..b7cceee528 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationInfo.java @@ -0,0 +1,51 @@ +/*******************************************************************************
+ * Copyright (c) 2001, 2014 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
+ * David Carver - STAR - [205989] - [validation] validate XML after XInclude resolution
+ * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.XMLValidationInfo
+ * modified in order to process JSON Objects.
+ *******************************************************************************/
+package org.eclipse.wst.json.core.internal.validation;
+
+import org.eclipse.json.IValidationReporter;
+import org.eclipse.wst.json.core.internal.validation.core.ValidationInfo;
+import org.eclipse.wst.json.core.validation.AnnotationMsg;
+
+public class JSONValidationInfo extends ValidationInfo implements
+ JSONValidationReport, IValidationReporter {
+
+ /**
+ * Constructor.
+ *
+ * @param uri
+ * The URI of the file this report describes.
+ */
+ public JSONValidationInfo(String uri) {
+ super(uri);
+ }
+
+ @Override
+ public boolean isGrammarEncountered() {
+ return false;
+ }
+
+ @Override
+ public void addMessage(String message, int line, int column, int offset) {
+ boolean isEndOfText = message.startsWith("Unexpected end of input");
+ AnnotationMsg annotation = new AnnotationMsg(
+ ProblemIDsJSON.MissingEndBracket, null, 1);//isEndOfText ? 1 : 1);
+ if (line > 1 && column == 0) {
+
+ }
+ int o = offset -1; //isEndOfText ? offset - 1 : offset -1;// -1;// - ( (line -2)* 2);
+ // if (column == 0) o=o-2;
+ super.addError(message, line - 1, o, getFileURI(), "null",
+ new Object[] { annotation });
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationReport.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationReport.java new file mode 100644 index 0000000000..49c60eebb9 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationReport.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.XMLValidationReport + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.validation; + +import org.eclipse.wst.json.core.internal.validation.core.ValidationReport; + +/** + * An interface represention a validation report for JSON validation. + * + */ +public interface JSONValidationReport extends ValidationReport { + + /** + * Returns whether a grammar was encountered during the validation. + * + * @return True if a grammar was encountered, false otherwise. + */ + public boolean isGrammarEncountered(); + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidator.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidator.java new file mode 100644 index 0000000000..a51bf1467a --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidator.java @@ -0,0 +1,312 @@ +/*******************************************************************************
+ * Copyright (c) 2001, 2014 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
+ * David Carver - STAR - [205989] - [validation] validate XML after XInclude resolution
+ * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.XMLValidator
+ * modified in order to process JSON Objects.
+ *******************************************************************************/
+package org.eclipse.wst.json.core.internal.validation;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.json.ValidatorHelper;
+import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolver;
+import org.eclipse.wst.common.uriresolver.internal.util.URIHelper;
+import org.eclipse.wst.json.core.JSONCorePlugin;
+import org.eclipse.wst.json.core.internal.validation.core.NestedValidatorContext;
+import org.eclipse.wst.json.core.preferences.JSONCorePreferenceNames;
+import org.eclipse.wst.json.core.validation.AnnotationMsg;
+import org.eclipse.wst.validation.ValidationResult;
+import org.eclipse.wst.validation.internal.ValOperation;
+import org.eclipse.wst.validation.internal.operations.LocalizedMessage;
+import org.eclipse.wst.validation.internal.provisional.core.IReporter;
+
+public class JSONValidator {
+
+ protected URIResolver uriResolver = null;
+ private JSONSyntaxValidator val = new JSONSyntaxValidator();
+ /**
+ * Validate the inputStream
+ *
+ * @param uri
+ * The URI of the file to validate.
+ * @param the
+ * inputStream of the file to validate
+ * @return Returns an JSON validation report.
+ */
+ public JSONValidationReport validate(String uri, InputStream inputStream) {
+ return validate(uri, inputStream, new JSONValidationConfiguration());
+ }
+
+ /**
+ * Validate the inputStream
+ *
+ * @param uri
+ * The URI of the file to validate.
+ * @param inputstream
+ * The inputStream of the file to validate
+ * @param configuration
+ * A configuration for this validation session.
+ * @return Returns an JSON validation report.
+ */
+ public JSONValidationReport validate(String uri, InputStream inputStream,
+ JSONValidationConfiguration configuration) {
+ return validate(uri, inputStream, configuration, null);
+ }
+
+ /**
+ * Validate the inputStream
+ *
+ * @param uri
+ * The URI of the file to validate.
+ * @param inputstream
+ * The inputStream of the file to validate
+ * @param configuration
+ * A configuration for this validation session.
+ * @param result
+ * The validation result
+ * @return Returns an JSON validation report.
+ */
+ public JSONValidationReport validate(String uri, InputStream inputStream,
+ JSONValidationConfiguration configuration, ValidationResult result) {
+ return validate(uri, inputStream, configuration, null, null);
+ }
+
+ /**
+ * Validate the inputStream
+ *
+ * @param uri
+ * The URI of the file to validate.
+ * @param inputstream
+ * The inputStream of the file to validate
+ * @param configuration
+ * A configuration for this validation session.
+ * @param result
+ * The validation result
+ * @param context
+ * The validation context
+ * @return Returns an JSON validation report.
+ */
+ public JSONValidationReport validate(String uri, InputStream inputStream,
+ JSONValidationConfiguration configuration, ValidationResult result,
+ NestedValidatorContext context) {
+ /*String grammarFile = ""; //$NON-NLS-1$
+ Reader reader1 = null; // Used for the preparse.
+ Reader reader2 = null; // Used for validation parse.
+
+ if (inputStream != null) {
+ String string = createStringForInputStream(inputStream);
+ reader1 = new StringReader(string);
+ reader2 = new StringReader(string);
+ }*/
+
+ JSONValidationInfo valinfo = new JSONValidationInfo(uri);
+ if (inputStream != null) {
+ ValidatorHelper.validate(new InputStreamReader(inputStream),
+ valinfo);
+ }
+ //JsonValidatorHelper.validate(text, reporter);
+
+ /*MyEntityResolver entityResolver = new MyEntityResolver(uriResolver,
+ context);
+ ValidatorHelper helper = new ValidatorHelper();
+ try {
+ helper.computeValidationInformation(uri, reader1, uriResolver);
+ valinfo.setDTDEncountered(helper.isDTDEncountered);
+ valinfo.setElementDeclarationCount(helper.numDTDElements);
+ valinfo.setNamespaceEncountered(helper.isNamespaceEncountered);
+ valinfo.setGrammarEncountered(helper.isGrammarEncountered);
+ JSONReader reader = createJSONReader(valinfo, entityResolver);
+ // Set the configuration option
+ if (configuration
+ .getFeature(JSONValidationConfiguration.HONOUR_ALL_SCHEMA_LOCATIONS)) {
+ reader.setFeature(
+ "http://apache.org/xml/features/honour-all-schemaLocations", true); //$NON-NLS-1$
+ }
+ if (configuration
+ .getFeature(JSONValidationConfiguration.USE_XINCLUDE)) {
+ reader.setFeature(
+ "http://apache.org/xml/features/xinclude", true); //$NON-NLS-1$
+ }
+
+ // Support external schema locations
+ boolean isGrammarEncountered = helper.isGrammarEncountered;
+ if (!isGrammarEncountered) {
+ isGrammarEncountered = checkExternalSchemas(reader,
+ valinfo.getFileURI());
+ }
+ reader.setFeature(
+ "http://xml.org/sax/features/validation", isGrammarEncountered); //$NON-NLS-1$
+ reader.setFeature(
+ "http://apache.org/xml/features/validation/schema", isGrammarEncountered); //$NON-NLS-1$
+
+ JSONErrorHandler errorhandler = new JSONErrorHandler(valinfo);
+ reader.setErrorHandler(errorhandler);
+
+ InputSource inputSource = new InputSource(uri);
+ inputSource.setCharacterStream(reader2);
+
+ ClassLoader originalClzLoader = Thread.currentThread()
+ .getContextClassLoader();
+ Thread.currentThread().setContextClassLoader(
+ getClass().getClassLoader());
+
+ try {
+ reader.parse(inputSource);
+ } finally {
+ Thread.currentThread().setContextClassLoader(originalClzLoader);
+ }
+
+ if (configuration
+ .getIntFeature(JSONValidationConfiguration.INDICATE_NO_GRAMMAR) > 0
+ && valinfo.isValid() && !isGrammarEncountered) {
+ if (configuration
+ .getIntFeature(JSONValidationConfiguration.INDICATE_NO_GRAMMAR) == 1)
+ valinfo.addWarning(JSONValidationMessages._WARN_NO_GRAMMAR,
+ 1, 0, uri, NO_GRAMMAR_FOUND, null);
+ else
+ // 2
+ valinfo.addError(JSONValidationMessages._WARN_NO_GRAMMAR,
+ 1, 0, uri, NO_GRAMMAR_FOUND, null);
+ }
+ if (configuration
+ .getIntFeature(JSONValidationConfiguration.INDICATE_NO_DOCUMENT_ELEMENT) > 0
+ && valinfo.isValid()
+ && !helper.isDocumentElementEncountered) {
+ if (configuration
+ .getIntFeature(JSONValidationConfiguration.INDICATE_NO_DOCUMENT_ELEMENT) == 1)
+ valinfo.addWarning(
+ JSONValidationMessages._NO_DOCUMENT_ELEMENT, 1, 0,
+ uri, NO_DOCUMENT_ELEMENT_FOUND, null);
+ else
+ // 2
+ valinfo.addError(
+ JSONValidationMessages._NO_DOCUMENT_ELEMENT, 1, 0,
+ uri, NO_DOCUMENT_ELEMENT_FOUND, null);
+ }
+ if (helper.isDTDEncountered)
+ grammarFile = entityResolver.getLocation();
+ else
+ grammarFile = helper.schemaLocationString;
+ } catch (SAXParseException saxParseException) {
+ // These errors are caught by the error handler.
+ // addValidationMessage(valinfo, saxParseException);
+ } catch (IOException ioException) {
+ addValidationMessage(valinfo, ioException);
+ } catch (Exception exception) {
+ Logger.logException(exception.getLocalizedMessage(), exception);
+ }
+
+ // Now set up the dependencies
+ // Wrap with try catch so that if something wrong happens, validation
+ // can
+ // still proceed as before
+ if (result != null) {
+ try {
+ IResource resource = getWorkspaceFileFromLocation(grammarFile);
+ ArrayList resources = new ArrayList();
+ if (resource != null)
+ resources.add(resource);
+ result.setDependsOn((IResource[]) resources
+ .toArray(new IResource[0]));
+ } catch (Exception e) {
+ Logger.logException(e.getLocalizedMessage(), e);
+ }
+ }*/
+
+ //if (JSONCorePlugin.getDefault().getPluginPreferences()
+ // .getBoolean(JSONCorePreferenceNames.SYNTAX_VALIDATION)) {
+ if (false) {
+ IReporter reporter = executeMarkupValidator(uri);
+ if (reporter != null) {
+ List msgList = reporter.getMessages();
+ for (int i = 0; i < msgList.size(); i++) {
+ LocalizedMessage msg = (LocalizedMessage) msgList.get(i);
+ if (msg.getSeverity() == 2)
+ valinfo.addError(msg.getLocalizedMessage(),
+ msg.getLineNumber(), msg.getOffset(),
+ valinfo.getFileURI(),
+ "null", getMsgArguments(msg)); //$NON-NLS-1$
+ else if (msg.getSeverity() == 1)
+ valinfo.addWarning(msg.getLocalizedMessage(),
+ msg.getLineNumber(), msg.getOffset(),
+ valinfo.getFileURI(),
+ "null", getMsgArguments(msg)); //$NON-NLS-1$
+ }
+ }
+ }
+
+ return valinfo;
+
+ }
+
+ private Object[] getMsgArguments(LocalizedMessage msg){
+ Object obj = msg.getAttribute(AnnotationMsg.ID);
+ return new Object[]{obj};
+ }
+
+
+ private IReporter executeMarkupValidator(String uri) {
+ Path path = new Path(uri);
+ String fileProtocol = "file://"; //$NON-NLS-1$
+ int index = uri.indexOf(fileProtocol);
+
+ IFile resource = null;
+ if (index == 0) {
+ String transformedUri = uri.substring(fileProtocol.length());
+ Path transformedPath = new Path(transformedUri);
+ resource = ResourcesPlugin.getWorkspace().getRoot()
+ .getFileForLocation(transformedPath);
+ } else {
+ resource = ResourcesPlugin.getWorkspace().getRoot().getFile(path);
+
+ }
+ IReporter reporter = null;
+ if (resource != null) {
+ reporter = val.validate(resource, 0, new ValOperation().getState());
+ }
+ return reporter;
+ }
+
+ protected IResource getWorkspaceFileFromLocation(String location) {
+ if (location == null)
+ return null;
+ IWorkspace workspace = ResourcesPlugin.getWorkspace();
+ // To canonicalize the EMF URI
+ IPath canonicalForm = new Path(location);
+ // Need to convert to absolute location...
+ IPath pathLocation = new Path(URIHelper.removeProtocol(canonicalForm
+ .toString()));
+ // ...to find the resource file that is in the workspace
+ IResource resourceFile = workspace.getRoot().getFileForLocation(
+ pathLocation);
+ // If the resource is resolved to a file from http, or a file outside
+ // the workspace, then we will just ignore it.
+ return resourceFile;
+ }
+
+ /**
+ * Set the URI Resolver to use.
+ *
+ * @param uriResolver
+ * The URI Resolver to use.
+ */
+ public void setURIResolver(URIResolver uriResolver) {
+ this.uriResolver = uriResolver;
+ // entityResolver = new MyEntityResolver(uriResolver);
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/ProblemIDsJSON.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/ProblemIDsJSON.java new file mode 100644 index 0000000000..14d7a85d32 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/ProblemIDsJSON.java @@ -0,0 +1,18 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.internal.validation;
+
+public interface ProblemIDsJSON {
+
+ int MissingStartBracket = 1;
+ int MissingEndBracket = 2;
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/AbstractNestedValidator.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/AbstractNestedValidator.java new file mode 100644 index 0000000000..843ba3b66b --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/AbstractNestedValidator.java @@ -0,0 +1,544 @@ +/*******************************************************************************
+ * Copyright (c) 2006, 2016 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
+ * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.core.AbstractNestedValidator
+ * modified in order to process JSON Objects.
+ *******************************************************************************/
+package org.eclipse.wst.json.core.internal.validation.core;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.wst.json.core.internal.Logger;
+import org.eclipse.wst.json.core.validation.AnnotationMsg;
+import org.eclipse.wst.validation.AbstractValidator;
+import org.eclipse.wst.validation.ValidationResult;
+import org.eclipse.wst.validation.ValidationState;
+import org.eclipse.wst.validation.internal.core.Message;
+import org.eclipse.wst.validation.internal.core.ValidationException;
+import org.eclipse.wst.validation.internal.provisional.core.IMessage;
+import org.eclipse.wst.validation.internal.provisional.core.IReporter;
+import org.eclipse.wst.validation.internal.provisional.core.IValidationContext;
+import org.eclipse.wst.validation.internal.provisional.core.IValidator;
+import org.eclipse.wst.validation.internal.provisional.core.IValidatorJob;
+
+/**
+ * An abstract validator that assists validators in running and contributing
+ * nested messages in the validation results. *note: Subclasses do not need to
+ * contribute nested messages in order to benefit from the use of this class.
+ * This class takes care of iterating through results for validators that use
+ * the standard context.
+ */
+public abstract class AbstractNestedValidator extends AbstractValidator
+ implements IValidatorJob {
+
+ private final String GET_FILE = "getFile"; //$NON-NLS-1$
+ private final String GET_PROJECT_FILES = "getAllFiles"; //$NON-NLS-1$
+ private final String GET_INPUTSTREAM = "inputStream"; //$NON-NLS-1$
+
+ private static final String FILE_PROTOCOL_NO_SLASH = "file:"; //$NON-NLS-1$
+ private static final String FILE_PROTOCOL = "file:///"; //$NON-NLS-1$
+
+ // Locally used, non-UI strings.
+ private static final String REFERENCED_FILE_ERROR_OPEN = "referencedFileError("; //$NON-NLS-1$
+ private static final String REFERENCED_FILE_ERROR_CLOSE = ")"; //$NON-NLS-1$
+
+ // Internal strings. These strings are common addition information types.
+ protected static final String COLUMN_NUMBER_ATTRIBUTE = "columnNumber"; //$NON-NLS-1$
+
+ @Override
+ public void cleanup(IReporter reporter) {
+ // No cleanup to perform. Subclasses are free to implement this method.
+ }
+
+ /**
+ * Perform the validation using version 2 of the validation framework.
+ */
+ @Override
+ public ValidationResult validate(IResource resource, int kind,
+ ValidationState state, IProgressMonitor monitor) {
+ ValidationResult result = new ValidationResult();
+ IFile file = null;
+ if (resource instanceof IFile)
+ file = (IFile) resource;
+ if (file != null && shouldValidate(file)) {
+ IReporter reporter = result.getReporter(monitor);
+
+ NestedValidatorContext nestedcontext = getNestedContext(state,
+ false);
+ boolean teardownRequired = false;
+ if (nestedcontext == null) {
+ // validationstart was not called, so manually setup and tear
+ // down
+ nestedcontext = getNestedContext(state, true);
+ nestedcontext.setProject(file.getProject());
+ setupValidation(nestedcontext);
+ teardownRequired = true;
+ } else {
+ nestedcontext.setProject(file.getProject());
+ }
+ InputStream inputStream = null;
+ try {
+ inputStream = file.getContents();
+ validate(file, inputStream, result, reporter, nestedcontext);
+ List messages = reporter.getMessages();
+ for (Object object : messages) {
+ if (object instanceof IMessage) {
+ IMessage message = (IMessage) object;
+ message.setLineNo(message.getLineNumber() + 1);
+ }
+ }
+ } catch (CoreException e) {
+ Logger.logException(e);
+ } finally {
+ if (inputStream != null) {
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ if (teardownRequired)
+ teardownValidation(nestedcontext);
+ }
+ return result;
+ }
+
+ @Override
+ public void validate(IValidationContext context, IReporter reporter)
+ throws ValidationException {
+ validateInJob(context, reporter);
+ }
+
+ @Override
+ public IStatus validateInJob(IValidationContext context, IReporter reporter)
+ throws ValidationException {
+ NestedValidatorContext nestedcontext = new NestedValidatorContext();
+ setupValidation(nestedcontext);
+ String[] fileURIs = context.getURIs();
+ if (fileURIs != null && fileURIs.length > 0) {
+ int numFiles = fileURIs.length;
+ for (int i = 0; i < numFiles && !reporter.isCancelled(); i++) {
+ String fileName = fileURIs[i];
+ if (fileName != null) {
+ Object[] parms = { fileName };
+
+ IFile file = (IFile) context.loadModel(GET_FILE, parms);
+ if (file != null && shouldValidate(file)) {
+ nestedcontext.setProject(file.getProject());
+ // The helper may not have a file stored in it but may
+ // have an InputStream if being
+ // called from a source other than the validation
+ // framework such as an editor.
+ if (context.loadModel(GET_INPUTSTREAM) instanceof InputStream) {
+ validate(file,
+ (InputStream) context
+ .loadModel(GET_INPUTSTREAM), null,
+ reporter, nestedcontext); // do we need the
+ // fileName?
+ // what is int
+ // ruleGroup?
+ } else {
+ validate(file, null, null, reporter, nestedcontext);
+ }
+ }
+ }
+ }
+ }
+ // TODO: Is this needed? Shouldn't the framework pass the complete list?
+ // Should I know that I'm validating a project as opposed to files?
+ else {
+ Object[] parms = { getValidatorID() };
+ Collection files = (Collection) context.loadModel(
+ GET_PROJECT_FILES, parms);
+ // files can be null if they're outside of the workspace
+ if (files != null) {
+ Iterator iter = files.iterator();
+ while (iter.hasNext() && !reporter.isCancelled()) {
+ IFile file = (IFile) iter.next();
+ if (shouldValidate(file)) {
+ validate(file, null, null, reporter, nestedcontext);
+ }
+ }
+ }
+ }
+
+ teardownValidation(nestedcontext);
+ if (reporter.isCancelled())
+ return Status.CANCEL_STATUS;
+ return Status.OK_STATUS;
+
+ }
+
+ /**
+ * Perform set up before validation runs. Subclasses may implement this
+ * method to perform validation specific set up.
+ *
+ * @param context
+ * The context of the current validation.
+ */
+ protected void setupValidation(NestedValidatorContext context) {
+ // Default implementation does nothing.
+ }
+
+ /**
+ * Perform tear down after validation runs. Subclasses may implement this
+ * method to perform validation specific tear down.
+ *
+ * @param context
+ * The context of the current validation.
+ */
+ protected void teardownValidation(NestedValidatorContext context) {
+ // Default implementation does nothing.
+ }
+
+ @Override
+ public ISchedulingRule getSchedulingRule(IValidationContext arg0) {
+ return null;
+ }
+
+ /**
+ * Determine if a given file should be validated.
+ *
+ * @param file
+ * The file that may be validated.
+ * @return True if the file should be validated, false otherwise.
+ */
+ private static boolean shouldValidate(IFile file) {
+ IResource resource = file;
+ do {
+ if (resource.isDerived() || resource.isTeamPrivateMember()
+ || !resource.isAccessible()
+ /* || resource.getName().charAt(0) == '.' */) {
+ return false;
+ }
+ resource = resource.getParent();
+ } while ((resource.getType() & IResource.PROJECT) == 0);
+
+ return true;
+ }
+
+ /**
+ * Validate the given file and use the reporter for the validation messages.
+ * This method does not perform the validation logic but rather delegates to
+ * the validate method in subclasses to validate. This method is responsible
+ * for reporting the messages that result from validation.
+ *
+ * @param file
+ * An IFile to validate.
+ * @param inputstream
+ * An InputStream that represents the file. The InputStream may
+ * be null in which case the files should be validated from the
+ * IFile.
+ * @param result
+ * - The validation result
+ * @param reporter
+ * The reporter with which to report validation messages.
+ * @param context
+ * The context of the current validation.
+ */
+ private void validate(IFile file, InputStream inputstream,
+ ValidationResult result, IReporter reporter,
+ NestedValidatorContext context) {
+ Message message = new LocalizedMessage(IMessage.LOW_SEVERITY, file
+ .getFullPath().toString());
+ reporter.displaySubtask(this, message);
+
+ String locationString = null;
+ if (file.getLocation() != null) {
+ locationString = file.getLocation().toString();
+ }
+ if (locationString == null && file.getLocationURI() != null) {
+ locationString = file.getLocationURI().toString();
+ }
+ if (locationString == null) {
+ locationString = file.getFullPath().toString();
+ }
+ String uri = createURIForFilePath(locationString);
+
+ clearMarkers(file, this, reporter);
+
+ ValidationReport valreport = null;
+ if (result == null)
+ valreport = validate(uri, inputstream, context);
+ else
+ valreport = validate(uri, inputstream, context, result);
+
+ createMarkers(file, valreport.getValidationMessages(), reporter);
+
+ try {
+ file.setSessionProperty(
+ ValidationMessage.ERROR_MESSAGE_MAP_QUALIFIED_NAME,
+ valreport.getNestedMessages());
+ } catch (CoreException e) {
+ System.out.println("Unable to set nested messages property."); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Validate the given file and use the reporter for the validation messages.
+ * Clients must implement this method with their specific validation logic.
+ *
+ * @param uri
+ * The URI of the file to validate.
+ * @param inputstream
+ * An InputStream that represents the file. The InputStream may
+ * be null in which case the files should be validated from the
+ * IFile.
+ * @param context
+ * The context of the current validation.
+ * @return A validation report summarizing the validation.
+ */
+ public abstract ValidationReport validate(String uri,
+ InputStream inputstream, NestedValidatorContext context);
+
+ /**
+ * Validate the given file and use the reporter for the validation messages.
+ * Clients should override this method with their specific validation logic.
+ * This method should now be used instead of the abstract version. Design
+ * decision to not make this abstract.
+ *
+ * @param uri
+ * @param inputstream
+ * @param context
+ * @param result
+ * @return
+ */
+ public ValidationReport validate(String uri, InputStream inputstream,
+ NestedValidatorContext context, ValidationResult result) {
+ return validate(uri, inputstream, context);
+ }
+
+ public ValidationReport validateWithSetup(String uri,
+ InputStream inputstream, NestedValidatorContext context) {
+ setupValidation(context);
+ return validate(uri, inputstream, context);
+ }
+
+ /**
+ * This method clears all the markers on the given IFile for a specified
+ * validator. This is a convenience method for subclasses.
+ *
+ * @param iFile
+ * The IFile from which to clear the markers.
+ * @param validator
+ * The validator for which to remove the markers.
+ * @param reporter
+ * The reporter that can remove the markers.
+ */
+ private void clearMarkers(IFile iFile, IValidator validator,
+ IReporter reporter) {
+ if (fileIsAccessible(iFile)) {
+ reporter.removeAllMessages(validator, iFile);
+ }
+ }
+
+ /**
+ * Provides the id of this validator. The ID is used by the validation
+ * framework. It usually is the fully qualified class name of the class
+ * implementing the IValidator interface.
+ *
+ * @return a String with the ID of this validator.
+ */
+ protected String getValidatorID() {
+ return this.getClass().getName();
+ }
+
+ /**
+ * Test whether the given file is accessible and may be used for validation.
+ * A file is available if 1. It is not null. 2. It exists. 3. The project
+ * containing the file is accessible.
+ *
+ * @param file
+ * The file to check to ensure it is accessible.
+ * @return True if the file is accessible, false otherwise.
+ */
+ private boolean fileIsAccessible(IFile file) {
+ if (file != null && file.exists() && file.getProject().isAccessible()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Format a file name into a correct URI. This is a convenience method for
+ * subclasses.
+ *
+ * @param filename
+ * The file name to format.
+ * @return
+ *
+ * The formatted URI.
+ */
+ private String createURIForFilePath(String filename) {
+ if (!filename.startsWith(FILE_PROTOCOL_NO_SLASH)) {
+ while (filename.startsWith("/")) //$NON-NLS-1$
+ {
+ filename = filename.substring(1);
+ }
+ filename = FILE_PROTOCOL + filename;
+ }
+ return filename;
+ }
+
+ /**
+ * Create markers for the valiation messages generated from the validation.
+ *
+ * @param iFile
+ * The resource to create the markers on.
+ * @param valmessages
+ * The array of validation messages.
+ */
+ public void createMarkers(IFile iFile, ValidationMessage[] valmessages,
+ IReporter reporter) {
+ if (!fileIsAccessible(iFile)) {
+ return;
+ }
+ int nummessages = valmessages.length;
+ for (int i = 0; i < nummessages; i++) {
+ ValidationMessage validationMessage = valmessages[i];
+ String uri = validationMessage.getUri();
+
+ LocalizedMessage message;
+ if (validationMessage.getSeverity() == ValidationMessage.SEV_LOW) {
+ message = new LocalizedMessage(IMessage.NORMAL_SEVERITY,
+ validationMessage.getMessage(), iFile);
+ } else {
+ message = new LocalizedMessage(IMessage.HIGH_SEVERITY,
+ validationMessage.getMessage(), iFile);
+ }
+
+ message.setLineNo(validationMessage.getLineNumber());
+ addInfoToMessage(validationMessage, message);
+ Object[] objlist = validationMessage.getMessageArguments();
+ if (objlist != null && objlist.length == 1) {
+ Object obj = objlist[0];
+ if (obj instanceof AnnotationMsg) {
+ message.setAttribute(AnnotationMsg.PROBMLEM_ID,
+ new Integer(((AnnotationMsg) obj).getProblemId()));
+ message.setAttribute(AnnotationMsg.LENGTH, new Integer(
+ ((AnnotationMsg) obj).getLength()));
+ Object obj1 = ((AnnotationMsg) obj).getAttributeValueText();
+ if (obj1 instanceof String) {
+ message.setAttribute(AnnotationMsg.ATTRVALUETEXT, obj1);
+ } else if (obj1 instanceof Object[]) {
+ Object[] objArray = (Object[]) obj1;
+ message.setAttribute(AnnotationMsg.ATTRVALUENO,
+ new Integer(objArray.length));
+ for (int j = 0; j < objArray.length; j++) {
+ Object obj2 = objArray[j];
+ String attrName = AnnotationMsg.ATTRNO + j;
+ message.setAttribute(attrName, obj2);
+ }
+
+ }
+ }
+ }
+ List nestederrors = validationMessage.getNestedMessages();
+ if (nestederrors != null && !nestederrors.isEmpty()) {
+ message.setGroupName(REFERENCED_FILE_ERROR_OPEN + uri
+ + REFERENCED_FILE_ERROR_CLOSE);
+ }
+
+ reporter.addMessage(this, message);
+
+ }
+ }
+
+ /**
+ * This method allows the addition of information to the validation message
+ *
+ * @param validationmessage
+ * The ValidationMessage to retrieve the information from.
+ * @param message
+ * The IMessage to add the information to.
+ */
+ protected void addInfoToMessage(ValidationMessage validationmessage,
+ IMessage message) {
+ // This method may be overridden by subclasses
+ }
+
+ /**
+ * Get the nested validation context.
+ *
+ * @param state
+ * the validation state.
+ * @param create
+ * when true, a new context will be created if one is not found
+ * @return the nested validation context.
+ */
+ protected NestedValidatorContext getNestedContext(ValidationState state,
+ boolean create) {
+ NestedValidatorContext context = null;
+ if (create) {
+ context = new NestedValidatorContext();
+ }
+ return context;
+ }
+
+ /**
+ * A localized message is a specialized type of IMessage that allows setting
+ * and using a localized message string for a message.
+ */
+ class LocalizedMessage extends Message {
+ private String _message = null;
+
+ public LocalizedMessage(int severity, String messageText) {
+ this(severity, messageText, null);
+ }
+
+ public LocalizedMessage(int severity, String messageText,
+ IResource targetObject) {
+ this(severity, messageText, (Object) targetObject);
+ }
+
+ public LocalizedMessage(int severity, String messageText,
+ Object targetObject) {
+ super(null, severity, null);
+ setLocalizedMessage(messageText);
+ setTargetObject(targetObject);
+ }
+
+ public void setLocalizedMessage(String message) {
+ _message = message;
+ }
+
+ public String getLocalizedMessage() {
+ return _message;
+ }
+
+ public String getText() {
+ return getLocalizedMessage();
+ }
+
+ public String getText(ClassLoader cl) {
+ return getLocalizedMessage();
+ }
+
+ public String getText(Locale l) {
+ return getLocalizedMessage();
+ }
+
+ public String getText(Locale l, ClassLoader cl) {
+ return getLocalizedMessage();
+ }
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/NestedValidatorContext.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/NestedValidatorContext.java new file mode 100644 index 0000000000..5df2b266dd --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/NestedValidatorContext.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2006, 2011 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.core.NestedValidatorContext + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.validation.core; + +import org.eclipse.core.resources.IProject; + + +/** + * A context class for validators to be able to determine the context of + * given validation session. Currently this class is only used to identify + * the unique context. + */ +public class NestedValidatorContext +{ + private IProject fProject; + + public void setProject(IProject project) { + fProject = project; + } + + public IProject getProject() { + return fProject; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationInfo.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationInfo.java new file mode 100644 index 0000000000..62d29964d9 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationInfo.java @@ -0,0 +1,256 @@ +/******************************************************************************* + * Copyright (c) 2001, 2016 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 + * David Carver (STAR) - bug 297005 - Some static constants not made final. + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.core.ValidationInfo + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.validation.core; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.eclipse.osgi.util.NLS; + +/** + * This class handles messages from a validator. This class can handle + * + * @author Lawrence Mandel, IBM + */ +public class ValidationInfo implements ValidationReport +{ + private boolean WRAPPER_ERROR_SUPPORT_ENABLED = true; + public static final int SEV_ERROR = 0; + public static final int SEV_WARNING = 1; + + private String validating_file_uri = null; + private URL validating_file_url = null; + private boolean valid = true; + private List messages = new ArrayList(); + private HashMap nestedMessages = new HashMap(); + + /** + * Constructor. + * + * @param uri + * The URI of the file for the validation. + */ + public ValidationInfo(String uri) + { + if(uri != null) + { + this.validating_file_uri = uri; + try + { + this.validating_file_url = new URL(uri); + } catch (MalformedURLException e) + { + } + } + } + + public String getFileURI() + { + return validating_file_uri; + } + + public boolean isValid() + { + return valid; + } + + /** + * Add an error message. + * + * @param message The message to add. + * @param line The line location of the message. + * @param column The column location of the message. + * @param uri The URI of the file that contains the message. + */ + public void addError(String message, int line, int column, String uri) + { + addError(message, line, column, uri, null, null); + } + + /** + * + * Add an error message. + * + * @param message The message to add. + * @param line The line location of the message. + * @param column The column location of the message. + * @param uri The URI of the file that contains the message. + * @param key The key for the message. + * @param messageArguments more information about the error + */ + public void addError(String message, int line, int column, String uri, String key, Object[] messageArguments) + { + if(addMessage(message, line, column, uri, SEV_ERROR, key, messageArguments)) + { + valid = false; + } + } + + /** + * Add a warning message. + * + * @param message The string message of the warning. + * @param line The line location of the warning. + * @param column The column location of the warning. + * @param uri The URI of the file that contains the warning. + */ + public void addWarning(String message, int line, int column, String uri) + { + addWarning(message, line, column, uri, null, null); + } + + /** + * + * Add an error message. + * + * @param message The message to add. + * @param line The line location of the message. + * @param column The column location of the message. + * @param uri The URI of the file that contains the message. + * @param key The key for the message. + * @param messageArguments more information about the error + */ + public void addWarning(String message, int line, int column, String uri, String key, Object[] messageArguments) + { + addMessage(message, line, column, uri, SEV_WARNING, key, messageArguments); + } + + /** + * Add a message to the list. Return true if successful, false otherwise. + * + * @param message The message to add to the list. + * @param line The line location of the message. + * @param column The column location of the message. + * @param uri The URI of the file that contains the message. + * @param severity The severity of the message. + * @param key the Xerces error key for this error + * @param messageArguments more information on the error + * @return True if the message was successfully added, false otherwise. + */ + private boolean addMessage(String message, int line, int column, String uri, int severity, String key, Object[] messageArguments) + { + boolean successfullyAdded = false; + // If the message if null there is nothing to add. + if(message == null) + { + return successfullyAdded; + } + String errorURI = normalize(uri); + URL errorURL = null; + if (errorURI != null) + { + try + { + errorURL = new URL(errorURI); + } catch (MalformedURLException e) + { + } + //errorURI = normalizeURI(errorURI); + } + //boolean doDialog = true; + if (errorURL != null) + { + successfullyAdded = true; + // Add to the appropriate list if nested error support is off or + // this message is for the current file. + if (!WRAPPER_ERROR_SUPPORT_ENABLED || (validating_file_url != null && validating_file_url.sameFile(errorURL))) + { + + ValidationMessage valmes = new ValidationMessage(message, line, + column, validating_file_uri, key, messageArguments); + if (severity == SEV_ERROR) + { + valmes.setSeverity(ValidationMessage.SEV_NORMAL); + } else if (severity == SEV_WARNING) + { + valmes.setSeverity(ValidationMessage.SEV_LOW); + } + messages.add(valmes); + } + // If nested error support is enabled create a nested error. + else if (WRAPPER_ERROR_SUPPORT_ENABLED) + { + String nesteduri = errorURL.toExternalForm(); + ValidationMessage nestedmess = new ValidationMessage(message, line, + column, nesteduri, key, messageArguments); + if(severity == SEV_WARNING) + { + nestedmess.setSeverity(ValidationMessage.SEV_LOW); + } + else + { + nestedmess.setSeverity(ValidationMessage.SEV_NORMAL); + } + + ValidationMessage container = (ValidationMessage) nestedMessages.get(nesteduri); + if(container == null) + { + /*container = new ValidationMessage(NLS.bind(XMLValidationMessages._UI_REF_FILE_ERROR_MESSAGE, new Object [] { nesteduri }), 1, 0, nesteduri); + + // Initially set the nested error to a warning. This will automatically be changed + // to an error if a nested message has a severity of error. + container.setSeverity(ValidationMessage.SEV_LOW); + nestedMessages.put(nesteduri, container); + messages.add(container);*/ + } + if (container != null) { // TODO: Bug #505666: Need to decide if the if-block above is to be dropped or re-developed + container.addNestedMessage(nestedmess); + } + } + } + return successfullyAdded; + } + + /** + * @see org.eclipse.wsdl.validate.ValidationReport#getValidationMessages() + */ + public ValidationMessage[] getValidationMessages() + { + return (ValidationMessage[])messages.toArray(new ValidationMessage[messages.size()]); + } + + public HashMap getNestedMessages() + { + return nestedMessages; + } + + /** + * Put the URI in a standard format. + * + * @param uri The URI to put into a standard format. + * @return The standard format of the URI. + */ + private String normalize(String uri) + { +// if(uri.startsWith("platform:")) +// { +// try +// { +// uri = Platform.resolve(new URL(uri)).toString(); +// } +// catch(Exception e) +// { +// } +// } + uri = uri.replaceAll("%20"," "); //$NON-NLS-1$ //$NON-NLS-2$ + uri = uri.replaceAll("%5E", "^"); //$NON-NLS-1$ //$NON-NLS-2$ + uri = uri.replace('\\','/'); + + return uri; + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationMessage.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationMessage.java new file mode 100644 index 0000000000..450d70b7dc --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationMessage.java @@ -0,0 +1,203 @@ +/******************************************************************************* + * Copyright (c) 2001, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * David Carver (STAR) - bug 297005 - Some static constants not made final. + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.core.ValidationMessage + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.validation.core; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.QualifiedName; +import org.eclipse.wst.validation.internal.provisional.core.IMessage; + +/** + * A class for holding validation message information. Holds the message and the + * message location. + */ +public class ValidationMessage +{ + public static final QualifiedName ERROR_MESSAGE_MAP_QUALIFIED_NAME = new QualifiedName("org.eclipse.wst.json.validation", "errorMessageMap"); //$NON-NLS-1$ //$NON-NLS-2$ + protected String message; + protected int lineNumber; + protected int columnNumber; + protected String uri; + protected List nestedErrors; + protected String key; + protected Object[] messageArguments; + protected int startOffset; + protected int severity = IMessage.NORMAL_SEVERITY; + public static final int SEV_HIGH = IMessage.HIGH_SEVERITY; + public static final int SEV_NORMAL = IMessage.NORMAL_SEVERITY; + public static final int SEV_LOW = IMessage.LOW_SEVERITY; + + /** + * Constructor. + * + * @param message The message for the validation message. + * @param lineNumber The line location of the message. + * @param columnNumber The column location of the message. + */ + public ValidationMessage(String message, int lineNumber, int columnNumber) + { + this(message, lineNumber, columnNumber, null); + } + + /** + * Constructor. + * + * @param message The message for the validation message. + * @param lineNumber The line location of the message. + * @param columnNumber The column location of the message. + * @param uri The uri of the file the message is for. + */ + public ValidationMessage(String message, int lineNumber, int columnNumber, String uri) + { + this(message, lineNumber, columnNumber, uri, null, null); + } + + /** + * Constructor. + * + * @param message The message for the validation message. + * @param lineNumber The line location of the message. + * @param columnNumber The column location of the message. + * @param uri The uri of the file the message is for. + * @param key a unique string representing the error + * @param messageArguments the arguments Xerces uses to create the message + */ + public ValidationMessage(String message, int lineNumber, int columnNumber, String uri, String key, Object[] messageArguments) + { + this.message = message; + this.lineNumber = lineNumber; + this.columnNumber = columnNumber; + this.uri = uri; + this.key = key; + this.messageArguments = messageArguments; + this.startOffset = 0; + } + + + /** + * Get the message for this valiation message. + * + * @return The message for this validation message. + */ + public String getMessage() + { + return message; + } + + /** + * Get the column location. + * + * @return The column number. + */ + public int getColumnNumber() + { + return columnNumber; + } + + /** + * Get the line location. + * + * @return The line number. + */ + public int getLineNumber() + { + return lineNumber; + } + + /** + * Get the uri for the file that contains the message. + * + * @return The uri of the file that contains the message. + */ + public String getUri() + { + return uri; + } + + /** + * Add a nested validation message to this validation message. + * + * @param validationMessage The validation message to add as a nested message. + */ + public void addNestedMessage(ValidationMessage validationMessage) + { + if (nestedErrors == null) + { + nestedErrors = new ArrayList(); + } + nestedErrors.add(validationMessage); + int validaitonmessageSeverity = validationMessage.getSeverity(); + if(validaitonmessageSeverity == SEV_NORMAL || validaitonmessageSeverity == SEV_HIGH) + { + setSeverity(SEV_NORMAL); + } + } + + /** + * Get the list of nested validation messages. + * + * @return The list of nested validation messages. + */ + public List getNestedMessages() + { + return nestedErrors != null ? nestedErrors : Collections.EMPTY_LIST; + } + + /** + * Get the severity of the defect. + * + * @return The severity of the defect. + */ + public int getSeverity() + { + return severity; + } + + /** + * Set the severity of the message. + * + * @param sev The severity to set. + */ + public void setSeverity(int sev) + { + severity = sev; + } + + public void setStartOffset(int offset) + { + this.startOffset = offset; + } + + /** + * @return Returns the key. + */ + public String getKey() + { + return key; + } + + public Object[] getMessageArguments() + { + return this.messageArguments; + } + + + public void setMessage(String message) + { + this.message = message; + } + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationReport.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationReport.java new file mode 100644 index 0000000000..b5c4e15793 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationReport.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2001, 2005 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.core.ValidationReport + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.validation.core; + +import java.util.HashMap; + +/** + * An interface for a validation report. + * + * @author Lawrence Mandel, IBM + */ +public interface ValidationReport +{ + /** + * Returns the URI for the file the report refers to. + * + * @return The URI for the file the report refers to. + */ + public String getFileURI(); + + /** + * Returns whether the file is valid. The file may have warnings associated with it. + * + * @return True if the file is valid, false otherwise. + */ + public boolean isValid(); + + /** + * Returns an array of validation messages. + * + * @return An array of validation messages. + */ + public ValidationMessage[] getValidationMessages(); + + public HashMap getNestedMessages(); + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/JSONMessageInfoHelper.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/JSONMessageInfoHelper.java new file mode 100644 index 0000000000..03bf69a89c --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/JSONMessageInfoHelper.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2005, 2011 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.eclipse.XMLMessageInfoHelper + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.internal.validation.eclipse; + +/** + * The message info helper determines the selection strategy for elements + * in a SSE editor. + * + */ +public class JSONMessageInfoHelper +{ + public JSONMessageInfoHelper() + { + super(); + } + + /** + * Returns an array containing information about what should be underlined with the red "squiggles" + * using the errorKey, and the messageArguments. + * <br>Position 0 of the array returned contains the selection Strategy, or what DOM Element to underline. + * For example "ATTRIBUTE_NAME" + * <br>Position 1 contains the name or value to squiggle. + * <p>For example, if we wanted to squiggle the attribute name of an attribute name + * foo this method would return {"ATTRIBUTE_NAME", "foo"} + * </p> + * @param errorKey + * The error key given by the Xerces parser. + * @param messageArguments + * The arguments used by Xerces to "fill in the blanks" of their messages. + * @return + * An array containing the squiggle information. + * @see org.eclipse.wst.xml.ui.internal.validation.DelegatingSourceValidator + * + */ + public String[] createMessageInfo(String errorKey, Object[] messageArguments) + { + String selectionStrategy = null; + String nameOrValue = null; + + if(errorKey != null) + { + if (errorKey.equals("cvc-complex-type.2.4.a") || errorKey.equals("cvc-complex-type.2.4.d") || errorKey.equals("cvc-complex-type.2.4.b") || errorKey.equals("MSG_CONTENT_INVALID") //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + | errorKey.equals("MSG_CONTENT_INCOMPLETE") || errorKey.equals("MSG_REQUIRED_ATTRIBUTE_NOT_SPECIFIED") || errorKey.equals("cvc-complex-type.4") || errorKey.equals("ElementPrefixUnbound") ) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + { + selectionStrategy = "START_TAG"; //$NON-NLS-1$ + } + else if (errorKey.equals("cvc-type.3.1.3")) //$NON-NLS-1$ + { + selectionStrategy = "TEXT"; //$NON-NLS-1$ + } + else if (errorKey.equals("cvc-complex-type.2.3")) //$NON-NLS-1$ + { + selectionStrategy = "FIRST_NON_WHITESPACE_TEXT"; //$NON-NLS-1$ + } + else if (errorKey.equals("cvc-type.3.1.1")) //$NON-NLS-1$ + { + selectionStrategy = "ALL_ATTRIBUTES"; //$NON-NLS-1$ + } + else if (errorKey.equals("cvc-complex-type.3.2.2") || errorKey.equals("MSG_ATTRIBUTE_NOT_DECLARED")) //$NON-NLS-1$ //$NON-NLS-2$ + { + selectionStrategy = "ATTRIBUTE_NAME"; //$NON-NLS-1$ + //in this case we need nameOrValue to be the name of the attribute to underline + nameOrValue = (String)messageArguments[1]; + } + else if (errorKey.equals("cvc-attribute.3") || errorKey.equals("MSG_ATTRIBUTE_VALUE_NOT_IN_LIST") || errorKey.equals("cvc-complex-type.3.1")) //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + { + selectionStrategy = "ATTRIBUTE_VALUE"; //$NON-NLS-1$ + //in this case we need nameOrValue to be the name of the Attribute + if (errorKey.equals("cvc-attribute.3") || errorKey.equals("cvc-complex-type.3.1")) //$NON-NLS-1$ //$NON-NLS-2$ + { + nameOrValue = (String)messageArguments[1]; + } + else if (errorKey.equals("MSG_ATTRIBUTE_VALUE_NOT_IN_LIST")) //$NON-NLS-1$ + { + nameOrValue = (String)messageArguments[0]; + } + } + else if (errorKey.equals("cvc-elt.4.2")) //$NON-NLS-1$ + { + selectionStrategy="VALUE_OF_ATTRIBUTE_WITH_GIVEN_VALUE"; //$NON-NLS-1$ + //in this case we need nameOrValue to be the value of the attribute we want to unerline + nameOrValue = (String)messageArguments[1]; + } + else if (errorKey.equals("EntityNotDeclared")) //$NON-NLS-1$ + { + selectionStrategy="TEXT_ENTITY_REFERENCE"; //$NON-NLS-1$ + nameOrValue = (String)messageArguments[0]; + } + else if (errorKey.equals("ElementUnterminated")) //$NON-NLS-1$ + { + selectionStrategy = "ENTIRE_ELEMENT"; //$NON-NLS-1$ + } + else if (errorKey.equals("ETagUnterminated") || errorKey.equals("ETagRequired")) //$NON-NLS-1$ //$NON-NLS-2$ + { + selectionStrategy = "END_TAG"; //$NON-NLS-1$ + } + else if (errorKey.equals("AttributeNotUnique")) //$NON-NLS-N$ + { + selectionStrategy = "ATTRIBUTE_NAME_LAST"; //$NON-NLS-1$ + //in this case we need nameOrValue to be the name of the last attribute(like in case of duplicate attributes) + //to underline + nameOrValue = (String)messageArguments[1]; + } + } + String messageInfo[] = new String[2]; + messageInfo[0] = selectionStrategy != null? selectionStrategy: ""; //$NON-NLS-1$ + messageInfo[1] = nameOrValue; + return messageInfo; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/JSONValidator.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/JSONValidator.java new file mode 100644 index 0000000000..2bd9a969b0 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/JSONValidator.java @@ -0,0 +1,45 @@ +/*******************************************************************************
+ * Copyright (c) 2001, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.eclipse.XMLValidator
+ * modified in order to process JSON Objects.
+ *******************************************************************************/
+package org.eclipse.wst.json.core.internal.validation.eclipse;
+
+import org.eclipse.wst.common.uriresolver.internal.provisional.URIResolverPlugin;
+
+public class JSONValidator extends
+ org.eclipse.wst.json.core.internal.validation.JSONValidator {
+
+ private static JSONValidator instance = null;
+
+ /**
+ * Return the one and only instance of the JSON validator. The validator can
+ * be reused and cannot be customized so there should only be one instance
+ * of it.
+ *
+ * @return The one and only instance of the JSON validator.
+ */
+ public static JSONValidator getInstance() {
+ if (instance == null) {
+ instance = new JSONValidator();
+ }
+ return instance;
+ }
+
+ /**
+ * Constructor. Create the JSON validator, set the URI resolver and get the
+ * extension error customizers from the registry.
+ */
+ protected JSONValidator() {
+ setURIResolver(URIResolverPlugin.createResolver());
+ // new ErrorCustomizationPluginRegistryReader().readRegistry();
+ }
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/Validator.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/Validator.java new file mode 100644 index 0000000000..f16f034669 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/Validator.java @@ -0,0 +1,946 @@ +/*******************************************************************************
+ * Copyright (c) 2001, 2011 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.eclipse.Validator
+ * modified in order to process JSON Objects.
+ *******************************************************************************/
+package org.eclipse.wst.json.core.internal.validation.eclipse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.ProjectScope;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.preferences.DefaultScope;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+import org.eclipse.core.runtime.preferences.IScopeContext;
+import org.eclipse.core.runtime.preferences.InstanceScope;
+import org.eclipse.json.impl.schema.JSONSchemaNode;
+import org.eclipse.json.provisonnal.com.eclipsesource.json.JsonArray;
+import org.eclipse.json.provisonnal.com.eclipsesource.json.JsonObject;
+import org.eclipse.json.provisonnal.com.eclipsesource.json.JsonObject.Member;
+import org.eclipse.json.provisonnal.com.eclipsesource.json.JsonValue;
+import org.eclipse.json.schema.IJSONSchemaDocument;
+import org.eclipse.json.schema.IJSONSchemaNode;
+import org.eclipse.json.schema.IJSONSchemaProperty;
+import org.eclipse.json.schema.JSONSchemaType;
+import org.eclipse.wst.json.core.JSONCorePlugin;
+import org.eclipse.wst.json.core.document.IJSONArray;
+import org.eclipse.wst.json.core.document.IJSONDocument;
+import org.eclipse.wst.json.core.document.IJSONModel;
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONObject;
+import org.eclipse.wst.json.core.document.IJSONPair;
+import org.eclipse.wst.json.core.document.IJSONStringValue;
+import org.eclipse.wst.json.core.document.IJSONValue;
+import org.eclipse.wst.json.core.internal.schema.SchemaProcessorRegistryReader;
+import org.eclipse.wst.json.core.internal.validation.JSONNestedValidatorContext;
+import org.eclipse.wst.json.core.internal.validation.JSONValidationConfiguration;
+import org.eclipse.wst.json.core.internal.validation.JSONValidationInfo;
+import org.eclipse.wst.json.core.internal.validation.JSONValidationReport;
+import org.eclipse.wst.json.core.internal.validation.core.AbstractNestedValidator;
+import org.eclipse.wst.json.core.internal.validation.core.NestedValidatorContext;
+import org.eclipse.wst.json.core.internal.validation.core.ValidationMessage;
+import org.eclipse.wst.json.core.internal.validation.core.ValidationReport;
+import org.eclipse.wst.json.core.preferences.JSONCorePreferenceNames;
+import org.eclipse.wst.json.core.util.JSONUtil;
+import org.eclipse.wst.sse.core.StructuredModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
+import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
+import org.eclipse.wst.validation.ValidationResult;
+import org.eclipse.wst.validation.ValidationState;
+import org.eclipse.wst.validation.internal.provisional.core.IMessage;
+
+public class Validator extends AbstractNestedValidator {
+
+ private static final String CLOSE_BRACKET = "]"; //$NON-NLS-1$
+ private static final String OPEN_BRACKET = "["; //$NON-NLS-1$
+ private static final String COMMA = ","; //$NON-NLS-1$
+ private static final String JSON_VALIDATOR_CONTEXT = "org.eclipse.wst.json.core.validatorContext"; //$NON-NLS-1$
+ protected int indicateNoGrammar = 0;
+ private IScopeContext[] fPreferenceScopes = null;
+
+ @Override
+ protected void setupValidation(NestedValidatorContext context) {
+ super.setupValidation(context);
+ fPreferenceScopes = createPreferenceScopes(context);
+ indicateNoGrammar = Platform.getPreferencesService().getInt(
+ JSONCorePlugin.getDefault().getBundle().getSymbolicName(), JSONCorePreferenceNames.INDICATE_NO_GRAMMAR,
+ 0, fPreferenceScopes);
+ }
+
+ protected IScopeContext[] createPreferenceScopes(NestedValidatorContext context) {
+ if (context != null) {
+ final IProject project = context.getProject();
+ if (project != null && project.isAccessible()) {
+ final ProjectScope projectScope = new ProjectScope(project);
+ if (projectScope.getNode(JSONCorePlugin.getDefault().getBundle().getSymbolicName())
+ .getBoolean(JSONCorePreferenceNames.USE_PROJECT_SETTINGS, false))
+ return new IScopeContext[] { projectScope, new InstanceScope(), new DefaultScope() };
+ }
+ }
+ return new IScopeContext[] { new InstanceScope(), new DefaultScope() };
+ }
+
+ @Override
+ public ValidationReport validate(String uri, InputStream inputstream, NestedValidatorContext context) {
+ return validate(uri, inputstream, context, null);
+ }
+
+ @Override
+ public ValidationReport validate(String uri, InputStream inputstream, NestedValidatorContext context,
+ ValidationResult result) {
+ JSONValidator validator = JSONValidator.getInstance();
+ JSONValidationConfiguration configuration = new JSONValidationConfiguration();
+ JSONValidationReport valreport = validator.validate(uri, inputstream, configuration, result, context);
+ String prefs = JSONCorePlugin.getDefault().getBundle().getSymbolicName();
+ IEclipsePreferences modelPreferences = InstanceScope.INSTANCE.getNode(prefs);
+ boolean validateSchema = modelPreferences.getBoolean(JSONCorePreferenceNames.SCHEMA_VALIDATION, false);
+ if (validateSchema) {
+ IJSONModel model = null;
+ try {
+ IStructuredModel temp = getModel(uri);
+ if (!(temp instanceof IJSONModel)) {
+ return valreport;
+ }
+ model = (IJSONModel) temp;
+ IJSONSchemaDocument schemaDocument = SchemaProcessorRegistryReader.getInstance()
+ .getSchemaDocument(model);
+ if (schemaDocument != null) {
+ JSONValidationInfo valinfo = null;
+ if (valreport instanceof JSONValidationInfo) {
+ valinfo = (JSONValidationInfo) valreport;
+ } else {
+ valinfo = new JSONValidationInfo(uri);
+ }
+ validate(model, schemaDocument, valinfo);
+ // ValidationMessage[] messages =
+ // valreport.getValidationMessages();
+ return valreport;
+ }
+ } catch (IOException e) {
+ logWarning(e);
+ return valreport;
+ } finally {
+ if (model != null) {
+ model.releaseFromRead();
+ }
+ }
+ }
+ return valreport;
+ }
+
+ private void validate(IJSONModel model, IJSONSchemaProperty schemaProperty, JSONValidationInfo valinfo) {
+ IJSONDocument document = model.getDocument();
+ IJSONNode node = document.getFirstChild();
+ while (node != null) {
+ validate(node, schemaProperty, valinfo);
+ node = node.getNextSibling();
+ }
+ }
+
+ private void validate(IJSONNode node, IJSONSchemaProperty schemaProperty, JSONValidationInfo valinfo) {
+ if (node == null || schemaProperty == null) {
+ return;
+ }
+ JsonObject schema = schemaProperty.getJsonObject();
+ validate(node, schema, valinfo);
+ IJSONNode child = node.getFirstChild();
+ while (child != null) {
+ IJSONSchemaProperty property = schemaProperty.getSchemaDocument().getProperty(child.getPath());
+ validate(child, property, valinfo);
+ if (child instanceof IJSONPair) {
+ IJSONValue value = ((IJSONPair) child).getValue();
+ if (value instanceof IJSONObject) {
+ IJSONSchemaProperty prop = schemaProperty.getSchemaDocument().getProperty(value.getPath());
+ validate(value, prop, valinfo);
+ }
+ }
+ child = child.getNextSibling();
+ }
+ }
+
+ private void validate(IJSONNode node, JsonObject schema, JSONValidationInfo valinfo) {
+ Iterator<Member> members = schema.iterator();
+ while (members.hasNext()) {
+ Member member = members.next();
+ validate(node, schema, member, valinfo);
+ }
+ }
+
+ private void validate(IJSONNode node, JsonObject schema, Member member, JSONValidationInfo valinfo) {
+ if (IJSONSchemaNode.ALL_OF.equals(member.getName()) && member.getValue() instanceof JsonArray) {
+ JsonArray jsonArray = (JsonArray) member.getValue();
+ Iterator<JsonValue> iter = jsonArray.iterator();
+ while (iter.hasNext()) {
+ JsonValue value = iter.next();
+ if (value instanceof JsonObject) {
+ validate(node, (JsonObject) value, valinfo);
+ }
+ }
+ }
+ if (IJSONSchemaNode.ANY_OF.equals(member.getName()) && member.getValue() instanceof JsonArray) {
+ JsonArray jsonArray = (JsonArray) member.getValue();
+ Iterator<JsonValue> iter = jsonArray.iterator();
+ while (iter.hasNext()) {
+ JsonValue value = iter.next();
+ if (value instanceof JsonObject) {
+ JSONValidationInfo info = new JSONValidationInfo("");
+ validate(node, (JsonObject) value, info);
+ if (info.getValidationMessages() == null || info.getValidationMessages().length == 0) {
+ return;
+ }
+ }
+ }
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Matches a schema that is not allowed", line, 0, offset == 0 ? 1 : offset);
+ }
+ if (IJSONSchemaNode.ONE_OF.equals(member.getName()) && member.getValue() instanceof JsonArray) {
+ JsonArray jsonArray = (JsonArray) member.getValue();
+ Iterator<JsonValue> iter = jsonArray.iterator();
+ int count = 0;
+ while (iter.hasNext()) {
+ JsonValue value = iter.next();
+ if (value instanceof JsonObject) {
+ JSONValidationInfo info = new JSONValidationInfo("");
+ validate(node, (JsonObject) value, info);
+ if (info.getValidationMessages() == null || info.getValidationMessages().length == 0) {
+ count = count + 1;
+ }
+ }
+ }
+ if (count != 1) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Matches a schema that is not allowed", line, 0, offset == 0 ? 1 : offset);
+ }
+ }
+ if (IJSONSchemaNode.NOT.equals(member.getName()) && member.getValue() instanceof JsonObject) {
+ JsonObject json = (JsonObject) member.getValue();
+ JSONValidationInfo info = new JSONValidationInfo("");
+ validate(node, json, info);
+ if (info.getValidationMessages() == null || info.getValidationMessages().length == 0) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Matches a schema that is not allowed", line, 0, offset == 0 ? 1 : offset);
+ }
+ }
+ if (IJSONSchemaNode.TYPE.equals(member.getName())) {
+ validateType(node, member, valinfo);
+ }
+ if (IJSONSchemaNode.ENUM.equals(member.getName())) {
+ validateEnum(node, schema, valinfo);
+ }
+ if (node.getNodeType() == IJSONNode.OBJECT_NODE) {
+ if (IJSONSchemaNode.REQUIRED.equals(member.getName())) {
+ validateRequired(node, schema, valinfo);
+ }
+ if (IJSONSchemaNode.MAX_PROPERTIES.equals(member.getName())) {
+ validateMaxProperties(node, schema, valinfo);
+ }
+ if (IJSONSchemaNode.MIN_PROPERTIES.equals(member.getName())) {
+ validateMinProperties(node, schema, valinfo);
+ }
+ if (IJSONSchemaNode.ADDITIONAL_PROPERTIES.equals(member.getName())) {
+ validateAdditionalProperties(node, schema, member.getValue(), valinfo);
+ }
+ }
+ if (node.getNodeType() == IJSONNode.PAIR_NODE) {
+ IJSONValue value = ((IJSONPair) node).getValue();
+ JSONSchemaType[] types = JSONSchemaNode.getType(schema.get(IJSONSchemaNode.TYPE));
+ if (value != null) {
+ if (value.getNodeType() == IJSONNode.VALUE_STRING_NODE && isType(types, JSONSchemaType.String)) {
+ validateString(node, schema, member, valinfo, value);
+ }
+ if (value.getNodeType() == IJSONNode.VALUE_NUMBER_NODE
+ && (isType(types, JSONSchemaType.Integer) || isType(types, JSONSchemaType.Number))) {
+ validateNumber(node, schema, member, valinfo, value);
+ }
+ if (value.getNodeType() == IJSONNode.ARRAY_NODE && isType(types, JSONSchemaType.Array)) {
+ validateArray(node, schema, member, valinfo, value);
+ }
+ }
+ }
+ }
+
+ private void validateAdditionalProperties(IJSONNode node, JsonObject schema, JsonValue value,
+ JSONValidationInfo valinfo) {
+ if (value != null && value.isBoolean() && !value.asBoolean()) {
+ Set<String> s = getProperties(node);
+ Set<String> p = getProperties(schema.get(IJSONSchemaNode.PROPERTIES));
+ Set<String> pp = getProperties(schema.get(IJSONSchemaNode.PATTERN_PROPERTIES));
+ for (String string : p) {
+ if (s.contains(string)) {
+ s.remove(string);
+ }
+ }
+ for (String patternStr : pp) {
+ Pattern pattern = Pattern.compile(patternStr);
+ Iterator<String> iter = s.iterator();
+ while (iter.hasNext()) {
+ String ss = iter.next();
+ if (ss != null) {
+ Matcher matcher = pattern.matcher(ss);
+ if (matcher.find()) {
+ iter.remove();
+ }
+ }
+ }
+ }
+ for (String string : s) {
+ if ("$schema".equals(string)) { //$NON-NLS-1$
+ continue;
+ }
+ IJSONNode n = node.getFirstChild();
+ while (n != null) {
+ if (n instanceof IJSONPair) {
+ IJSONPair pair = (IJSONPair) n;
+ if (string.equals(pair.getName())) {
+ break;
+ }
+ }
+ n = n.getNextSibling();
+ }
+ if (n == null) {
+ node = n;
+ }
+ int offset = n.getStartOffset();
+ int line = n.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Property " + string + " is not allowed", line, 0, offset == 0 ? 1 : offset);
+ }
+ }
+ }
+
+ private Set<String> getProperties(JsonValue value) {
+ Set<String> result = new HashSet<String>();
+ if (value instanceof JsonObject) {
+ Iterator<Member> members = ((JsonObject) value).iterator();
+ while (members.hasNext()) {
+ result.add(members.next().getName());
+ }
+ }
+ return result;
+ }
+
+ private void validateArray(IJSONNode node, JsonObject schema, Member member, JSONValidationInfo valinfo,
+ IJSONValue value) {
+ if (IJSONSchemaNode.ITEMS.equals(member.getName())) {
+ validateItems(node, schema, value, member.getValue(), valinfo);
+ }
+ if (IJSONSchemaNode.MAX_ITEMS.equals(member.getName())) {
+ validateMaxItems(node, schema, value, member.getValue(), valinfo);
+ }
+ if (IJSONSchemaNode.MIN_ITEMS.equals(member.getName())) {
+ validateMinItems(node, schema, value, member.getValue(), valinfo);
+ }
+ if (IJSONSchemaNode.UNIQUE_ITEMS.equals(member.getName())) {
+ validateUniqueItems(node, schema, value, member.getValue(), valinfo);
+ }
+ }
+
+ private void validateMaxItems(IJSONNode node, JsonObject schema, IJSONValue value, JsonValue memberValue,
+ JSONValidationInfo valinfo) {
+ if (memberValue != null && memberValue.isNumber() && memberValue.asInt() > 0) {
+ if (value instanceof IJSONArray) {
+ int instanceSize = getSize((IJSONArray) value);
+ int size = memberValue.asInt();
+ if (instanceSize > size) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Array has too many items. Expected " + size + " or fewer", line, 0,
+ offset == 0 ? 1 : offset);
+ }
+ }
+ }
+ }
+
+ private void validateMinItems(IJSONNode node, JsonObject schema, IJSONValue value, JsonValue memberValue,
+ JSONValidationInfo valinfo) {
+ if (memberValue != null && memberValue.isNumber() && memberValue.asInt() > 0) {
+ if (value instanceof IJSONArray) {
+ int instanceSize = getSize((IJSONArray) value);
+ int size = memberValue.asInt();
+ if (instanceSize < size) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Array has too few items. Expected " + size + " or more", line, 0,
+ offset == 0 ? 1 : offset);
+ }
+ }
+ }
+ }
+
+ private void validateUniqueItems(IJSONNode node, JsonObject schema, IJSONValue value, JsonValue memberValue,
+ JSONValidationInfo valinfo) {
+ if (memberValue != null && memberValue.isBoolean() && memberValue.asBoolean()) {
+ if (value instanceof IJSONArray) {
+ Set<String> instanceValues = new HashSet<String>();
+ IJSONNode child = value.getFirstChild();
+ int instanceSize = 0;
+ while (child != null) {
+ instanceSize = instanceSize + 1;
+ instanceValues.add(JSONUtil.getString(child));
+ child = child.getNextSibling();
+ }
+ ;
+ if (instanceSize != instanceValues.size()) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Array has duplicate items", line, 0, offset == 0 ? 1 : offset);
+ }
+ }
+ }
+ }
+
+ private void validateItems(IJSONNode node, JsonObject schema, IJSONValue value, JsonValue memberValue,
+ JSONValidationInfo valinfo) {
+ JsonValue additionalItems = schema.get(IJSONSchemaNode.ADDITIONAL_ITEMS);
+ if (additionalItems != null && additionalItems.isBoolean() && !additionalItems.asBoolean()) {
+ if (memberValue != null && memberValue.isArray()) {
+ if (value instanceof IJSONArray) {
+ int instanceSize = getSize((IJSONArray) value);
+ int size = memberValue.asArray().size();
+ if (instanceSize > size) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Array has too many items. Expected " + size + " or fewer", line, 0,
+ offset == 0 ? 1 : offset);
+ }
+ }
+ }
+ }
+ }
+
+ private int getSize(IJSONArray instance) {
+ if (instance == null) {
+ return 0;
+ }
+ int instanceSize = 0;
+ IJSONNode child = instance.getFirstChild();
+ while (child != null) {
+ instanceSize = instanceSize + 1;
+ child = child.getNextSibling();
+ }
+ return instanceSize;
+ }
+
+ private void validateNumber(IJSONNode node, JsonObject schema, Member member, JSONValidationInfo valinfo,
+ IJSONValue value) {
+ if (IJSONSchemaNode.MULTIPLEOF.equals(member.getName())) {
+ validateMultipleOf(node, schema, value, valinfo);
+ }
+ if (IJSONSchemaNode.MAXIMUM.equals(member.getName())) {
+ validateMaximum(node, schema, value, valinfo);
+ }
+ if (IJSONSchemaNode.MINIMUM.equals(member.getName())) {
+ validateMinimum(node, schema, value, valinfo);
+ }
+ }
+
+ private void validateMaximum(IJSONNode node, JsonObject schema, IJSONValue valueNode, JSONValidationInfo valinfo) {
+ double maximum;
+ try {
+ maximum = schema.getDouble(IJSONSchemaNode.MAXIMUM, Double.MIN_VALUE);
+ } catch (Exception e) {
+ maximum = Double.MIN_VALUE;
+ }
+ if (maximum > Double.MIN_VALUE) {
+ boolean exclusiveMaximum;
+ try {
+ exclusiveMaximum = schema.getBoolean(IJSONSchemaNode.EXCLUSIVE_MAXIMUM, false);
+ } catch (Exception e1) {
+ exclusiveMaximum = false;
+ }
+ String valueStr = JSONUtil.getString(valueNode);
+ try {
+ double value = new Double(valueStr).doubleValue();
+ boolean valid = exclusiveMaximum ? value < maximum : value <= maximum;
+ if (!valid) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ if (exclusiveMaximum) {
+ valinfo.addMessage("Value is above the exclusive maximum of " + maximum, line, 0,
+ offset == 0 ? 1 : offset);
+ } else {
+ valinfo.addMessage("Value is above the maximum of " + maximum, line, 0,
+ offset == 0 ? 1 : offset);
+ }
+ }
+ } catch (NumberFormatException e) {
+ // ignore
+ return;
+ }
+ }
+ }
+
+ private void validateMinimum(IJSONNode node, JsonObject schema, IJSONValue valueNode, JSONValidationInfo valinfo) {
+ double minimum;
+ try {
+ minimum = schema.getDouble(IJSONSchemaNode.MINIMUM, Double.MAX_VALUE);
+ } catch (Exception e) {
+ minimum = Double.MAX_VALUE;
+ }
+ if (minimum < Double.MAX_VALUE) {
+ boolean exclusiveMinimum;
+ try {
+ exclusiveMinimum = schema.getBoolean(IJSONSchemaNode.EXCLUSIVE_MINIMUM, false);
+ } catch (Exception e1) {
+ exclusiveMinimum = false;
+ }
+ String valueStr = JSONUtil.getString(valueNode);
+ try {
+ double value = new Double(valueStr).doubleValue();
+ boolean valid = exclusiveMinimum ? value > minimum : value >= minimum;
+ if (!valid) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ if (exclusiveMinimum) {
+ valinfo.addMessage("Value is below the exclusive minimum of " + minimum, line, 0,
+ offset == 0 ? 1 : offset);
+ } else {
+ valinfo.addMessage("Value is below the minimum of " + minimum, line, 0,
+ offset == 0 ? 1 : offset);
+ }
+ }
+ } catch (NumberFormatException e) {
+ // ignore
+ return;
+ }
+ }
+ }
+
+ private void validateMultipleOf(IJSONNode node, JsonObject schema, IJSONValue valueNode,
+ JSONValidationInfo valinfo) {
+ int multipleOff;
+ try {
+ multipleOff = schema.getInt(IJSONSchemaNode.MULTIPLEOF, -1);
+ } catch (Exception e) {
+ multipleOff = -1;
+ }
+ if (multipleOff > 0) {
+ String value = JSONUtil.getString(valueNode);
+ double n;
+ try {
+ n = new Double(value).doubleValue();
+ } catch (NumberFormatException e) {
+ // ignore
+ return;
+ }
+ long div = Math.round(n / multipleOff);
+ if (Math.abs(div * multipleOff - n) > 1e-12) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Value is not divisible by " + multipleOff, line, 0, offset == 0 ? 1 : offset);
+ }
+ }
+ }
+
+ private boolean isType(JSONSchemaType[] types, JSONSchemaType type) {
+ for (JSONSchemaType t : types) {
+ if (t == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void validateString(IJSONNode node, JsonObject schema, Member member, JSONValidationInfo valinfo,
+ IJSONValue value) {
+ if (IJSONSchemaNode.MIN_LENGTH.equals(member.getName())) {
+ validateMinLength(node, schema, value, valinfo);
+ }
+ if (IJSONSchemaNode.MAX_LENGTH.equals(member.getName())) {
+ validateMaxLength(node, schema, value, valinfo);
+ }
+ if (IJSONSchemaNode.PATTERN.equals(member.getName())) {
+ validatePattern(node, schema, value, valinfo);
+ }
+ }
+
+ private void validateMaxLength(IJSONNode node, JsonObject schema, IJSONValue valueNode,
+ JSONValidationInfo valinfo) {
+ int maxLength;
+ try {
+ maxLength = schema.getInt(IJSONSchemaNode.MAX_LENGTH, -1);
+ } catch (Exception e) {
+ maxLength = -1;
+ }
+ if (maxLength >= 0) {
+ String value = JSONUtil.getString(valueNode);
+ boolean valid = value == null || value.length() <= maxLength;
+ if (!valid) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("String is longer than the maximum length of " + maxLength, line, 0,
+ offset == 0 ? 1 : offset);
+ }
+ }
+ }
+
+ private void validateMinLength(IJSONNode node, JsonObject schema, IJSONValue valueNode,
+ JSONValidationInfo valinfo) {
+ int minLength;
+ try {
+ minLength = schema.getInt(IJSONSchemaNode.MIN_LENGTH, -1);
+ } catch (Exception e) {
+ minLength = -1;
+ }
+ if (minLength >= 0) {
+ String value = JSONUtil.getString(valueNode);
+ boolean valid = value == null || value.length() >= minLength;
+ if (!valid) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("String is shorter than the minimum length of " + minLength, line, 0,
+ offset == 0 ? 1 : offset);
+ }
+ }
+ }
+
+ private void validatePattern(IJSONNode node, JsonObject schema, IJSONValue valueNode, JSONValidationInfo valinfo) {
+ String patternStr;
+ try {
+ patternStr = schema.getString(IJSONSchemaNode.PATTERN, null);
+ } catch (Exception e) {
+ patternStr = null;
+ }
+ if (patternStr != null) {
+ String value = JSONUtil.getString(valueNode);
+ if (value != null) {
+ Pattern pattern = Pattern.compile(patternStr);
+ Matcher matcher = pattern.matcher(value);
+ if (!matcher.matches()) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("String does not match the pattern of " + patternStr, line, 0,
+ offset == 0 ? 1 : offset);
+ }
+ }
+ }
+ }
+
+ private void validateType(IJSONNode node, Member member, JSONValidationInfo valinfo) {
+ if (IJSONSchemaNode.TYPE.equals(member.getName())) {
+ Set<String> types = new HashSet<String>();
+ if (member.getValue().isString()) {
+ types.add(member.getValue().asString());
+ } else if (member.getValue().isArray()) {
+ JsonArray array = (JsonArray) member.getValue();
+ for (JsonValue item : array) {
+ types.add(item.asString());
+ }
+ }
+ boolean valid = false;
+ for (String type : types) {
+ if (node.getNodeType() == IJSONNode.OBJECT_NODE && JSONSchemaType.Object.getName().equals(type)) {
+ valid = true;
+ break;
+ }
+ if (node.getNodeType() == IJSONNode.PAIR_NODE) {
+ IJSONValue value = ((IJSONPair) node).getValue();
+ if (value == null && JSONSchemaType.Null.getName().equals(type)) {
+ valid = true;
+ break;
+ }
+ if (value == null) {
+ valid = false;
+ break;
+ }
+ if (value.getNodeType() == IJSONNode.OBJECT_NODE && JSONSchemaType.Object.getName().equals(type)) {
+ valid = true;
+ break;
+ }
+ if (value.getNodeType() == IJSONNode.VALUE_STRING_NODE
+ && JSONSchemaType.String.getName().equals(type)) {
+ valid = true;
+ break;
+ }
+ if (value.getNodeType() == IJSONNode.ARRAY_NODE && JSONSchemaType.Array.getName().equals(type)) {
+ valid = true;
+ break;
+ }
+ if (value.getNodeType() == IJSONNode.VALUE_BOOLEAN_NODE
+ && JSONSchemaType.Boolean.getName().equals(type)) {
+ valid = true;
+ break;
+ }
+ if (value.getNodeType() == IJSONNode.VALUE_NULL_NODE
+ && JSONSchemaType.Null.getName().equals(type)) {
+ valid = true;
+ break;
+ }
+ if (value.getNodeType() == IJSONNode.VALUE_NUMBER_NODE
+ && JSONSchemaType.Number.getName().equals(type)) {
+ valid = true;
+ break;
+ }
+ if (value.getNodeType() == IJSONNode.VALUE_NUMBER_NODE
+ && JSONSchemaType.Integer.getName().equals(type)) {
+ valid = true;
+ break;
+ }
+ }
+ }
+ if (!valid) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ StringBuffer buffer = new StringBuffer();
+ Iterator<String> iter = types.iterator();
+ buffer.append(OPEN_BRACKET);
+ while (iter.hasNext()) {
+ buffer.append(iter.next());
+ if (iter.hasNext()) {
+ buffer.append(COMMA);
+ }
+ }
+ buffer.append(CLOSE_BRACKET);
+ valinfo.addMessage("Incorrect type. Expected " + buffer.toString(), line, 0, offset == 0 ? 1 : offset);
+ }
+ }
+ }
+
+ private void validateEnum(IJSONNode node, JsonObject schema, JSONValidationInfo valinfo) {
+ JsonValue value = schema.get(IJSONSchemaNode.ENUM);
+ if (value instanceof JsonArray) {
+ JsonArray array = value.asArray();
+ Iterator<JsonValue> iter = array.iterator();
+ Set<String> values = new HashSet<String>();
+ while (iter.hasNext()) {
+ String v = iter.next().toString();
+ values.add(JSONUtil.removeQuote(v));
+ }
+ if (node instanceof IJSONPair) {
+ IJSONPair pair = (IJSONPair) node;
+ String v = JSONUtil.getString(pair.getValue());
+ if (!values.contains(v)) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Value is not an accepted value. Valid values " + values + "'", line, 0,
+ offset == 0 ? 1 : offset);
+ }
+ }
+ }
+ }
+
+ private void validateRequired(IJSONNode node, JsonObject schema, JSONValidationInfo valinfo) {
+ JsonValue required = schema.get(IJSONSchemaNode.REQUIRED);
+ if (required instanceof JsonArray) {
+ JsonArray array = required.asArray();
+ Iterator<JsonValue> iter = array.iterator();
+ Set<String> values = new HashSet<String>();
+ while (iter.hasNext()) {
+ JsonValue v = iter.next();
+ if (v.isString()) {
+ values.add(v.asString());
+ }
+ }
+ Set<String> properties = getProperties(node);
+ for (String property : properties) {
+ if (property != null && values.contains(property)) {
+ values.remove(property);
+ }
+ }
+ for (String value : values) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Missing property '" + value + "'", line, 0, offset == 0 ? 1 : offset);
+ }
+ }
+ }
+
+ private void validateMinProperties(IJSONNode node, JsonObject schema, JSONValidationInfo valinfo) {
+ int value;
+ try {
+ value = schema.getInt(IJSONSchemaNode.MIN_PROPERTIES, -1);
+ } catch (Exception e) {
+ value = -1;
+ }
+ if (value >= 0) {
+ Set<String> properties = getProperties(node);
+ if (properties.size() < value) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Object has fewer properties than the required number of" + value, line, 0,
+ offset == 0 ? 1 : offset);
+ }
+ }
+ }
+
+ private void validateMaxProperties(IJSONNode node, JsonObject schema, JSONValidationInfo valinfo) {
+ int value;
+ try {
+ value = schema.getInt(IJSONSchemaNode.MAX_PROPERTIES, -1);
+ } catch (Exception e) {
+ value = -1;
+ }
+ if (value >= 0) {
+ Set<String> properties = getProperties(node);
+ if (properties.size() > value) {
+ int offset = node.getStartOffset();
+ int line = node.getModel().getStructuredDocument().getLineOfOffset(offset);
+ valinfo.addMessage("Object has more properties than limit of" + value, line, 0,
+ offset == 0 ? 1 : offset);
+ }
+ }
+ }
+
+ private Set<String> getProperties(IJSONNode node) {
+ Set<String> properties = new HashSet<String>();
+ IJSONNode child = node.getFirstChild();
+ while (child != null) {
+ if (child instanceof IJSONPair) {
+ IJSONPair pair = (IJSONPair) child;
+ if (pair.getName() != null) {
+ properties.add(pair.getName());
+ }
+ }
+ child = child.getNextSibling();
+ }
+ return properties;
+ }
+
+ protected IStructuredModel getModel(String uriString) {
+ URI uri;
+ try {
+ uri = new URI(uriString);
+ } catch (URISyntaxException e) {
+ logWarning(e);
+ return null;
+ }
+ IFile[] files = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(uri);
+ if (files == null || files.length <= 0 || !files[0].exists()) {
+ return null;
+ }
+ IFile file = files[0];
+ IModelManager manager = StructuredModelManager.getModelManager();
+ if (manager == null)
+ return null;
+
+ IStructuredModel model = null;
+ try {
+ file.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
+ } catch (CoreException e) {
+ logWarning(e);
+ }
+ try {
+ try {
+ model = manager.getModelForRead(file);
+ } catch (UnsupportedEncodingException ex) {
+ // retry ignoring META charset for invalid META charset
+ // specification
+ // recreate input stream, because it is already partially read
+ model = manager.getModelForRead(file, new String(), null);
+ }
+ } catch (UnsupportedEncodingException ex) {
+ } catch (IOException ex) {
+ } catch (CoreException e) {
+ logWarning(e);
+ }
+ return model;
+ }
+
+ private static void logWarning(Exception e) {
+ IStatus status = new Status(IStatus.WARNING, JSONCorePlugin.PLUGIN_ID, e.getMessage(), e);
+ JSONCorePlugin.getDefault().getLog().log(status);
+ }
+
+ /**
+ * Store additional information in the message parameters. For JSON
+ * validation there are three additional pieces of information to store:
+ * param[0] = the column number of the error param[1] = the 'squiggle
+ * selection strategy' for which DOM part to squiggle param[2] = the name or
+ * value of what is to be squiggled
+ *
+ * @see org.eclipse.wst.json.core.internal.validation.core.AbstractNestedValidator#addInfoToMessage(org.eclipse.wst.json.core.internal.validation.core.ValidationMessage,
+ * org.eclipse.wst.validation.internal.provisional.core.IMessage)
+ */
+ @Override
+ protected void addInfoToMessage(ValidationMessage validationMessage, IMessage message) {
+ String key = validationMessage.getKey();
+ if (key != null) {
+ JSONMessageInfoHelper messageInfoHelper = new JSONMessageInfoHelper();
+ String[] messageInfo = messageInfoHelper.createMessageInfo(key, validationMessage.getMessageArguments());
+
+ message.setAttribute(COLUMN_NUMBER_ATTRIBUTE, new Integer(validationMessage.getColumnNumber()));
+ /*
+ * message.setAttribute(SQUIGGLE_SELECTION_STRATEGY_ATTRIBUTE,
+ * messageInfo[0]);
+ * message.setAttribute(SQUIGGLE_NAME_OR_VALUE_ATTRIBUTE,
+ * messageInfo[1]);
+ */
+ }
+ }
+
+ /**
+ * Get the nested validation context.
+ *
+ * @param state
+ * the validation state.
+ * @param create
+ * when true, a new context will be created if one is not found
+ * @return the nested validation context.
+ */
+ @Override
+ protected NestedValidatorContext getNestedContext(ValidationState state, boolean create) {
+ NestedValidatorContext context = null;
+ Object o = state.get(JSON_VALIDATOR_CONTEXT);
+ if (o instanceof JSONNestedValidatorContext)
+ context = (JSONNestedValidatorContext) o;
+ else if (create) {
+ context = new JSONNestedValidatorContext();
+ }
+ return context;
+ }
+
+ @Override
+ public void validationStarting(IProject project, ValidationState state, IProgressMonitor monitor) {
+ if (project != null) {
+ NestedValidatorContext context = getNestedContext(state, false);
+ if (context == null) {
+ context = getNestedContext(state, true);
+ if (context != null)
+ context.setProject(project);
+ setupValidation(context);
+ state.put(JSON_VALIDATOR_CONTEXT, context);
+ }
+ super.validationStarting(project, state, monitor);
+ }
+ }
+
+ @Override
+ public void validationFinishing(IProject project, ValidationState state, IProgressMonitor monitor) {
+ if (project != null) {
+ super.validationFinishing(project, state, monitor);
+ NestedValidatorContext context = getNestedContext(state, false);
+ if (context != null) {
+ teardownValidation(context);
+ state.put(JSON_VALIDATOR_CONTEXT, null);
+ }
+ }
+ }
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/jsonpath/JSONPathMatcher.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/jsonpath/JSONPathMatcher.java new file mode 100644 index 0000000000..93089f364f --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/jsonpath/JSONPathMatcher.java @@ -0,0 +1,57 @@ +/**
+ * Copyright (c) 2013-2016 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.jsonpath;
+
+import org.eclipse.json.jsonpath.IJSONPath;
+import org.eclipse.wst.json.core.document.IJSONNode;
+import org.eclipse.wst.json.core.document.IJSONPair;
+
+public class JSONPathMatcher {
+
+ public static boolean isMatch(IJSONNode node, IJSONPath path) {
+ String name = null;
+ String segment = null;
+ String[] segments = path.getSegments();
+ for (int i = segments.length - 1; i >= 0; i--) {
+ segment = segments[i];
+ name = getName(node);
+ if (name == null) {
+ return false;
+ }
+ if (!(segment.equals(name))) {
+ return false;
+ }
+ node = node.getParentNode();
+ }
+ return true;
+ }
+
+ public static String getName(IJSONNode node) {
+ if (node == null) {
+ return null;
+ }
+ if (node.getNodeType() == IJSONNode.PAIR_NODE) {
+ return ((IJSONPair) node).getName();
+ }
+ IJSONNode parent = node.getParentOrPairNode();
+ if (parent == null) {
+ return null;
+ }
+ switch (parent.getNodeType()) {
+ case IJSONNode.DOCUMENT_NODE:
+ return "$";
+ case IJSONNode.PAIR_NODE:
+ return ((IJSONPair) parent).getName();
+ default:
+ return null;
+ }
+ }
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/modelhandler/IIModelHandlerForJSON.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/modelhandler/IIModelHandlerForJSON.java new file mode 100644 index 0000000000..5aad34412e --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/modelhandler/IIModelHandlerForJSON.java @@ -0,0 +1,20 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.modelhandler;
+
+import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler;
+
+/**
+ * Model handler API for JSON.
+ */
+public interface IIModelHandlerForJSON extends IModelHandler {
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/preferences/JSONCorePreferenceNames.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/preferences/JSONCorePreferenceNames.java new file mode 100644 index 0000000000..f3a8db1ce8 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/preferences/JSONCorePreferenceNames.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2005, 2016 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 + * David Carver - STAR - [205989] - [validation] validate XML after XInclude resolution + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.preferences.XMLCorePreferenceNames + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.preferences; + +/** + * JSON core preference keys. + * + */ +public class JSONCorePreferenceNames { + + private JSONCorePreferenceNames() { + // empty private constructor so users cannot instantiate class + } + + /** + * Indicates whether or not Syntax Validator should run as part of JSON + * Validation. + * <p> + * Value is of type <code>boolean</code>.<br /> + * Possible values: {TRUE, FALSE} + * </p> + */ + public static final String SYNTAX_VALIDATION = "syntaxValidation"; //$NON-NLS-1$ + public static final String SCHEMA_VALIDATION = "schemaValidation"; //$NON-NLS-1$ + + /** + * Indicates whether or not a message should be produced when validating a + * file that specifies not grammar. + * <p> + * Value is of type <code>integer</code>.<br /> + * Possible values: {0, 1, 2} (none, warning, error) + * </p> + */ + public static final String INDICATE_NO_GRAMMAR = "indicateNoGrammar";//$NON-NLS-1$ + + public static final String USE_PROJECT_SETTINGS = "use-project-settings";//$NON-NLS-1$ + + public static final String MISSING_START_OBJECT = "missingStartObject"; //$NON-NLS-1$ + public static final String MISSING_END_OBJECT = "missingEndObject"; //$NON-NLS-1$ + public static final String MISSING_START_ARRAY = "missingStartArray"; //$NON-NLS-1$ + public static final String MISSING_END_ARRAY = "missingEndArray";//$NON-NLS-1$ + + public static final String MISSING_BRACKET = "missingBracket"; + + // TODO : clean the following preference names... + + public static final String CASE_IDENTIFIER = "identifierCase"; //$NON-NLS-1$ + public static final String CASE_SELECTOR = "selectorCase"; //$NON-NLS-1$ + public static final String CASE_PROPERTY_NAME = "propNameCase"; //$NON-NLS-1$ + public static final String CASE_PROPERTY_VALUE = "propValueCase"; //$NON-NLS-1$ + public static final String FORMAT_BETWEEN_VALUE = "betweenValue"; //$NON-NLS-1$ + public static final String FORMAT_PROP_POST_DELIM = "postDelim"; //$NON-NLS-1$ + public static final String FORMAT_PROP_PRE_DELIM = "preDelim"; //$NON-NLS-1$ + public static final String FORMAT_QUOTE = "quote"; //$NON-NLS-1$ + public static final String FORMAT_QUOTE_IN_URI = "quoteInURI"; //$NON-NLS-1$ + public static final String FORMAT_SPACE_BETWEEN_SELECTORS = "spaceBetweenSelectors"; //$NON-NLS-1$ + public static final String WRAPPING_NEWLINE_ON_OPEN_BRACE = "newLineOnOpenBrace"; //$NON-NLS-1$ + public static final String WRAPPING_ONE_PER_LINE = "onePropertyPerLine"; //$NON-NLS-1$ + public static final String WRAPPING_PROHIBIT_WRAP_ON_ATTR = "prohibitWrapOnAttr"; //$NON-NLS-1$ + + /** + * The default extension to use when none is specified in the New CSS File + * Wizard. + * <p> + * Value is of type <code>String</code>. + * </p> + */ + public static final String DEFAULT_EXTENSION = "defaultExtension"; //$NON-NLS-1$ + + /** + * The maximum width of a line before a line split is needed. + * <p> + * Value is of type <code>Integer</code>. + * </p> + */ + public static final String LINE_WIDTH = "lineWidth";//$NON-NLS-1$ + + /** + * Indicates if all blanks lines should be cleared during formatting. Blank + * lines will be kept when false. + * <p> + * Value is of type <code>Boolean</code>. + * </p> + */ + public static final String CLEAR_ALL_BLANK_LINES = "clearAllBlankLines";//$NON-NLS-1$ + + /** + * The number of #INDENTATION_CHAR for 1 indentation. + * <p> + * Value is of type <code>Integer</code>. + * </p> + */ + public static final String INDENTATION_SIZE = "indentationSize";//$NON-NLS-1$ + + /** + * The character used for indentation. + * <p> + * Value is of type <code>String</code>.<br /> + * Possible values: {TAB, SPACE} + * </p> + */ + public static final String INDENTATION_CHAR = "indentationChar";//$NON-NLS-1$ + + /** + * Possible value for the preference #INDENTATION_CHAR. Indicates to use tab + * character when formatting. + * + * @see #SPACE + * @see #INDENTATION_CHAR + */ + public static final String TAB = "tab"; //$NON-NLS-1$ + + /** + * Possible value for the preference #INDENTATION_CHAR. Indicates to use + * space character when formatting. + * + * @see #TAB + * @see #INDENTATION_CHAR + */ + public static final String SPACE = "space"; //$NON-NLS-1$ + + /** + * Indicates whether or not to quote all attribute values during cleanup. + * <p> + * Value is of type <code>Boolean</code>. + * </p> + */ + public static final String QUOTE_ATTR_VALUES = "quoteAttrValues";//$NON-NLS-1$ + + /** + * Indicates whether or not cleanup processor should format source. + * <p> + * Value is of type <code>Boolean</code>. + * </p> + */ + public static final String FORMAT_SOURCE = "formatSource";//$NON-NLS-1$ + + /** + * Possible value for the case preferences Indicates to leave case as is. + * + * @see #LOWER + * @see #UPPER + */ + public static final int ASIS = 0; + + /** + * Possible value for the case preferences Indicates to make name lowercase. + * + * @see #ASIS + * @see #UPPER + */ + public static final int LOWER = 1; + + /** + * Possible value for the case preferences Indicates to make name uppercase. + * + * @see #LOWER + * @see #ASIS + */ + public static final int UPPER = 2; + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/regions/JSONRegionContexts.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/regions/JSONRegionContexts.java new file mode 100644 index 0000000000..93bd86d2ba --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/regions/JSONRegionContexts.java @@ -0,0 +1,41 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.regions;
+
+/**
+ * JSON region contexts.
+ *
+ */
+public interface JSONRegionContexts {
+
+ // JSON Object
+ public static final String JSON_OBJECT_OPEN = "JSON_OBJECT_OPEN"; //$NON-NLS-1$
+ public static final String JSON_OBJECT_CLOSE = "JSON_OBJECT_CLOSE"; //$NON-NLS-1$
+ public static final String JSON_OBJECT_KEY = "JSON_OBJECT_KEY"; //$NON-NLS-1$
+ public static final String JSON_COLON = "JSON_COLON"; //$NON-NLS-1$
+ public static final String JSON_COMMA = "JSON_COMMA"; //$NON-NLS-1$
+
+ // JSON Array
+ public static final String JSON_ARRAY_OPEN = "JSON_ARRAY_OPEN"; //$NON-NLS-1$
+ public static final String JSON_ARRAY_CLOSE = "JSON_ARRAY_CLOSE"; //$NON-NLS-1$
+
+ // JSON Value
+ public static final String JSON_VALUE_STRING = "JSON_VALUE_STRING"; //$NON-NLS-1$
+ public static final String JSON_VALUE_BOOLEAN = "JSON_VALUE_BOOLEAN"; //$NON-NLS-1$
+ public static final String JSON_VALUE_NUMBER = "JSON_VALUE_NUMBER"; //$NON-NLS-1$
+ public static final String JSON_VALUE_NULL = "JSON_VALUE_NULL"; //$NON-NLS-1$
+
+ // Other
+ public static final String JSON_COMMENT = "JSON_COMMENT"; //$NON-NLS-1$
+ public static final String JSON_UNKNOWN = "JSON_UNKNOWN"; //$NON-NLS-1$
+ public static final String WHITE_SPACE = "WHITE_SPACE"; //$NON-NLS-1$
+ public static final String UNDEFINED = "UNDEFINED"; //$NON-NLS-1$
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalog.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalog.java new file mode 100644 index 0000000000..3227eb1674 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalog.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2002, 2009 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 + * Jesper Steen Moeller - Added XML Catalogs 1.1 support + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalog + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.schema.catalog; + +import java.io.IOException; +import java.net.MalformedURLException; + +/** + * A representation of the model object '<em><b>Catalog</b></em>'. + * + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + */ +public interface ICatalog { + /** + * Returns catalog id string + * + * @return catalog id string + */ + String getId(); + + /** + * Sets catalog id string + * + */ + void setId(String id); + + void setLocation(String location); + + String getLocation(); + + /** + * Return the applicable schema. + * + * <p> + * If a URI entry exists in the Catalog for the URI specified, return the + * mapped value. + * </p> + * + * <p> + * URI comparison is case sensitive. + * </p> + * + * @param uri + * The URI to locate in the catalog. + * + * @return The resolved URI. + * + * @throws MalformedURLException + * The system identifier of a subordinate catalog cannot be + * turned into a valid URL. + * @throws IOException + * Error reading subordinate catalog file. + */ + String resolveSchema(String uri) throws MalformedURLException, IOException; + + /** + * Adds catalog element to the collection of the catalog elements. + * + * @param element + * - catalog element + */ + void addCatalogElement(ICatalogElement element); + + /** + * Removes catalog element from the collection of the catalog elements. + * + * @param element + * - catalog element + */ + void removeCatalogElement(ICatalogElement element); + + /** + * Returns an array of catalog elements of type ICatalogElement.TYPE_ENTRY + * + * @return an array of catalog elements + */ + ICatalogEntry[] getCatalogEntries(); + + /** + * Returns an array of catalog elements of type ICatalogElement.TYPE_REWRITE + * + * @return an array of rewrite catalog elements + */ + IRewriteEntry[] getRewriteEntries(); + + /** + * Returns an array of catalog elements of type ICatalogElement.TYPE_SUFFIX + * + * @return an array of suffix entry elements + */ + ISuffixEntry[] getSuffixEntries(); + + /** + * Returns an array of catalog elements of type + * ICatalogElement.TYPE_DELEGATE + * + * @return an array of delegate catalog elements + */ + IDelegateCatalog[] getDelegateCatalogs(); + + /** + * Returns an array of catalog elements of type + * ICatalogElement.TYPE_DELEGATE + * + * @return an array of catalog elements + */ + INextCatalog[] getNextCatalogs(); + + /** + * Returns new catalog element with the specified type. If the type is one + * of ELEMENT_TYPE, the result entry will have corresponding interface. + * + * @return + */ + ICatalogElement createCatalogElement(int type); + + /** + * Removes all the elements from this catalog. + * + */ + void clear(); + + // void load() throws IOException; + + void save() throws IOException; + + /** + * This method copies all catalog entries from source catalog to this one. + * + * @param catalog + * - source catalog + * @param includeNested + * - a boolean flag indicating wether to include entries of the + * same type from the nested catalogs + */ + void addEntriesFromCatalog(ICatalog catalog); + + /** + * Adds a listener of the catalog events + * + * @param listener + * - listener of the catalog events + * @see ICatalogEvent + */ + void addListener(ICatalogListener listener); + + /** + * Removes a listener of the catalog events + * + * @param listener + * - listener of the catalog events + * @see ICatalogEvent + */ + void removeListener(ICatalogListener listener); + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogElement.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogElement.java new file mode 100644 index 0000000000..cd030cba53 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogElement.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2002, 2009 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 + * Jesper Steen Moeller - Added XML Catalogs 1.1 support + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalogElement + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.schema.catalog; + + +/** + * + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + */ +public interface ICatalogElement +{ + /** Types of the catalog entries */ + /** The PUBLIC, URI or SYSTEM Catalog Entry. */ + int TYPE_ENTRY = 1; + + /** The NEXT_CATALOG Catalog Entry type. */ + int TYPE_NEXT_CATALOG = 10; + + /** Rewrite types (since XML Catalogs 1.1) */ + int TYPE_REWRITE = 20; + + /** Delegate types (sinceXML Catalogs 1.1) */ + int TYPE_DELEGATE = 30; + + /** Suffix types (since XML Catalogs 1.1) */ + int TYPE_SUFFIX = 40; + + /** + * Returns the value of the '<em><b>Type</b></em>' attribute. + * + * @return the value of the '<em>Type</em>' attribute. + */ + int getType(); + + /** + * Returns the value of the attribute with specified name. + * + * @return the value of the attribute with specified name. + * @see #setAttributeValue(String) + */ + String getAttributeValue(String name); + + /** + * Sets the value of the named attribute. + * + * @param name + * the name of the attribute that will be set + * @param value + * the new value of the named attribute. + * @see #getAttributeValue() + */ + void setAttributeValue(String name, String value); + + /** + * Returns an array of attribute names for any dynamic attributes that may exist + * + * @return array of attribute names + * @see #getAttributeValue() + * @see #setAttributeValue(String) + */ + String[] getAttributes(); + + /** + * Returns element's id string + * + * @return element's id string + */ + public String getId(); + + /** + * Sets element's id string + * + */ + public void setId(String id); + + public void setOwnerCatalog(ICatalog catalog); + + public ICatalog getOwnerCatalog(); + + void setBase(String base); + + String getBase(); +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogEntry.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogEntry.java new file mode 100644 index 0000000000..b692ff36e9 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogEntry.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2002, 2010 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalogEntry + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.schema.catalog; + +/** + * + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + */ +public interface ICatalogEntry extends ICatalogElement +{ + + /** The Schema Catalog Entry type. */ + public static final int ENTRY_TYPE_SCHEMA = 2; + + /** Attribute name for Web address of catalog entry */ + public static final String ATTR_WEB_URL = "webURL"; //$NON-NLS-1$ + + /** + * + * @param entryType + */ + public void setEntryType(int entryType); + + /** + * + * @return + */ + public int getEntryType(); + + /** + * + * @param key + */ + public void setKey(String key); + + /** + * + * @return + */ + public String getKey(); + + /** + * + * @return + */ + public String getURI(); + + /** + * + * @param uri + */ + public void setURI(String uri); +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogEvent.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogEvent.java new file mode 100644 index 0000000000..b8ee504a9d --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogEvent.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2002, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalogEvent + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.schema.catalog; + + + +/** + * + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + */ +public interface ICatalogEvent +{ + /** */ + public static final int CHANGED = 0; + + public static final int ELEMENT_ADDED = 1; + + /** */ + public static final int ELEMENT_REMOVED = 2; + + /** */ + public static final int ELEMENT_CHANGED = 3; + + /** + * + * @return + */ + public int getEventType(); + + /** + * + * @return + */ + public ICatalog getCatalog(); + + /** + * + * @return + */ + public ICatalogElement getCatalogElement(); +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogListener.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogListener.java new file mode 100644 index 0000000000..bdb9051058 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogListener.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2002, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.provisional.ICatalogListener + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.schema.catalog; + +import java.util.EventListener; + + +/** + * The clients of the catalog that want to listen to catalog changes should + * implement this interface. + * + * @see ICatalog, ICatalogEvent, + * + */ +public interface ICatalogListener extends EventListener +{ + /** + * This method allows to react to catalog events + * + * @param event - + * an event that client should react to + */ + public void catalogChanged(ICatalogEvent event); +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/IDelegateCatalog.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/IDelegateCatalog.java new file mode 100644 index 0000000000..a1abc0291b --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/IDelegateCatalog.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2009 Jesper Steen Moeller + * 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: + * Jesper Steen Moeller - Added XML Catalogs 1.1 support + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.provisional.IDelegateCatalog + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.schema.catalog; + +/** + * + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + */ +public interface IDelegateCatalog extends ICatalogElement +{ + /** The SYSTEM Catalog Entry type. */ + int DELEGATE_TYPE_PUBLIC = 31; + + /** The SYSTEM Catalog Entry type. */ + int DELEGATE_TYPE_SYSTEM = 32; + + /** The URI Catalog Entry type. */ + int DELEGATE_TYPE_SCHEMA = 33; + + /** + * + * @param entryType + */ + void setEntryType(int entryType); + + /** + * + * @return + */ + int getEntryType(); + + /** + * + * @param key + */ + void setStartString(String key); + + /** + * + * @return + */ + String getStartString(); + + /** + * Set location of the referenced catalog. + * + * @param uri - + * location uri of the referenced catalog + */ + void setCatalogLocation(String uri); + + /** + * Get location uri of the referenced catalog. + * + * @return location uri of the referenced catalog + */ + String getCatalogLocation(); + + ICatalog getReferencedCatalog(); +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/INextCatalog.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/INextCatalog.java new file mode 100644 index 0000000000..acf2e2853b --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/INextCatalog.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2002, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Jens Lukowski/Innoopract - initial renaming/restructuring + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.provisional.INextCatalog + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.schema.catalog; + + + +/** + * + * A representation of the nextCatalog OASIS XML catalog element. Object of the + * class that implements this interface would serve as a reference to the + * catalog object. + * + * @see ICatalog, ICatalogElement + * + * This interface currently is used only by the catalog itself. Need to find if + * there are any clients that need it. + * + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + */ +public interface INextCatalog extends ICatalogElement +{ + /** + * Set location of the referenced catalog. + * + * @param uri - + * location uri of the referenced catalog + */ + public void setCatalogLocation(String uri); + + /** + * Get location uri of the referenced catalog. + * + * @return location uri of the referenced catalog + */ + public String getCatalogLocation(); + + public ICatalog getReferencedCatalog(); + + +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/IRewriteEntry.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/IRewriteEntry.java new file mode 100644 index 0000000000..7e86f805a4 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/IRewriteEntry.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2009 Jesper Steen Moeller + * 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: + * Jesper Steen Moeller - Added XML Catalogs 1.1 support + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.provisional.IRewriteEntry + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.schema.catalog; + +/** + * + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + */ +public interface IRewriteEntry extends ICatalogElement +{ + /** The rewriteSystem Catalog type. */ + int REWRITE_TYPE_SYSTEM = 21; + + /** The URI Catalog Entry type. */ + int REWRITE_TYPE_URI = 22; + + /** + * + * @param entryType + */ + void setEntryType(int entryType); + + /** + * + * @return + */ + int getEntryType(); + + /** + * + * @param key + */ + void setStartString(String startString); + + /** + * + * @return + */ + String getStartString(); + + /** + * + * @return + */ + String getRewritePrefix(); + + /** + * + * @param uri + */ + void setRewritePrefix(String uri); +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ISuffixEntry.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ISuffixEntry.java new file mode 100644 index 0000000000..b8828aadd0 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ISuffixEntry.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2009 Jesper Steen Moeller + * 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: + * Jesper Steen Moeller - Added XML Catalogs 1.1 support + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.catalog.provisional.ISuffixEntry + * modified in order to process JSON Objects. + *******************************************************************************/ +package org.eclipse.wst.json.core.schema.catalog; + +/** + * + * <p> + * This interface is not intended to be implemented by clients. + * </p> + * + */ +public interface ISuffixEntry extends ICatalogElement +{ + /** The rewriteSystem Catalog type. */ + int SUFFIX_TYPE_SYSTEM = 41; + + /** The URI Catalog Entry type. */ + int SUFFIX_TYPE_URI = 42; + + /** + * + * @param entryType + */ + void setEntryType(int entryType); + + /** + * + * @return + */ + int getEntryType(); + + /** + * + * @param key + */ + void setSuffix(String suffixString); + + /** + * + * @return + */ + String getSuffix(); + + /** + * + * @return + */ + String getURI(); + + /** + * + * @param uri + */ + void setURI(String uri); +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/text/IJSONPartitions.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/text/IJSONPartitions.java new file mode 100644 index 0000000000..342e96c449 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/text/IJSONPartitions.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2013-2014 Angelo ZERR. + * 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: + * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation + */ +package org.eclipse.wst.json.core.text; + +/** + * This interface is not intended to be implemented. It defines the partitioning + * for JSON and all its partitions. Clients should reference the partition type + * Strings defined here directly. + */ +public interface IJSONPartitions { + + String JSON = "org.eclipse.wst.json.JSON"; //$NON-NLS-1$ + String COMMENT = "org.eclipse.wst.json.COMMENT"; //$NON-NLS-1$ +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/util/JSONUtil.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/util/JSONUtil.java new file mode 100644 index 0000000000..266a306b40 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/util/JSONUtil.java @@ -0,0 +1,305 @@ +/** + * Copyright (c) 2013-2016 Angelo ZERR. + * 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: + * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation + */ + +package org.eclipse.wst.json.core.util; + +import java.util.Enumeration; +import java.util.Iterator; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.wst.json.core.document.IJSONNode; +import org.eclipse.wst.json.core.internal.Logger; +import org.eclipse.wst.json.core.regions.JSONRegionContexts; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegionList; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion; +import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList; + +public class JSONUtil { + + public static final String QUOTE = "\""; //$NON-NLS-1$ + + public static void debugOut(String str) { + Logger.log(Logger.WARNING, "json warning: " + str); //$NON-NLS-1$ + } + + /** + * + */ + public static String extractStringContents(String text) { + return stripQuotes(text); + } + + /** + * + */ + public static IStructuredDocumentRegion findNextSignificantNode( + IStructuredDocumentRegion startNode) { + if (startNode == null) { + return null; + } + IStructuredDocumentRegion node = startNode.getNext(); + while (node != null) { + String type = getStructuredDocumentRegionType(node); + /* + * if (type != JSONRegionContexts.JSON_S && type != + * JSONRegionContexts.JSON_COMMENT && type != + * JSONRegionContexts.JSON_CDO && type != + * JSONRegionContexts.JSON_CDC) { return node; } + */ + node = node.getNext(); + } + return null; + } + + /** + * + */ + public static IStructuredDocumentRegion findNodeBackward( + IStructuredDocumentRegion startNode, + IStructuredDocumentRegion endNode, String type) { + IStructuredDocumentRegion node; + for (node = startNode; node != null; node = node.getPrevious()) { + if (node.getStartOffset() < endNode.getStartOffset()) { + node = null; + break; + } else if (getStructuredDocumentRegionType(node) == type) { + break; + } + } + return node; + } + + /** + * + */ + public static IStructuredDocumentRegion findNodeForward( + IStructuredDocumentRegion startNode, + IStructuredDocumentRegion endNode, String type) { + IStructuredDocumentRegion node; + for (node = startNode; node != null; node = node.getNext()) { + if (endNode.getStartOffset() < node.getStartOffset()) { + node = null; + break; + } else if (getStructuredDocumentRegionType(node) == type) { + break; + } + } + return node; + } + + /** + * + */ + public static IStructuredDocumentRegion findPreviousSignificantNode( + IStructuredDocumentRegion startNode) { + if (startNode == null) { + return null; + } + IStructuredDocumentRegion node = startNode.getPrevious(); + while (node != null) { + String type = getStructuredDocumentRegionType(node); + /* + * if (type != JSONRegionContexts.JSON_S && type != + * JSONRegionContexts.JSON_COMMENT && type != + * JSONRegionContexts.JSON_CDO && type != + * JSONRegionContexts.JSON_CDC) { return node; } + */ + node = node.getPrevious(); + } + return null; + } + + /** + * + */ + public static String getClassString(Object object) { + if (object == null) { + return "null"; //$NON-NLS-1$ + } else { + String name = object.getClass().toString(); + int lastPeriod = name.lastIndexOf('.'); + return name.substring(lastPeriod + 1); + } + } + + /** + * + */ + public static String getStructuredDocumentRegionType( + IStructuredDocumentRegion flatNode) { + if (flatNode == null) { + return JSONRegionContexts.JSON_UNKNOWN; + } + ITextRegionList regions = flatNode.getRegions(); + if (regions == null || regions.size() == 0) { + return JSONRegionContexts.JSON_UNKNOWN; + } + ITextRegion region = regions.get(0); + return region.getType(); + } + + /** + * + */ + public static int getLengthDifference( + IStructuredDocumentRegionList newNodes, + IStructuredDocumentRegionList oldNodes) { + int newLen = getTextLength(newNodes); + int oldLen = getTextLength(oldNodes); + return newLen - oldLen; + } + + /** + * + */ + public static String getRegionText(IStructuredDocumentRegion flatNode, + ITextRegionList regions) { + StringBuffer buf = new StringBuffer(); + if (regions != null) { + for (Iterator i = regions.iterator(); i.hasNext();) { + ITextRegion region = (ITextRegion) i.next(); + if (region == null) { + continue; + } + buf.append(flatNode.getText(region)); + } + } + + return buf.toString(); + } + + /** + * + */ + public static int getTextLength(IStructuredDocumentRegionList nodes) { + int length = 0; + + if (nodes != null) { + for (Enumeration e = nodes.elements(); e.hasMoreElements();) { + IStructuredDocumentRegion flatNode = (IStructuredDocumentRegion) e + .nextElement(); + if (flatNode != null) { + length += flatNode.getText().length(); + } + } + } + + return length; + } + + /** + * + */ + public static String stripQuotes(String text) { + if (text == null) + return null; + String contents = text.trim(); + if (2 <= contents.length()) { + char first = contents.charAt(0); + char last = contents.charAt(contents.length() - 1); + if ((first == '\"' && last == '\"') + || (first == '\'' && last == '\'')) { + contents = contents.substring(1, contents.length() - 1); + } + } + return contents; + } + + public static String detectQuote(String source, String defaultQuote) { + if (source == null) + return defaultQuote; + final String D_QUOTE = "\""; //$NON-NLS-1$ + final String S_QUOTE = "\'"; //$NON-NLS-1$ + + int dIndex = source.indexOf(D_QUOTE); + int sIndex = source.indexOf(S_QUOTE); + if (dIndex < 0 && sIndex < 0) { + return defaultQuote; + } else if (dIndex < 0) { + return D_QUOTE; + } else if (sIndex < 0) { + return S_QUOTE; + } else if (dIndex < sIndex) { + return S_QUOTE; + } else { + return D_QUOTE; + } + } + + /** + * + */ + public static void stripSurroundingSpace(ITextRegionList regions) { + if (regions == null) { + return; + } + while (!regions.isEmpty()) { + ITextRegion region = regions.get(0); + String type = region.getType(); + // if (type == JSONRegionContexts.JSON_S + // || type == JSONRegionContexts.JSON_COMMENT) { + // regions.remove(0); + // } else { + break; + // } + } + while (!regions.isEmpty()) { + ITextRegion region = regions.get(regions.size() - 1); + String type = region.getType(); + // if (type == JSONRegionContexts.JSON_S + // || type == JSONRegionContexts.JSON_COMMENT) { + // regions.remove(region); + // } else { + break; + // } + } + } + + public static boolean isJSONSimpleValue(String regionType) { + return (regionType == JSONRegionContexts.JSON_VALUE_BOOLEAN + || regionType == JSONRegionContexts.JSON_VALUE_NULL + || regionType == JSONRegionContexts.JSON_VALUE_NUMBER || regionType == JSONRegionContexts.JSON_VALUE_STRING); + } + + public static boolean isStartJSONStructure(String regionType) { + return (regionType == JSONRegionContexts.JSON_ARRAY_OPEN || regionType == JSONRegionContexts.JSON_OBJECT_OPEN); + } + + public static boolean isEndJSONStructure(String regionType) { + return (regionType == JSONRegionContexts.JSON_ARRAY_CLOSE || regionType == JSONRegionContexts.JSON_OBJECT_CLOSE); + } + + public static String getString(IJSONNode node) { + String value; + try { + value = node.getModel().getStructuredDocument().get(node.getStartOffset(), node.getEndOffset()-node.getStartOffset()); + } catch (BadLocationException e) { + // ignore + return null; + } + value = removeQuote(value); + return value; + } + + public static String removeQuote(String value) { + if (value != null) { + value = value.trim(); + if (value.startsWith(QUOTE)) { + value = value.substring(1); + } + if (value.endsWith(QUOTE)) { + value = value.substring(0, value.length() - 1); + } + } + return value; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/AnnotationMsg.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/AnnotationMsg.java new file mode 100644 index 0000000000..e3a908ba20 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/AnnotationMsg.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2010 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 + * Angelo Zerr <angelo.zerr@gmail.com> - copied from org.eclipse.wst.xml.core.internal.validation.AnnotationMsg + * modified in order to process JSON Objects. + ********************************************************************************/ +package org.eclipse.wst.json.core.validation; + +public class AnnotationMsg { + + public static final String ID = AnnotationMsg.class.getName(); + + public static String PROBMLEM_ID = "ProblemId"; //$NON-NLS-1$ + public static String LENGTH = "Length"; //$NON-NLS-1$ + public static String ATTRVALUETEXT = "AttributeValueText"; //$NON-NLS-1$ + public static String ATTRVALUENO = "AttributeValueNo"; //$NON-NLS-1$ + public static String ATTRNO = "AttrNo"; //$NON-NLS-1$ + private int problemId; + private Object attributeValueText; + private int length; + + public AnnotationMsg(int problemId, Object attributeValueText, int length) { + super(); + this.problemId = problemId; + this.attributeValueText = attributeValueText; + this.length = length; + } + public int getProblemId() { + return problemId; + } + + public Object getAttributeValueText() { + return attributeValueText; + } + + public int getLength(){ + return length; + } +} diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/ISeverityProvider.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/ISeverityProvider.java new file mode 100644 index 0000000000..fe0557f055 --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/ISeverityProvider.java @@ -0,0 +1,17 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.validation;
+
+public interface ISeverityProvider {
+
+ int getSeverity(String preferenceName);
+
+}
diff --git a/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/JSONSyntaxValidatorHelper.java b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/JSONSyntaxValidatorHelper.java new file mode 100644 index 0000000000..0b06b123ee --- /dev/null +++ b/json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/JSONSyntaxValidatorHelper.java @@ -0,0 +1,322 @@ +/**
+ * Copyright (c) 2013-2014 Angelo ZERR.
+ * 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:
+ * Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
+ */
+package org.eclipse.wst.json.core.validation;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Stack;
+
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.wst.json.core.internal.JSONCoreMessages;
+import org.eclipse.wst.json.core.internal.parser.JSONLineTokenizer;
+import org.eclipse.wst.json.core.internal.validation.ProblemIDsJSON;
+import org.eclipse.wst.json.core.preferences.JSONCorePreferenceNames;
+import org.eclipse.wst.json.core.regions.JSONRegionContexts;
+import org.eclipse.wst.json.core.util.JSONUtil;
+import org.eclipse.wst.validation.internal.operations.LocalizedMessage;
+import org.eclipse.wst.validation.internal.provisional.core.IReporter;
+import org.eclipse.wst.validation.internal.provisional.core.IValidator;
+
+public class JSONSyntaxValidatorHelper {
+
+ /**
+ * The error threshold - sometimes, after you get so many errors, it's not
+ * worth seeing the others
+ */
+ private static final int ERROR_THRESHOLD = 25;
+
+ /**
+ * A token from the tokenizer
+ */
+ private static class Token {
+ String type;
+ int offset;
+ int length;
+ int line;
+ String text;
+
+ public Token(String type, String text, int offset, int length, int line) {
+ this.type = type;
+ this.text = text;
+ this.offset = offset;
+ this.length = length;
+ this.line = line;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(type).append("[").append(line).append("-")
+ .append(offset).append("]: ").append(text).toString();
+ }
+ }
+
+ public static void validate(JSONLineTokenizer tokenizer,
+ IReporter reporter, IValidator validator, ISeverityProvider provider) {
+ List previousRegions = null;
+ String type = null;
+ Stack<Token> tagStack = new Stack<Token>();
+ List<Token> region = null;
+ boolean isClosed = true;
+ int tagErrorCount = 0;
+ Token previousRegion = null;
+ while ((type = getNextToken(tokenizer)) != null) {
+ // System.err.println(type);
+ Token token = new Token(type, tokenizer.yytext(),
+ tokenizer.getOffset(), tokenizer.yylength(),
+ tokenizer.getLine());
+ isClosed = false;
+ boolean hasError = checkExpectedRegion(token, previousRegion,
+ tagStack, reporter, validator, provider);
+ if (type == JSONRegionContexts.JSON_OBJECT_OPEN
+ || type == JSONRegionContexts.JSON_ARRAY_OPEN) {
+ tagStack.push(token);
+ } else if (type == JSONRegionContexts.JSON_OBJECT_CLOSE) {
+ if (tagStack.isEmpty()) {
+ createMissingTagError(token, false, reporter,
+ tagErrorCount, tagStack, validator, provider);
+ } else {
+ Token lastToken = tagStack.peek();
+ if (lastToken.type == JSONRegionContexts.JSON_OBJECT_OPEN) {
+ tagStack.pop();
+ }
+ }
+ } else if (type == JSONRegionContexts.JSON_ARRAY_CLOSE) {
+ if (tagStack.isEmpty()) {
+ createMissingTagError(token, false, reporter,
+ tagErrorCount, tagStack, validator, provider);
+ } else {
+ Token lastToken = tagStack.peek();
+ if (lastToken.type == JSONRegionContexts.JSON_ARRAY_OPEN) {
+ if (!tagStack.isEmpty())
+ tagStack.pop();
+ }
+ }
+ } else if (type.equalsIgnoreCase(JSONRegionContexts.UNDEFINED)) {
+ if ("{".equals(token.text)) {
+ tagStack.push(token);
+ } else if ("}".equals(token.text)) {
+ if (!tagStack.isEmpty()) {
+ tagStack.pop();
+ }
+ } else if ("[".equals(token.text)) {
+ tagStack.push(token);
+ } else if ("]".equals(token.text)) {
+ if (!tagStack.isEmpty()) {
+ tagStack.pop();
+ }
+ } else {
+ if (!hasError) {
+ String messageText = "Unexpected token";
+ LocalizedMessage message = createMessage(token,
+ messageText,
+ JSONCorePreferenceNames.MISSING_BRACKET,
+ provider);
+ getAnnotationMsg(reporter,
+ ProblemIDsJSON.MissingEndBracket, message,
+ null, token.length, validator);
+ }
+ }
+ }
+ /*
+ * else if (check &&
+ * type.equalsIgnoreCase(JSONRegionContexts.UNDEFINED)) { String
+ * messageText = "Unexpected token"; LocalizedMessage message =
+ * createMessage(token, messageText,
+ * JSONCorePreferenceNames.MISSING_BRACKET, provider);
+ * getAnnotationMsg(reporter, ProblemIDsJSON.MissingEndBracket,
+ * message, null, token.length, validator);
+ *
+ * }
+ */
+ if (!isIgnoreRegion(type)) {
+ previousRegion = token;
+ }
+ }
+
+ if (!tagStack.isEmpty()) {
+ while (!tagStack.isEmpty()) {
+ createMissingTagError(tagStack.pop(), true, reporter,
+ tagErrorCount, tagStack, validator, provider);
+ }
+ }
+ }
+
+ private static boolean isIgnoreRegion(String type) {
+ return type == JSONRegionContexts.JSON_COMMENT
+ || type == JSONRegionContexts.WHITE_SPACE
+ || type.equalsIgnoreCase(JSONRegionContexts.UNDEFINED);
+ }
+
+ private static boolean checkExpectedRegion(Token current, Token previous,
+ Stack<Token> tagStack, IReporter reporter, IValidator validator,
+ ISeverityProvider provider) {
+ if (previous == null || isIgnoreRegion(current.type)) {
+ return false;
+ }
+ if (previous.type == JSONRegionContexts.JSON_OBJECT_OPEN) {
+ if (current.type != JSONRegionContexts.JSON_OBJECT_CLOSE
+ && current.type != JSONRegionContexts.JSON_OBJECT_KEY) {
+ String messageText = "Expected object key but found "
+ + current.type;
+ LocalizedMessage message = createMessage(current, messageText,
+ JSONCorePreferenceNames.MISSING_BRACKET, provider);
+ getAnnotationMsg(reporter, ProblemIDsJSON.MissingEndBracket,
+ message, null, current.length, validator);
+ return true;
+ }
+ } else if (previous.type == JSONRegionContexts.JSON_OBJECT_KEY) {
+ if (current.type != JSONRegionContexts.JSON_COLON) {
+ String messageText = "Expected colon but found " + current.type;
+ LocalizedMessage message = createMessage(current, messageText,
+ JSONCorePreferenceNames.MISSING_BRACKET, provider);
+ getAnnotationMsg(reporter, ProblemIDsJSON.MissingEndBracket,
+ message, null, current.length, validator);
+ return true;
+ }
+ } else if (previous.type == JSONRegionContexts.JSON_COLON) {
+ if (!JSONUtil.isJSONSimpleValue(current.type)
+ && !JSONUtil.isStartJSONStructure(current.type)) {
+ String messageText = "Expected JSON value but found "
+ + current.type;
+ LocalizedMessage message = createMessage(current, messageText,
+ JSONCorePreferenceNames.MISSING_BRACKET, provider);
+ getAnnotationMsg(reporter, ProblemIDsJSON.MissingEndBracket,
+ message, null, current.length, validator);
+ return true;
+ }
+ } else if (previous.type == JSONRegionContexts.JSON_COMMA) {
+ if (tagStack.isEmpty()) {
+ String messageText = "Unexpected comma";
+ LocalizedMessage message = createMessage(current, messageText,
+ JSONCorePreferenceNames.MISSING_BRACKET, provider);
+ getAnnotationMsg(reporter, ProblemIDsJSON.MissingEndBracket,
+ message, null, current.length, validator);
+ return true;
+ } else {
+ if (tagStack.peek().type == JSONRegionContexts.JSON_ARRAY_OPEN) {
+ // inside array, previous token must be a JSON value
+ if (!JSONUtil.isJSONSimpleValue(current.type)
+ && current.type != JSONRegionContexts.JSON_ARRAY_OPEN
+ && current.type != JSONRegionContexts.JSON_OBJECT_OPEN) {
+ String messageText = "Expected JSON value but found "
+ + current.type;
+ LocalizedMessage message = createMessage(current,
+ messageText,
+ JSONCorePreferenceNames.MISSING_BRACKET,
+ provider);
+ getAnnotationMsg(reporter,
+ ProblemIDsJSON.MissingEndBracket, message,
+ null, current.length, validator);
+ return true;
+ }
+ } else {
+ // inside object, previous token must be a JSON key
+ if (current.type != JSONRegionContexts.JSON_OBJECT_KEY) {
+ String messageText = "Expected JSON key but found "
+ + current.type;
+ LocalizedMessage message = createMessage(current,
+ messageText,
+ JSONCorePreferenceNames.MISSING_BRACKET,
+ provider);
+ getAnnotationMsg(reporter,
+ ProblemIDsJSON.MissingEndBracket, message,
+ null, current.length, validator);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private static void createMissingTagError(Token token, boolean isStartTag,
+ IReporter reporter, int tagErrorCount, Stack<Token> tagStack,
+ IValidator validator, ISeverityProvider provider) {
+ boolean isArray = (token.type == JSONRegionContexts.JSON_ARRAY_OPEN || token.type == JSONRegionContexts.JSON_ARRAY_CLOSE);
+ Object[] args = { token.text };
+ String messageText = NLS.bind(getMessage(isStartTag, isArray), args);
+
+ LocalizedMessage message = createMessage(token, messageText,
+ JSONCorePreferenceNames.MISSING_BRACKET, provider);
+
+ Object fixInfo = /*
+ * isStartTag ? (Object) getStartEndFixInfo(token.text,
+ * token) :
+ */token.text;
+ getAnnotationMsg(reporter,
+ isStartTag ? ProblemIDsJSON.MissingEndBracket
+ : ProblemIDsJSON.MissingStartBracket, message, fixInfo,
+ token.length, validator);
+
+ if (++tagErrorCount > ERROR_THRESHOLD) {
+ tagStack.clear();
+ }
+ }
+
+ private static LocalizedMessage createMessage(Token token,
+ String messageText, String severityPreference,
+ ISeverityProvider provider) {
+ LocalizedMessage message = new LocalizedMessage(
+ provider.getSeverity(severityPreference), messageText);
+ message.setOffset(token.offset);
+ message.setLength(token.length);
+ message.setLineNo(getLine(token));
+ return message;
+ }
+
+ private static void getAnnotationMsg(IReporter reporter, int problemId,
+ LocalizedMessage message, Object attributeValueText, int len,
+ IValidator validator) {
+ AnnotationMsg annotation = new AnnotationMsg(problemId,
+ attributeValueText, len);
+ message.setAttribute(AnnotationMsg.ID, annotation);
+ reporter.addMessage(validator, message);
+ }
+
+ private static String getMessage(boolean isStartTag, boolean isArray) {
+ if (isArray) {
+ return isStartTag ? JSONCoreMessages.Missing_end_array
+ : JSONCoreMessages.Missing_start_array;
+ }
+ return isStartTag ? JSONCoreMessages.Missing_end_object
+ : JSONCoreMessages.Missing_start_object;
+ }
+
+ /**
+ * Gets the line number for a token
+ *
+ * @param token
+ * the token to find the line of
+ * @return the line in the document where the token can be found
+ */
+ private static int getLine(Token token) {
+ return token.line + 1;
+ }
+
+ /**
+ * Gets the next token from the tokenizer.
+ *
+ * @param tokenizer
+ * the JSON tokenizer for the file being validated
+ * @return the next token type from the tokenizer, or null if it's at the
+ * end of the file
+ */
+ private static String getNextToken(JSONLineTokenizer tokenizer) {
+ String token = null;
+ try {
+ if (!tokenizer.isEOF()) {
+ token = tokenizer.primGetNextToken();
+ }
+ } catch (IOException e) {
+ }
+ return token;
+ }
+}
|