Bug 494111 -  Validate JSON file with JSON Schema

Change-Id: I51491c77a6f270c17f6df3b84f47e5ccb7ea13bf
Signed-off-by: Snjezana Peco <snjezana.peco@redhat.com>
Signed-off-by: Victor Rubezhny <vrubezhny@redhat.com>
diff --git a/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaDocument.java b/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaDocument.java
index 5e70c98..bd8d689 100644
--- a/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaDocument.java
+++ b/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaDocument.java
@@ -13,10 +13,6 @@
 import java.io.IOException;

 import java.io.Reader;

 import java.util.Collection;

-import java.util.HashMap;

-import java.util.Iterator;

-import java.util.List;

-import java.util.Map;

 

 import org.eclipse.json.IValidationReporter;

 import org.eclipse.json.jsonpath.IJSONPath;

@@ -24,77 +20,13 @@
 import org.eclipse.json.provisonnal.com.eclipsesource.json.JsonValue;

 import org.eclipse.json.schema.IJSONSchemaDocument;

 import org.eclipse.json.schema.IJSONSchemaProperty;

-import org.eclipse.json.schema.JSONSchemaType;

 

 @SuppressWarnings("serial")

 public class JSONSchemaDocument extends JSONSchemaNode implements

 		IJSONSchemaDocument {

-	private static final String DEFINITIONS = "#/definitions/"; //$NON-NLS-1$

-	private final Map<String, IJSONSchemaProperty> definitions;

 

 	public JSONSchemaDocument(Reader reader) throws IOException {

 		super(JsonObject.readFrom(reader), null);

-		this.definitions = new HashMap<String, IJSONSchemaProperty>();

-		addDefinitions(getJsonObject());

-		resolveReferences();

-	}

-

-	private void resolveReferences() {

-		resolveReference(this);

-		for (IJSONSchemaProperty definition : definitions.values()) {

-			resolveReference(definition);

-		}

-		Collection<IJSONSchemaProperty> props = getProperties().values();

-		for (IJSONSchemaProperty property:props) {

-			resolveReference(property);

-		}

-	}

-

-	private void resolveReference(IJSONSchemaProperty node) {

-		String reference = node.getReference();

-		if (reference != null) {

-			String ref = reference.substring(DEFINITIONS.length());

-			IJSONSchemaProperty property = definitions.get(ref);

-			if (property != null) {

-				for (IJSONSchemaProperty p : property.getProperties().values()) {

-					node.addProperty(p);

-				}

-				Collection<IJSONSchemaProperty> props = property.getProperties().values();

-				for (IJSONSchemaProperty p : props) {

-					resolveReference(p);

-				}

-			}

-		}

-		Collection<IJSONSchemaProperty> props = node.getProperties().values();

-		for (IJSONSchemaProperty p:props) {

-			resolveReference(p);

-		}

-		List<String> references = node.getReferences();

-		for (String ref:references) {

-			String r = ref.substring(DEFINITIONS.length());

-			IJSONSchemaProperty property = definitions.get(r);

-			if (property != null) {

-				for (IJSONSchemaProperty p:property.getProperties().values()) {

-					node.addProperty(p);

-				}

-			}

-		}

-	}

-

-	private void addDefinitions(JsonObject json) {

-		Member member = null;

-		JsonObject definitions = (JsonObject) json.get("definitions");

-		if (definitions != null) {

-			Iterator<Member> members = definitions.iterator();

-			while (members.hasNext()) {

-				member = members.next();

-				addDefinition(new JSONSchemaProperty(member.getName(),

-						(JsonObject) member.getValue(), this));

-			}

-		}

-	}

-	private void addDefinition(IJSONSchemaProperty property) {

-		definitions.put(property.getName(), property);

 	}

 

 	@Override

@@ -128,39 +60,14 @@
 		return null;

 	}

 

-	@Override

-	public String getName() {

-		return null;

-	}

-

-	@Override

-	public String getDescription() {

-		return null;

-	}

-

-	@Override

-	public JSONSchemaType[] getType() {

-		return null;

-	}

-

-	@Override

-	public JSONSchemaType getFirstType() {

-		return null;

-	}

-

 	public void validate(JsonValue value, IValidationReporter reporter) {

 		// TODO Auto-generated method stub

 

 	}

 

 	@Override

-	public List<String> getEnumList() {

-		return null;

-	}

-

-	@Override

-	public String getDefaultValue() {

-		return null;

+	public String getName() {

+		return ""; //$NON-NLS-1$

 	}

 

 }

