Fixed tiny bug with underspecified tokens
diff --git a/CHANGES b/CHANGES
index cfac631..afe7b3f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -10,6 +10,8 @@
 	- [bugfix] Ignore foundry for orth layer (diewald)
 	- [feature] Support fields in meta (diewald)
 	- [sigh] Support more legacy APIs (diewald)
+	- [bugfix] Check for the existence of @type in JSON-LD groups
+	  to avoid NullPointer (diewald)
 
 0.47 2014-11-05
         - [feature] Support new index format with more metadata (diewald)
diff --git a/src/main/java/de/ids_mannheim/korap/KorapCollection.java b/src/main/java/de/ids_mannheim/korap/KorapCollection.java
index 5dcb81d..904c6ce 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapCollection.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapCollection.java
@@ -25,12 +25,12 @@
 import org.slf4j.LoggerFactory;
 
 // TODO: Make a cache for the bits!!! DELETE IT IN CASE OF AN EXTENSION OR A FILTER!
-// Todo: Maybe use radomaccessfilterstrategy
+// Todo: Maybe use randomaccessfilterstrategy
 // TODO: Maybe a constantScoreQuery can make things faster?
 
-// accepts as first parameter the index
 // THIS MAY CHANGE for stuff like combining virtual collections
-// See http://mail-archives.apache.org/mod_mbox/lucene-java-user/200805.mbox/%3C17080852.post@talk.nabble.com%3E
+// See http://mail-archives.apache.org/mod_mbox/lucene-java-user/
+//     200805.mbox/%3C17080852.post@talk.nabble.com%3E
 
 public class KorapCollection {
     private KorapIndex index;
@@ -46,12 +46,16 @@
     // This advices the java compiler to ignore all loggings
     public static final boolean DEBUG = false;
 
-    // user?
     public KorapCollection (KorapIndex ki) {
 	this.index = ki;
 	this.filter = new ArrayList<FilterOperation>(5);
     };
 
+    /**
+     * Construct a new KorapCollection by passing a JSON query.
+     * This supports collections with key "collection" and
+     * legacy collections with the key "collections".
+     */
     public KorapCollection (String jsonString) {
 	ObjectMapper mapper = new ObjectMapper();
 	this.filter = new ArrayList<FilterOperation>(5);
@@ -79,10 +83,12 @@
 	};
     };
 
+
     public KorapCollection () {
 	this.filter = new ArrayList<FilterOperation>(5);
     };
 
+
     public void fromJSON (String jsonString) throws QueryException {
 	ObjectMapper mapper = new ObjectMapper();
 	try {
@@ -93,11 +99,15 @@
 	};
     };
 
+
     public void fromJSON (JsonNode json) throws QueryException {
 	this.filter(new KorapFilter(json));
     };
 
 
