Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'json/bundles/org.eclipse.wst.json.core/src')
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/JSONCorePlugin.java118
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/CleanupProcessorJSON.java88
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/IJSONCleanupStrategy.java108
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/cleanup/JSONCleanupStrategyImpl.java248
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/contenttype/ContentTypeIdForJSON.java40
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONArray.java22
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONBooleanValue.java19
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONDocument.java55
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONModel.java28
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNode.java122
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNullValue.java15
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONNumberValue.java15
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONObject.java19
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONPair.java26
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONStringValue.java15
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONStructure.java23
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/IJSONValue.java19
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/document/JSONException.java17
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/format/FormatProcessorJSON.java116
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/JSONCoreMessages.java57
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/JSONCoreMessages.properties10
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/Logger.java158
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/ByteReader.java110
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/ContentDescriberForJSON.java222
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/contenttype/JSONResourceEncodingDetector.java115
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/ISourceGenerator.java24
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONArrayImpl.java297
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONBooleanValueImpl.java42
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONDocumentImpl.java122
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONGeneratorImpl.java39
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelContext.java285
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelImpl.java869
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelNotifier.java129
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelNotifierImpl.java552
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelParser.java1817
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONModelUpdater.java1757
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNodeImpl.java844
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNullValueImpl.java41
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONNumberValueImpl.java42
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONObjectImpl.java284
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONPairImpl.java202
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONStringValueImpl.java42
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONStructureImpl.java554
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/JSONValueImpl.java73
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionContainer.java437
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionManagementException.java34
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionProxy.java409
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/document/StructuredDocumentRegionUtil.java180
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/download/HttpClientProvider.java213
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/encoding/JSONDocumentCharsetDetector.java68
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/encoding/JSONDocumentLoader.java97
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/AbstractJSONSourceFormatter.java903
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/CompoundRegion.java60
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/DefaultJSONSourceFormatter.java63
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/FormatRegion.java48
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/IJSONSourceFormatter.java27
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONArrayFormatter.java199
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONDocumentFormatter.java52
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONFormatUtil.java132
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONObjectFormatter.java79
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONPairFormatter.java135
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONSourceFormatterFactory.java49
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/JSONStructureFormatter.java21
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/format/UnknownRuleFormatter.java79
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/modelhandler/JSONModelLoader.java46
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/modelhandler/ModelHandlerForJSON.java61
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/AbstractJSONTokenizer.java68
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/IJSONLineTokenizer.java16
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/IJSONTokenizer.java33
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONLineTokenizer.java968
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONSourceParser.java260
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONStructuredDocumentRegionFactory.java29
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/JSONTokenizer.java916
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/parser/regions/JSONTextRegionFactory.java40
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/preferences/JSONCorePreferenceInitializer.java86
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/SchemaProcessorRegistryReader.java163
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/Catalog.java588
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogContributorRegistryReader.java277
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogElement.java174
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogEntry.java65
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogEvent.java42
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogSchemastoreReader.java128
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogSet.java73
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/CatalogUserCatalogReader.java42
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/EntryParser.java138
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/JSONCatalogURIResolverExtension.java58
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/NextCatalog.java60
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/SchemaStoreCatalogConstants.java31
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/UserEntries.java26
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/catalog/UserEntry.java45
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/text/JSONStructuredDocumentReParser.java439
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/text/StructuredTextPartitionerForJSON.java58
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/util/RegionIterator.java142
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONNestedValidatorContext.java53
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONSyntaxValidator.java257
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationConfiguration.java15
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationInfo.java51
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidationReport.java30
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/JSONValidator.java312
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/ProblemIDsJSON.java18
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/AbstractNestedValidator.java544
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/NestedValidatorContext.java34
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationInfo.java256
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationMessage.java203
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/core/ValidationReport.java47
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/JSONMessageInfoHelper.java120
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/JSONValidator.java45
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/Validator.java946
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/jsonpath/JSONPathMatcher.java57
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/modelhandler/IIModelHandlerForJSON.java20
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/preferences/JSONCorePreferenceNames.java173
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/regions/JSONRegionContexts.java41
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalog.java171
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogElement.java98
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogEntry.java67
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogEvent.java55
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ICatalogListener.java35
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/IDelegateCatalog.java73
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/INextCatalog.java54
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/IRewriteEntry.java65
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/schema/catalog/ISuffixEntry.java65
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/text/IJSONPartitions.java22
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/util/JSONUtil.java305
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/AnnotationMsg.java45
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/ISeverityProvider.java17
-rw-r--r--json/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/validation/JSONSyntaxValidatorHelper.java322
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;
+ }
+}

Back to the top