diff --git a/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaNode.java b/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaNode.java
index 92d85f2..8f02638 100644
--- a/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaNode.java
+++ b/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaNode.java
@@ -1,5 +1,5 @@
 /**

- *  Copyright (c) 2013-2014 Angelo ZERR.

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

@@ -13,33 +13,133 @@
 import java.util.ArrayList;

 import java.util.HashMap;

 import java.util.Iterator;

+import java.util.LinkedList;

 import java.util.List;

 import java.util.Map;

 

 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.json.schema.IJSONSchemaDocument;

 import org.eclipse.json.schema.IJSONSchemaNode;

 import org.eclipse.json.schema.IJSONSchemaProperty;

+import org.eclipse.json.schema.JSONSchemaType;

 

 @SuppressWarnings("serial")

 public class JSONSchemaNode extends JsonObject implements IJSONSchemaNode {

 

-	private static final String PROPERTIES = "properties"; //$NON-NLS-1$

-	private static final String REF = "$ref"; //$NON-NLS-1$

+	private Map<String, JsonValue> definitions;

 	private final IJSONSchemaNode parent;

 	private final Map<String, IJSONSchemaProperty> properties;

-	private final String reference;

-	private final List<String> references;

 	private JsonObject jsonObject;

+	protected final String description;

+	protected JSONSchemaType[] type;

+	protected String defaultValue;

+	private List<String> enumList;

 

 	public JSONSchemaNode(JsonObject jsonObject, IJSONSchemaNode parent) {

 		this.parent = parent;

 		this.jsonObject = jsonObject;

+		if (this instanceof IJSONSchemaDocument) {

+			definitions = new HashMap<String, JsonValue>();

+			addDefinitions();

+			resolveReferences(jsonObject);

+		}

 		this.properties = new HashMap<String, IJSONSchemaProperty>();

-		this.references = new ArrayList<String>();

-		this.reference = jsonObject.getString(REF, null);

-		walk(jsonObject, this, true);

+		walk(jsonObject, this);

+		this.description = jsonObject.getString("description", null); //$NON-NLS-1$

+		this.type = getType(jsonObject.get(TYPE));

+		JsonValue value = jsonObject.get("default"); //$NON-NLS-1$

+		if (value != null) {

+			defaultValue = removeQuote(value);

+		}

+		value = jsonObject.get(ENUM);

+		if (value instanceof JsonArray) {

+			JsonArray array = (JsonArray) value;

+			List<JsonValue> items = array.values();

+			for (JsonValue v:items) {

+				if (v != null) {

+					if (type == null || type.length == 0) {

+						String str = v.toString();

+						if (str.startsWith(QUOTE) && str.endsWith(QUOTE)) {

+							type = new JSONSchemaType[] {JSONSchemaType.String};

+						}

+					}

+					addEnum(removeQuote(v));

+				}

+			}

+		}

+	}

+

+	private void resolveReferences(JsonObject json) {

+		Iterator<Member> members = json.iterator();

+		while (members.hasNext()) {

+			Member member = members.next();

+			JsonValue value = member.getValue();

+			resolveReferences(json, member.getName(), value);

+		}

+	}

+

+	private void resolveReferences(JsonObject parent, String name, JsonValue value) {

+		if (value instanceof JsonObject) {

+			JsonObject json = value.asObject();

+			String ref = json.getString(REF, null);

+			if (ref != null && ref.startsWith(DEFINITIONS)) {

+				String r = ref.substring(DEFINITIONS.length());

+				JsonValue v = definitions.get(r);

+				parent.set(name, v);

+				//json.remove(REF);

+			} else {

+				Iterator<Member> members = json.iterator();

+				while (members.hasNext()) {

+					Member member = members.next();

+					JsonValue v = member.getValue();

+					resolveReferences(json, member.getName(), v);

+				}

+			}

+		} else if (value instanceof JsonArray) {

+			JsonArray jsonArray = (JsonArray) value;

+			for (int i = 0; i < jsonArray.size(); i++) {

+				JsonValue item = jsonArray.get(i);

+				if (item instanceof JsonObject) {

+					JsonObject json = item.asObject();

+					String ref = json.getString(REF, null);

+					if (ref != null && ref.startsWith(DEFINITIONS)) {

+						String r = ref.substring(DEFINITIONS.length());

+						JsonValue v = definitions.get(r);

+						jsonArray.set(i, v);

+					} else {

+						resolveReferences(json);

+					}

+				}

+			}

+		}

+	}

+

+	private void addDefinitions() {

+		JsonValue defs = jsonObject.get("definitions"); //$NON-NLS-1$

+		if (defs instanceof JsonObject) {

+			Iterator<Member> members = ((JsonObject) defs).iterator();

+			while (members.hasNext()) {

+				Member member = members.next();

+				JsonValue value = member.getValue();

+				if (value instanceof JsonObject) {

+					definitions.put(member.getName(), member.getValue());

+				}

+			}

+		}

+	}

+

+	private void walk(JsonObject json, IJSONSchemaNode schemaNode) {

+		JsonObject properties = (JsonObject) json.get(PROPERTIES);

+		addProperties(schemaNode, properties);

+		add(json, schemaNode, ALL_OF);

+		add(json, schemaNode, ANY_OF);

+		add(json, schemaNode, ONE_OF);

+		JsonValue notMember = json.get(NOT);

+		if (notMember != null) {

+			walk(notMember.asObject(), schemaNode);

+		}

 	}

 

 	private void add(JsonObject jsonObject, IJSONSchemaNode schemaNode, String pref) {

@@ -50,59 +150,21 @@
 			while (iter.hasNext()) {

 				JsonValue value = iter.next();

 				if (value != null) {

-					String ref = value.asObject().getString(REF, null);

-					if (ref != null) {

-						references.add(ref);

-					} else {

-						walk(value.asObject(), schemaNode, true);

-					}

+					walk(value.asObject(), schemaNode);

 				}

 			}

 		}

 	}

 

-	private void walk(JsonObject json, IJSONSchemaNode schemaNode, boolean add) {

-		JsonObject properties = (JsonObject) json.get(PROPERTIES);

-		addProperties(schemaNode, properties, add);

-		if (properties == null) {

-			JsonObject items = (JsonObject) json.get("items"); //$NON-NLS-1$

-			if (items != null) {

-				properties = (JsonObject) items.get(PROPERTIES);

-				addProperties(schemaNode, properties, add);

-				String ref = items.getString(REF, null);

-				if (ref != null) {

-					if (add) {

-						schemaNode.getReferences().add(ref);

-					} else {

-						schemaNode.getReferences().remove(ref);

-					}

-				} else {

-					walk(items, schemaNode, add);

-				}

-			}

-		}

-		add(json, schemaNode, "allOf"); //$NON-NLS-1$

-		add(json, schemaNode, "anyOf"); //$NON-NLS-1$

-		add(json, schemaNode, "oneOf"); //$NON-NLS-1$

-		JsonValue notMember = json.get("not"); //$NON-NLS-1$

-		if (notMember != null) {

-			walk(notMember.asObject(), schemaNode, false);

-		}

-	}

-

-	private void addProperties(IJSONSchemaNode schemaNode, JsonObject properties, boolean add) {

+	private void addProperties(IJSONSchemaNode schemaNode, JsonObject properties) {

 		if (properties == null) {

 			return;

 		}

 		Iterator<Member> members = properties.iterator();

 		while (members.hasNext()) {

 			Member member = members.next();

-			if (add) {

-				schemaNode.addProperty(

-						new JSONSchemaProperty(member.getName(), (JsonObject) member.getValue(), schemaNode));

-			} else {

-				schemaNode.getProperties().remove(member.getName());

-			}

+			schemaNode

+					.addProperty(new JSONSchemaProperty(member.getName(), (JsonObject) member.getValue(), schemaNode));

 		}

 	}

 

@@ -132,17 +194,112 @@
 	}

 

 	@Override

-	public String getReference() {

-		return reference;

-	}

-

-	@Override

-	public List<String> getReferences() {

-		return references;

-	}

-

-	@Override

 	public Map<String, IJSONSchemaProperty> getProperties() {

 		return properties;

 	}

+

+	protected String[] getRequired(JsonValue value) {

+		if (value == null) {

+			return null;

+		}

+		List<String> names = new ArrayList<String>();

+		if (value.isString()) {

+			String s = value.asString();

+			names.add(s);

+		} else if (value.isArray()) {

+			JsonArray array = (JsonArray) value;

+			for (JsonValue item : array) {

+				if (item.isString()) {

+					String s = item.asString();

+					names.add(s);

+				}

+			}

+		}

+		return names.toArray(new String[0]);

+	}

+

+	protected String removeQuote(JsonValue value) {

+		String str = value.toString();

+		if (str.startsWith(QUOTE)) {

+			str = str.substring(1);

+		}

+		if (str.endsWith(QUOTE)) {

+			str = str.substring(0, str.length()-1);

+		}

+		return str;

+	}

+

+	public static JSONSchemaType[] getType(JsonValue value) {

+		if (value == null) {

+			return JSONSchemaType.EMPTY_TYPES;

+		}

+		JSONSchemaType t = null;

+		List<JSONSchemaType> types = new ArrayList<JSONSchemaType>();

+		if (value.isString()) {

+			t = JSONSchemaType.getType(value.asString());

+			if (t != null) {

+				types.add(t);

+			}

+		} else if (value.isArray()) {

+			JsonArray array = (JsonArray) value;

+			for (JsonValue item : array) {

+				t = JSONSchemaType.getType(item.asString());

+				if (t != null) {

+					types.add(t);

+				}

+			}

+		}

+		return types.toArray(JSONSchemaType.EMPTY_TYPES);

+	}

+

+	@Override

+	public String getDescription() {

+		return description;

+	}

+

+	@Override

+	public JSONSchemaType[] getType() {

+		return type;

+	}

+

+	@Override

+	public JSONSchemaType getFirstType() {

+		if (type == null) {

+			return null;

+		}

+		if (type.length == 0) {

+			return null;

+		}

+		return type[0];

+	}

+

+	@Override

+	public String getDefaultValue() {

+		return defaultValue;

+	}

+

+	@Override

+	public List<String> getEnumList() {

+		return enumList;

+	}

+

+	public void addEnum(String item) {

+		if (enumList == null) {

+			enumList = new LinkedList<String>();

+		}

+		this.enumList.add(item);

+	}

+

+	@Override

+	public IJSONSchemaDocument getSchemaDocument() {

+		if (this instanceof IJSONSchemaDocument) {

+			return (IJSONSchemaDocument) this;

+		}

+		IJSONSchemaNode p = getParent();

+		if (p != null) {

+			return p.getSchemaDocument();

+		}

+		return null;

+	}

+

 }

diff --git a/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaProperty.java b/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaProperty.java
index 330f8880..a32da70 100644
--- a/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaProperty.java
+++ b/bundles/org.eclipse.json/src/org/eclipse/json/impl/schema/JSONSchemaProperty.java
@@ -10,88 +10,19 @@
  */

 package org.eclipse.json.impl.schema;

 