+    /**
+     * Legacy API for collection filters.
+     */
     public void fromJSONLegacy (String jsonString) throws QueryException {
 	ObjectMapper mapper = new ObjectMapper();
 	try {
diff --git a/src/main/java/de/ids_mannheim/korap/KorapIndex.java b/src/main/java/de/ids_mannheim/korap/KorapIndex.java
index 7072d87..28969a9 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapIndex.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapIndex.java
@@ -436,7 +436,8 @@
 			  String field,
 			  String type) {
 	// Short cut for documents
-	if (type.equals("documents")) {
+	// This will be only "texts" in the future
+	if (type.equals("documents") || type.equals("base/texts")) {
 	    if (collection.getCount() <= 0) {
 		try {
 		    return (long) this.reader().numDocs();
@@ -462,6 +463,7 @@
 	};
     
 	// Create search term
+	// This may be prefixed by foundries
 	Term term = new Term(field, "-:" + type);
 
 	long occurrences = 0;
diff --git a/src/main/java/de/ids_mannheim/korap/KorapQuery.java b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
index 2cf0832..33aa4e7 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
@@ -79,10 +79,11 @@
 
 	public Boundary (JsonNode json, int defaultMin, int defaultMax) throws QueryException {
 
-	    if (!json.has("@type") ||
-		!json.get("@type").asText().equals("korap:boundary")) {
+	    if (!json.has("@type"))
+		throw new QueryException(612, "JSON-LD group has no @type attribute");
+
+	    if (!json.get("@type").asText().equals("korap:boundary"))
 		throw new QueryException(612, "Boundary definition is not valid");
-	    };
 
 	    // Set min boundary
 	    if (json.has("min"))
@@ -126,9 +127,8 @@
 
 	int number = 0;
 
-	if (!json.has("@type")) {
+	if (!json.has("@type"))
 	    throw new QueryException(612, "JSON-LD group has no @type attribute");
-	};
 
 	String type = json.get("@type").asText();
 
@@ -278,7 +278,7 @@
 		    JsonNode firstDistance = json.get("distances").get(0);
 
 		    if (!firstDistance.has("@type"))
-			throw new QueryException(612, "Distances need a defined @type");
+			throw new QueryException(612, "JSON-LD group has no @type attribute");
 
 		    JsonNode distances;
 		    if (firstDistance.get("@type").asText().equals("korap:group"))
@@ -520,6 +520,10 @@
 
 
     private SpanQueryWrapper _segFromJSON (JsonNode json) throws QueryException {
+
+	if (!json.has("@type"))
+	    throw new QueryException(612, "JSON-LD group has no @type attribute");
+
 	String type = json.get("@type").asText();
 
 	if (DEBUG)
@@ -594,6 +598,9 @@
 	if (!json.has("key") || json.get("key").asText().length() < 1)
 	    throw new QueryException(612, "Terms and spans have to provide key attributes");
 	    
+	if (!json.has("@type"))
+	    throw new QueryException(612, "JSON-LD group has no @type attribute");
+
 	Boolean isTerm = json.get("@type").asText().equals("korap:term") ? true : false;
 	Boolean isCaseInsensitive = false;
 
diff --git a/src/test/java/de/ids_mannheim/korap/query/TestKorapQueryJSON.java b/src/test/java/de/ids_mannheim/korap/query/TestKorapQueryJSON.java
index 8f4dd0e..01c94be 100644
--- a/src/test/java/de/ids_mannheim/korap/query/TestKorapQueryJSON.java
+++ b/src/test/java/de/ids_mannheim/korap/query/TestKorapQueryJSON.java
@@ -294,6 +294,19 @@
     };
 
 
+    @Test
+    public void queryJSONunderspecifiedTokenBug () {
+	// ((MORPH(APPR) ODER MORPH(APPRART)) /+w1 Urlaub
+	try {
+	    String json = getString(getClass().getResource("/queries/bugs/underspecified_token.jsonld").getFile());
+	    new KorapQuery("tokens").fromJSON(json);
+	}
+	catch (QueryException e) {
+	    assertEquals(612, e.getErrorCode());
+	};
+    };
+
+
 
     public static String getString (String path) {
 	StringBuilder contentBuilder = new StringBuilder();
diff --git a/src/test/resources/queries/bugs/underspecified_token.jsonld b/src/test/resources/queries/bugs/underspecified_token.jsonld
new file mode 100644
index 0000000..783480f
--- /dev/null
+++ b/src/test/resources/queries/bugs/underspecified_token.jsonld
@@ -0,0 +1,106 @@
+{
+  "@context":"http://ids-mannheim.de/ns/KorAP/json-ld/v0.2/context.jsonld",
+  "errors":[],
+  "warnings":[],
+  "messages":[
+    [303,"Deprecated 2014-07-24: 'min' and 'max' to be supported until 3 months from deprecation date."],
+    ["A class has been introduced into the backend representation of your query for later reference to a part of the query. The class id is 129"],
+    [303,"Deprecated 2014-10-07: 'class' only to be supported until 3 months from deprecation date. Classes are now defined using the 'classOut' attribute."],
+    ["A class has been introduced into the backend representation of your query for later reference to a part of the query. The class id is 129"],
+    [303,"Deprecated 2014-10-07: 'class' only to be supported until 3 months from deprecation date. Classes are now defined using the 'classOut' attribute."]
+  ],
+  "meta":{
+    "startIndex":0,
+    "count":25,
+    "cutOff":false,
+    "context":{
+      "left":["char",200],
+      "right":["char",200]
+    }
+  },
+  "query":{
+    "@type":"korap:reference",
+    "operation":"operation:focus",
+    "classRef":[129],
+    "operands":[
+      {
+	"@type":"korap:group",
+	"operation":"operation:sequence",
+	"operands":[
+	  {
+	    "@type":"korap:group",
+	    "operation":"operation:class",
+	    "class":129,
+	    "classOut":129,
+	    "operands":[
+	      {
+		"@type":"korap:group",
+		"operation":"operation:or",
+		"operands":[
+		  {
+		    "@type":"korap:token",
+		    "wrap":{
+		      "key":"APPR",
+		      "match":"match:eq",
+		      "foundry":"opennlp"
+		    }
+		  },
+		  {
+		    "@type":"korap:token",
+		    "wrap":{
+		      "key":"APPRART",
+		      "match":"match:eq",
+		      "foundry":"opennlp"
+		    }
+		  }
+		]
+	      }
+	    ]
+	  },
+	  {
+	    "@type":"korap:group",
+	    "operation":"operation:class",
+	    "class":129,
+	    "classOut":129,
+	    "operands":[
+	      {
+		"@type":"korap:token",
+		"wrap":{
+		  "@type":"korap:term",
+		  "key":"Urlaub",
+		  "layer":"orth",
+		  "match":"match:eq",
+		  "foundry":"opennlp"
+		}
+	      }
+	    ]
+	  }
+	],
+	"inOrder":true,
+	"distances":[
+	  {
+	    "@type":"korap:distance",
+	    "key":"w",
+	    "boundary":{
+	      "@type":"korap:boundary",
+	      "min":0,
+	      "max":1
+	    },
+	    "min":0,
+	    "max":1
+	  }
+	]
+      }
+    ]
+  },
+  "collections":[
+    {
+      "@type":"korap:meta-filter",
+      "@value":{
+	"@type":"korap:term",
+	"@field":"korap:field#textClass",
+	"@value":"wissenschaft"
+      }
+    }
+  ]
+}