-import java.util.ArrayList;

-import java.util.LinkedList;

-import java.util.List;

-

-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.json.schema.IJSONSchemaNode;

 import org.eclipse.json.schema.IJSONSchemaProperty;

-import org.eclipse.json.schema.JSONSchemaType;

 

 @SuppressWarnings("serial")

 public class JSONSchemaProperty extends JSONSchemaNode implements

 		IJSONSchemaProperty {

 

-	private static final String QUOTE = "\""; //$NON-NLS-1$

-	private final String name;

-	private final String description;

-	private JSONSchemaType[] type;

-	private String defaultValue;

-	private List<String> enumList;

-	

+	protected final String name;

 	public JSONSchemaProperty(String name, JsonObject jsonObject,

 			IJSONSchemaNode parent) {

 		super(jsonObject, parent);

 		this.name = name;

-		this.description = jsonObject.getString("description", null); //$NON-NLS-1$

-		this.type = getType(jsonObject.get("type")); //$NON-NLS-1$

-		JsonValue value = jsonObject.get("default"); //$NON-NLS-1$

-		if (value != null) {

-			defaultValue = removeQuote(value);

-		}

-		value = jsonObject.get("enum"); //$NON-NLS-1$

-		if (value instanceof JsonArray) {

-			JsonArray array = (JsonArray) value;

-			List<JsonValue> items = array.values();

-			for (JsonValue v:items) {

-				if (v != null) {

-					if (type == null || type.length == 0) {

-						String str = v.toString();

-						if (str.startsWith(QUOTE) && str.endsWith(QUOTE)) {

-							type = new JSONSchemaType[] {JSONSchemaType.String};

-						}

-					}

-					addEnum(removeQuote(v));

-				}

-			}

-		}

-	}

-

-	private String removeQuote(JsonValue value) {

-		String str = value.toString();

-		if (str.startsWith(QUOTE)) {

-			str = str.substring(1);

-		}

-		if (str.endsWith(QUOTE)) {

-			str = str.substring(0, str.length()-1);

-		}

-		return str;

-	}

-

-	private JSONSchemaType[] getType(JsonValue value) {

-		if (value == null) {

-			return JSONSchemaType.EMPTY_TYPES;

-		}

-		JSONSchemaType t = null;

-		List<JSONSchemaType> types = new ArrayList<JSONSchemaType>();

-		if (value.isString()) {

-			t = JSONSchemaType.getType(value.asString());

-			if (t != null) {

-				types.add(t);

-			}

-		} else if (value.isArray()) {

-			JsonArray array = (JsonArray) value;

-			for (JsonValue item : array) {

-				t = JSONSchemaType.getType(item.asString());

-				if (t != null) {

-					types.add(t);

-				}

-			}

-		}

-		return types.toArray(JSONSchemaType.EMPTY_TYPES);

 	}

 

 	@Override

@@ -99,39 +30,4 @@
 		return name;

 	}

 

-	@Override

-	public String getDescription() {

-		return description;

-	}

-

-	@Override

-	public JSONSchemaType[] getType() {

-		return type;

-	}

-

-	@Override

-	public JSONSchemaType getFirstType() {

-		if (type == null) {

-			return null;

-		}

-		if (type.length == 0) {

-			return null;

-		}

-		return type[0];

-	}

-

-	public String getDefaultValue() {

-		return defaultValue;

-	}

-

-	public List<String> getEnumList() {

-		return enumList;

-	}

-

-	public void addEnum(String item) {

-		if (enumList == null) {

-			enumList = new LinkedList<String>();

-		}

-		this.enumList.add(item);

-	}

 }

diff --git a/bundles/org.eclipse.json/src/org/eclipse/json/schema/IJSONSchemaNode.java b/bundles/org.eclipse.json/src/org/eclipse/json/schema/IJSONSchemaNode.java
index edd22cf..040c669 100644
--- a/bundles/org.eclipse.json/src/org/eclipse/json/schema/IJSONSchemaNode.java
+++ b/bundles/org.eclipse.json/src/org/eclipse/json/schema/IJSONSchemaNode.java
@@ -1,5 +1,5 @@
 /**

- *  Copyright (c) 2013-2014 Angelo ZERR.

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

@@ -17,19 +17,56 @@
 

 public interface IJSONSchemaNode {

 

+	static final String NOT = "not"; //$NON-NLS-1$

+	static final String ONE_OF = "oneOf"; //$NON-NLS-1$

+	static final String ANY_OF = "anyOf"; //$NON-NLS-1$

+	static final String ALL_OF = "allOf"; //$NON-NLS-1$

+	static final String TYPE = "type"; //$NON-NLS-1$

+	static final String REQUIRED = "required"; //$NON-NLS-1$

+	static final String MIN_PROPERTIES = "minProperties"; //$NON-NLS-1$

+	static final String MAX_PROPERTIES = "maxProperties"; //$NON-NLS-1$

+	static final String PATTERN = "pattern"; //$NON-NLS-1$

+	static final String MIN_LENGTH = "minLength"; //$NON-NLS-1$

+	static final String MAX_LENGTH = "maxLength"; //$NON-NLS-1$

+	static final String MULTIPLEOF = "multipleOf"; //$NON-NLS-1$

+	static final String MAXIMUM = "maximum"; //$NON-NLS-1$

+	static final String EXCLUSIVE_MAXIMUM = "exclusiveMaximum"; //$NON-NLS-1$

+	static final String MINIMUM = "minimum"; //$NON-NLS-1$

+	static final String EXCLUSIVE_MINIMUM = "exclusiveMinimum"; //$NON-NLS-1$

+	static final String MIN_ITEMS = "minItems"; //$NON-NLS-1$

+	static final String MAX_ITEMS = "maxItems"; //$NON-NLS-1$

+	static final String UNIQUE_ITEMS = "uniqueItems"; //$NON-NLS-1$

+	static final String ADDITIONAL_ITEMS = "additionalItems"; //$NON-NLS-1$

+	static final String ITEMS = "items"; //$NON-NLS-1$

+	static final String ADDITIONAL_PROPERTIES = "additionalProperties"; //$NON-NLS-1$

+	static final String PATTERN_PROPERTIES = "patternProperties"; //$NON-NLS-1$

+	static final String ENUM = "enum"; //$NON-NLS-1$

+	String PROPERTIES = "properties"; //$NON-NLS-1$

+	String REF = "$ref"; //$NON-NLS-1$

+	String QUOTE = "\""; //$NON-NLS-1$

+	String DEFINITIONS = "#/definitions/"; //$NON-NLS-1$

+

+	String getDescription();

+

+	JSONSchemaType[] getType();

+

+	JSONSchemaType getFirstType();

+

+	List<String> getEnumList();

+

+	String getDefaultValue();

+

 	IJSONSchemaNode getParent();

 

 	IJSONSchemaProperty[] getPropertyValues();

 

-	List<String> getReferences();

-

 	Map<String, IJSONSchemaProperty> getProperties();

 

-	String getReference();

-

 	void setJsonObject(JsonObject jsonObject);

 

 	JsonObject getJsonObject();

 

 	void addProperty(IJSONSchemaProperty property);

+

+	IJSONSchemaDocument getSchemaDocument();

 }

diff --git a/bundles/org.eclipse.json/src/org/eclipse/json/schema/IJSONSchemaProperty.java b/bundles/org.eclipse.json/src/org/eclipse/json/schema/IJSONSchemaProperty.java
index dffaa6c..4ec26dd 100644
--- a/bundles/org.eclipse.json/src/org/eclipse/json/schema/IJSONSchemaProperty.java
+++ b/bundles/org.eclipse.json/src/org/eclipse/json/schema/IJSONSchemaProperty.java
@@ -10,22 +10,10 @@
  */

 package org.eclipse.json.schema;

 

-import java.util.List;

-

 public interface IJSONSchemaProperty extends IJSONSchemaNode {

 

 	IJSONSchemaProperty[] EMPTY_PROPERTY = new IJSONSchemaProperty[0];

-

+	

 	String getName();

 

-	String getDescription();

-

-	JSONSchemaType[] getType();

-

-	JSONSchemaType getFirstType();

-	

-	List<String> getEnumList();

-	

-	public String getDefaultValue();

-

 }

diff --git a/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/preferences/JSONCorePreferenceInitializer.java b/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/preferences/JSONCorePreferenceInitializer.java
index 3c38a36..4144811 100644
--- a/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/preferences/JSONCorePreferenceInitializer.java
+++ b/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/preferences/JSONCorePreferenceInitializer.java
@@ -30,6 +30,7 @@
 
 		// Validation preferences
 		node.putBoolean(JSONCorePreferenceNames.SYNTAX_VALIDATION, false);
+		node.putBoolean(JSONCorePreferenceNames.SCHEMA_VALIDATION, false);
 		node.putInt(JSONCorePreferenceNames.MISSING_BRACKET, 2);
 		
 		// formatting preferences
diff --git a/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/SchemaProcessorRegistryReader.java b/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/SchemaProcessorRegistryReader.java
index c350eb1..fdaec89 100644
--- a/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/SchemaProcessorRegistryReader.java
+++ b/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/schema/SchemaProcessorRegistryReader.java
@@ -1,5 +1,5 @@
 /**

- *  Copyright (c) 2013-2014 Angelo ZERR.

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

@@ -11,6 +11,8 @@
 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;

@@ -22,9 +24,13 @@
 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 {

 

@@ -58,6 +64,30 @@
 		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();

 		/**

diff --git a/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/Validator.java b/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/Validator.java
index f200af1..f16f034 100644
--- a/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/Validator.java
+++ b/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/internal/validation/eclipse/Validator.java
@@ -12,30 +12,73 @@
  *******************************************************************************/

 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;

@@ -45,81 +88,786 @@
 		super.setupValidation(context);

 		fPreferenceScopes = createPreferenceScopes(context);

 		indicateNoGrammar = Platform.getPreferencesService().getInt(

-				JSONCorePlugin.getDefault().getBundle().getSymbolicName(),

-				JSONCorePreferenceNames.INDICATE_NO_GRAMMAR, 0,

-				fPreferenceScopes);

+				JSONCorePlugin.getDefault().getBundle().getSymbolicName(), JSONCorePreferenceNames.INDICATE_NO_GRAMMAR,

+				0, fPreferenceScopes);

 	}

 

-	protected IScopeContext[] createPreferenceScopes(

-			NestedValidatorContext context) {

+	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() };

+				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) {

+	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) {

+	public ValidationReport validate(String uri, InputStream inputstream, NestedValidatorContext context,

+			ValidationResult result) {

 		JSONValidator validator = JSONValidator.getInstance();

-

 		JSONValidationConfiguration configuration = new JSONValidationConfiguration();

-		// try {

-		// // Preferences pluginPreferences =

-		// // JSONCorePlugin.getDefault().getPluginPreferences();

-		// configuration.setFeature(

-		// JSONValidationConfiguration.INDICATE_NO_GRAMMAR,

-		// indicateNoGrammar);

-		// final IPreferencesService preferencesService = Platform

-		// .getPreferencesService();

-		// configuration

-		// .setFeature(

-		// JSONValidationConfiguration.INDICATE_NO_DOCUMENT_ELEMENT,

-		// preferencesService

-		// .getInt(JSONCorePlugin.getDefault()

-		// .getBundle().getSymbolicName(),

-		// JSONCorePreferenceNames.INDICATE_NO_DOCUMENT_ELEMENT,

-		// -1, fPreferenceScopes));

-		// configuration.setFeature(JSONValidationConfiguration.USE_XINCLUDE,

-		// preferencesService.getBoolean(JSONCorePlugin.getDefault()

-		// .getBundle().getSymbolicName(),

-		// JSONCorePreferenceNames.USE_XINCLUDE, false,

-		// fPreferenceScopes));

-		// configuration

-		// .setFeature(

-		// JSONValidationConfiguration.HONOUR_ALL_SCHEMA_LOCATIONS,

-		// preferencesService

-		// .getBoolean(

-		// JSONCorePlugin.getDefault()

-		// .getBundle()

-		// .getSymbolicName(),

-		// JSONCorePreferenceNames.HONOUR_ALL_SCHEMA_LOCATIONS,

-		// true, fPreferenceScopes));

-		// } catch (Exception e) {

-		// // TODO: Unable to set the preference. Log this problem.

-		// }

-

-		JSONValidationReport valreport = validator.validate(uri, inputstream,

-				configuration, result, context);

-

+		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:

@@ -131,21 +879,19 @@
 	 *      org.eclipse.wst.validation.internal.provisional.core.IMessage)

 	 */

 	@Override

-	protected void addInfoToMessage(ValidationMessage validationMessage,

-			IMessage message) {

+	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());

+			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]);

-					*/

+			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]);

+			 */

 		}

 	}

 

@@ -159,8 +905,7 @@
 	 * @return the nested validation context.

 	 */

 	@Override

-	protected NestedValidatorContext getNestedContext(ValidationState state,

-			boolean create) {

+	protected NestedValidatorContext getNestedContext(ValidationState state, boolean create) {

 		NestedValidatorContext context = null;

 		Object o = state.get(JSON_VALIDATOR_CONTEXT);

 		if (o instanceof JSONNestedValidatorContext)

@@ -172,8 +917,7 @@
 	}

 

 	@Override

-	public void validationStarting(IProject project, ValidationState state,

-			IProgressMonitor monitor) {

+	public void validationStarting(IProject project, ValidationState state, IProgressMonitor monitor) {

 		if (project != null) {

 			NestedValidatorContext context = getNestedContext(state, false);

 			if (context == null) {

@@ -188,8 +932,7 @@
 	}

 

 	@Override

-	public void validationFinishing(IProject project, ValidationState state,

-			IProgressMonitor monitor) {

+	public void validationFinishing(IProject project, ValidationState state, IProgressMonitor monitor) {

 		if (project != null) {

 			super.validationFinishing(project, state, monitor);

 			NestedValidatorContext context = getNestedContext(state, false);

diff --git a/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/preferences/JSONCorePreferenceNames.java b/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/preferences/JSONCorePreferenceNames.java
index 79546b5..f3a8db1 100644
--- a/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/preferences/JSONCorePreferenceNames.java
+++ b/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/preferences/JSONCorePreferenceNames.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2005, 2011 IBM Corporation and others.
+ * 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
@@ -32,6 +32,7 @@
 	 * </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
diff --git a/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/util/JSONUtil.java b/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/util/JSONUtil.java
index 759a0ba..266a306 100644
--- a/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/util/JSONUtil.java
+++ b/bundles/org.eclipse.wst.json.core/src/org/eclipse/wst/json/core/util/JSONUtil.java
@@ -1,5 +1,5 @@
 /**
- *  Copyright (c) 2013-2014 Angelo ZERR.
+ *  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
@@ -14,6 +14,8 @@
 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;
@@ -23,6 +25,8 @@
 
 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$
 	}
@@ -273,4 +277,29 @@
 	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/bundles/org.eclipse.wst.json.schemaprocessor/src/org/eclipse/wst/json/schemaprocessor/internal/JSONSchemaProcessor.java b/bundles/org.eclipse.wst.json.schemaprocessor/src/org/eclipse/wst/json/schemaprocessor/internal/JSONSchemaProcessor.java
index 20adfb1..b55ea39 100644
--- a/bundles/org.eclipse.wst.json.schemaprocessor/src/org/eclipse/wst/json/schemaprocessor/internal/JSONSchemaProcessor.java
+++ b/bundles/org.eclipse.wst.json.schemaprocessor/src/org/eclipse/wst/json/schemaprocessor/internal/JSONSchemaProcessor.java
@@ -47,15 +47,21 @@
 				is = url.openStream();

 			} else {

 				File f = HttpClientProvider.getFile(url);

-				is = new FileInputStream(f);

+				if (f != null) {

+					is = new FileInputStream(f);

+				}

 			}

-			schemaDocument = new JSONSchemaDocument(new InputStreamReader(is));

+			if (is != null) {

+				schemaDocument = new JSONSchemaDocument(new InputStreamReader(is));

+			}

 		} finally {

 			if (is != null) {

 				is.close();

 			}

 		}

-		schemaDocuments.put(uriString, schemaDocument);

+		if (schemaDocument != null) {

+			schemaDocuments.put(uriString, schemaDocument);

+		}

 		return schemaDocument;

 	}

 	

diff --git a/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/JSONUIMessages.java b/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/JSONUIMessages.java
index c8ab168..8208501 100644
--- a/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/JSONUIMessages.java
+++ b/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/JSONUIMessages.java
@@ -23,6 +23,8 @@
 

 	private static final String BUNDLE_NAME = "org.eclipse.wst.json.ui.internal.JSONUIMessages";//$NON-NLS-1$

 

+	public static String EnableSchemaValidation;

+

 	public static String Invalid_URL;

 	public static String The_name_field_is_required;

 	public static String The_entry_already_exists;

diff --git a/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/JSONUIMessages.properties b/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/JSONUIMessages.properties
index f6ca05f..56d9dbb 100644
--- a/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/JSONUIMessages.properties
+++ b/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/JSONUIMessages.properties
@@ -25,6 +25,7 @@
 

 ## Validation preferences page

 SyntaxValidation_files=&Enable syntax validation

+EnableSchemaValidation=Enable schema validation

 SyntaxValidation_files_label=Errors/Warnings

 Severity_error=Error

 Severity_warning=Warning

diff --git a/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/preferences/JSONValidatorPreferencePage.java b/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/preferences/JSONValidatorPreferencePage.java
index 9b99574..18855f4 100644
--- a/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/preferences/JSONValidatorPreferencePage.java
+++ b/bundles/org.eclipse.wst.json.ui/src/org/eclipse/wst/json/ui/internal/preferences/JSONValidatorPreferencePage.java
@@ -19,6 +19,7 @@
 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.jface.dialogs.ControlEnableState;
 import org.eclipse.jface.dialogs.IDialogSettings;
 import org.eclipse.swt.SWT;
@@ -38,6 +39,7 @@
 import org.eclipse.wst.json.ui.internal.JSONUIPlugin;
 import org.eclipse.wst.sse.core.internal.validate.ValidationMessage;
 import org.eclipse.wst.sse.ui.internal.preferences.ui.AbstractValidationSettingsPage;
+import org.osgi.service.prefs.BackingStoreException;
 
 public class JSONValidatorPreferencePage extends AbstractValidationSettingsPage {
 	private static final String SETTINGS_SECTION_NAME = "JSONValidationSeverities";//$NON-NLS-1$
@@ -75,6 +77,10 @@
 	private Group fSyntaxValidationGroup;
 	private ControlEnableState fSyntaxState;
 
+	private boolean fOriginalUseSchemaValidation;
+
+	private Button fSchemaValidation;
+
 	private static final int[] JSON_SEVERITIES = { ValidationMessage.WARNING,
 			ValidationMessage.ERROR, ValidationMessage.IGNORE };
 
@@ -122,6 +128,13 @@
 			}
 		});
 
+		fOriginalUseSchemaValidation = getBooleanPreference(
+				JSONCorePreferenceNames.SCHEMA_VALIDATION, false, contexts);
+		fSchemaValidation = createCheckBox(parent,
+				JSONUIMessages.EnableSchemaValidation);
+		((GridData) fSchemaValidation.getLayoutData()).horizontalSpan = 2;
+		fSchemaValidation
+				.setSelection(fOriginalUseSchemaValidation);
 		fSyntaxValidationGroup = createGroup(parent, 3);
 		((GridLayout) fSyntaxValidationGroup.getLayout()).makeColumnsEqualWidth = false;
 		fSyntaxValidationGroup
@@ -219,6 +232,12 @@
 		boolean useExtendedSyntaxValidation = modelPreferences.getBoolean(
 				JSONCorePreferenceNames.SYNTAX_VALIDATION, false);
 
+		boolean useSchemaValidation = modelPreferences.getBoolean(
+				JSONCorePreferenceNames.SCHEMA_VALIDATION, false);
+		if (fSchemaValidation != null) {
+			fSchemaValidation.setSelection(useSchemaValidation);
+		}
+
 		if (fExtendedSyntaxValidation != null) {
 			if (fExtendedSyntaxValidation.getSelection() != useExtendedSyntaxValidation) {
 				handleSyntaxSeveritySelection(useExtendedSyntaxValidation);
@@ -251,6 +270,11 @@
 					JSONCorePreferenceNames.SYNTAX_VALIDATION,
 					extendedSyntaxValidation);
 		}
+		if (fSchemaValidation != null) {
+			contexts[0].getNode(getPreferenceNodeQualifier()).putBoolean(
+					JSONCorePreferenceNames.SCHEMA_VALIDATION,
+					fSchemaValidation.getSelection());
+		}
 	}
 
 	@Override
@@ -367,8 +391,9 @@
 
 	@Override
 	protected boolean shouldRevalidateOnSettingsChange() {
-		return fOriginalUseExtendedSyntaxValidation != fExtendedSyntaxValidation
-				.getSelection()
+		return fOriginalUseSchemaValidation != fSchemaValidation.getSelection()
+				|| fOriginalUseExtendedSyntaxValidation != fExtendedSyntaxValidation.getSelection()
 				|| super.shouldRevalidateOnSettingsChange();
 	}
+
 }