Modified AttributeSpans to have the same start and end positions as their elements,
Modified SpansWithAttribute to be able to return *arbitrary* elements with attributes.
Modified deserialization for SpanWithAttributeQuery.

diff --git a/src/main/java/de/ids_mannheim/korap/KorapQuery.java b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
index 9af942d..a3e1f86 100644
--- a/src/main/java/de/ids_mannheim/korap/KorapQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/KorapQuery.java
@@ -959,6 +959,7 @@
 						"JSON-LD group has no @type attribute");
 			}
 
+			SpanQueryWrapper elementWithIdWrapper = null;
 			if (value.toString().isEmpty()) {
 				// attribute with arbitraty elements
 
@@ -966,10 +967,9 @@
 						"Arbitraty elements with attributes are currently not supported.");
 				return null;
 			}
-
-			SpanQueryWrapper elementWithIdWrapper = tag(value.toString());
-			if (elementWithIdWrapper == null) {
-				return null;
+			else{
+				elementWithIdWrapper = tag(value.toString());
+				if (elementWithIdWrapper == null) return null;
 			}
 
 			if (attrNode.get("@type").asText().equals("korap:term")) {
diff --git a/src/main/java/de/ids_mannheim/korap/query/SimpleSpanQuery.java b/src/main/java/de/ids_mannheim/korap/query/SimpleSpanQuery.java
index 9c7f616..c304bb9 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SimpleSpanQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SimpleSpanQuery.java
@@ -8,6 +8,10 @@
 import org.apache.lucene.index.Term;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.spans.SpanQuery;
+import org.apache.lucene.search.spans.SpanTermQuery;
+
+import de.ids_mannheim.korap.query.spans.AttributeSpans;
+import de.ids_mannheim.korap.query.spans.ElementSpans;
 
 /**
  * A base class for Spanqueries. It added some properties and methods to the
@@ -110,28 +114,16 @@
      * */
     public SimpleSpanQuery(SpanQuery firstClause,
             List<SpanQuery> secondClauses, boolean collectPayloads) {
-        this(firstClause, collectPayloads);
-
-        if (secondClauses == null) {
-            throw new IllegalArgumentException(
-                    "The list of second clauses cannot be null.");
-        }
-        if (secondClauses.size() < 1) {
-            throw new IllegalArgumentException(
-                    "The list of second clauses cannot be empty.");
-        }
-
-        for (SpanQuery secondClause : secondClauses) {
-            if (secondClause == null) {
-                throw new IllegalArgumentException(
-                        "A second clause cannot be null.");
-            }
-            checkField(secondClause);
-        }
-        this.setClauseList(secondClauses);
+		this(firstClause, collectPayloads);
+		setClauseList(secondClauses);
     }
 
-    private void checkField(SpanQuery clause) {
+	public SimpleSpanQuery(List<SpanQuery> clauses, boolean collectPayloads) {
+		this.collectPayloads = collectPayloads;
+		setClauseList(clauses);
+	}
+
+	private void checkField(SpanQuery clause) {
         if (!clause.getField().equals(field)) {
             throw new IllegalArgumentException(
                     "Clauses must have the same field.");
@@ -152,8 +144,27 @@
      * 
      * @param clauseList a list of spanqueries
      */
-    public void setClauseList(List<SpanQuery> clauseList) {
-        this.clauseList = clauseList;
+	public void setClauseList(List<SpanQuery> clauses) {
+		if (clauses == null) {
+			throw new IllegalArgumentException(
+					"The list of clauses cannot be null.");
+		}
+		if (clauses.size() < 1) {
+			throw new IllegalArgumentException(
+					"The list of clauses cannot be empty.");
+		}
+
+		if (this.field == null) {
+			this.field = clauses.get(0).getField();
+		}
+
+		for (SpanQuery clause : clauses) {
+			if (clause == null) {
+				throw new IllegalArgumentException("A clause cannot be null.");
+			}
+			checkField(clause);
+		}
+		this.clauseList = clauses;
     }
 
     /**
@@ -247,10 +258,13 @@
     @Override
     public Query rewrite(IndexReader reader) throws IOException {
         SimpleSpanQuery clone = null;
-        clone = updateClone(reader, clone, firstClause, 1);
+		if (firstClause != null) {
+			clone = updateClone(reader, clone, firstClause, 1);
+		}
         if (secondClause != null) {
             clone = updateClone(reader, clone, secondClause, 2);
-        } else if (clauseList != null) {
+        } 
+        else if (clauseList != null) {
             clone = updateClone(reader, clone, clauseList);
         }
         return (clone != null ? clone : this);
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanAttributeQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanAttributeQuery.java
index 5f4c262..6a5038c 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanAttributeQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanAttributeQuery.java
@@ -38,7 +38,7 @@
  * 
  * @author margaretha
  * */
-public class SpanAttributeQuery extends SimpleSpanQuery {
+public class SpanAttributeQuery extends SpanWithIdQuery {
 
     boolean negation;
 
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanWithAttributeQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanWithAttributeQuery.java
index 65335f2..d4d66eb 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanWithAttributeQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanWithAttributeQuery.java
@@ -39,13 +39,39 @@
  */
 public class SpanWithAttributeQuery extends SpanWithIdQuery {
 
-    private boolean isMultipleAttributes;
+	public boolean isMultipleAttributes;
     private String type;
 
+	/**
+	 * Constructs a SpanWithAttributeQuery for any arbitrary SpansWithId (e.g.
+	 * elements, relations) having the specified {@link SpanAttributeQuery}.
+	 * 
+	 * @param attributeQuery a SpanAttributeQuery
+	 * @param collectPayloads a boolean flag representing the value
+     *        <code>true</code> if payloads are to be collected, otherwise
+     *        <code>false</code>.
+	 */
+	public SpanWithAttributeQuery(SpanAttributeQuery attributeQuery,
+			boolean collectPayloads) {
+		super(attributeQuery, collectPayloads);
+		type = "spanWithAttribute";
+	}
+
+	public SpanWithAttributeQuery(List<SpanQuery> attributeQueries,
+			boolean collectPayloads) {
+		super(attributeQueries, collectPayloads);
+		isMultipleAttributes = true;
+		type = "spanWithAttribute";
+	}
+
     /**
      * Constructs a SpanWithAttributeQuery for the specified SpanWithIdQuery and
      * SpanAttributeQuery retrieving spans having a specific attribute.
      * 
+     * If the SpanWithIdQuery is a SpanAttributeQuery, this will return arbitrary 
+     * elements with two specified attributes (i.e. and relation between the two 
+     * attributes).
+     * 
      * @param firstClause a SpanWithIdQuery
      * @param secondClause a SpanAttributeQuery
      * @param collectPayloads a boolean flag representing the value
@@ -98,53 +124,88 @@
 
     @Override
     public SimpleSpanQuery clone() {
-        SpanWithAttributeQuery sq;
-        if (!isMultipleAttributes) {
-            sq = new SpanWithAttributeQuery(
-                    (SpanWithIdQuery) firstClause.clone(),
-                    (SpanAttributeQuery) secondClause.clone(), collectPayloads);
-        } else {
-            List<SpanQuery> clauseList = new ArrayList<SpanQuery>();
-            SpanAttributeQuery saq;
-            for (SpanQuery q : this.clauseList) {
-                saq = (SpanAttributeQuery) q;
-                clauseList.add(saq.clone());
+		if (secondClause != null) {
+			if (isMultipleAttributes) {
+				return new SpanWithAttributeQuery(
+						(SpanWithIdQuery) firstClause.clone(), cloneClauseList(),
+						collectPayloads);
+			} 
+			else {
+				return new SpanWithAttributeQuery(
+						(SpanWithIdQuery) firstClause.clone(),
+						(SpanAttributeQuery) secondClause.clone(),
+						collectPayloads);
             }
-            sq = new SpanWithAttributeQuery(
-                    (SpanWithIdQuery) firstClause.clone(), clauseList,
-                    collectPayloads);
         }
-        return sq;
+        else {
+			if (isMultipleAttributes) {
+				return new SpanWithAttributeQuery(cloneClauseList(),
+						collectPayloads);
+			} 
+			else {
+				return new SpanWithAttributeQuery(
+					(SpanAttributeQuery) firstClause.clone(),
+                    collectPayloads);
+			}
+        }	
     }
 
+	private List<SpanQuery> cloneClauseList() {
+		List<SpanQuery> clauseList = new ArrayList<SpanQuery>();
+		SpanAttributeQuery saq;
+		for (SpanQuery q : this.clauseList) {
+			saq = (SpanAttributeQuery) q;
+			clauseList.add(saq.clone());
+		}
+		return clauseList;
+	}
+
     @Override
     public Spans getSpans(AtomicReaderContext context, Bits acceptDocs,
             Map<Term, TermContext> termContexts) throws IOException {
 
+		if (type.equals("spanWithAttribute")) {
+			return new SpansWithAttribute(this, context, acceptDocs,
+					termContexts);
+		}
+
         Spans spans = this.getFirstClause().getSpans(context, acceptDocs,
                 termContexts);
 
-        if (type.equals("spanElementWithAttribute")) {
+		if (type.equals("spanElementWithAttribute")) {
             return new SpansWithAttribute(this, (ElementSpans) spans, context,
                     acceptDocs, termContexts);
-        } else if (type.equals("spanRelationWithAttribute")) {
+        } 
+        else if (type.equals("spanRelationWithAttribute")) {
             return new SpansWithAttribute(this, (RelationSpans) spans, context,
                     acceptDocs, termContexts);
         }
-
-        return new SpansWithAttribute(this, (TermSpansWithId) spans, context,
+        else if (type.equals("spanTermWithAttribute")){
+        	return new SpansWithAttribute(this, (TermSpansWithId) spans, context,
                 acceptDocs, termContexts);
+        }
+        else {
+			throw new IllegalArgumentException("Span query type: " + type
+					+ "is unknown.");
+		}
     }
 
     @Override
     public String toString(String field) {
-
+    	boolean isFirstClassNull = true;
         StringBuilder sb = new StringBuilder();
         sb.append(type);
         sb.append("(");
-        sb.append(firstClause.toString(field));
-        sb.append(", ");
-        if (isMultipleAttributes) {
+		if (firstClause != null) {
+			sb.append(firstClause.toString(field));
+			isFirstClassNull = false;
+		}
+		if (secondClause !=null){
+			sb.append(", ");
+            sb.append(secondClause.toString(field));
+        }
+		else if (isMultipleAttributes) {
+			if (!isFirstClassNull) sb.append(", ");
             sb.append("[");
 
             SpanQuery sq;
@@ -157,10 +218,8 @@
             }
 
             sb.append("]");
-        } else {
-            sb.append(secondClause.toString(field));
-        }
-        sb.append(")");
+        } 
+		sb.append(")");
         return sb.toString();
     }
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/SpanWithIdQuery.java b/src/main/java/de/ids_mannheim/korap/query/SpanWithIdQuery.java
index 55e2744..4845a01 100644
--- a/src/main/java/de/ids_mannheim/korap/query/SpanWithIdQuery.java
+++ b/src/main/java/de/ids_mannheim/korap/query/SpanWithIdQuery.java
@@ -56,4 +56,9 @@
             List<SpanQuery> secondClauses, boolean collectPayloads) {
         super(firstClause, secondClauses, collectPayloads);
     }
+
+	public SpanWithIdQuery(List<SpanQuery> clauses,
+			boolean collectPayloads) {
+		super(clauses, collectPayloads);
+	}
 }
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/AttributeSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/AttributeSpans.java
index 201e9df..6382222 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/AttributeSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/AttributeSpans.java
@@ -18,7 +18,7 @@
 
 import de.ids_mannheim.korap.query.SpanAttributeQuery;
 
-/**
+/** UPDATE THIS!
  * Span enumeration of attributes which are term spans with special payload
  * assignments referring to another span (e.g. element/relation span) to which
  * an attribute span belongs. The class is basically a wrapper of Lucene
@@ -36,13 +36,13 @@
  * 
  * @author margaretha
  * */
-public class AttributeSpans extends SimpleSpans {
+public class AttributeSpans extends SpansWithId {
 
     private List<CandidateAttributeSpan> candidateList;
     private int currentDoc, currentPosition;
-    private short referentId;
+	// private short referentId;
     private boolean isFinish;
-    private int elementEnd;
+	// private int elementEnd;
 
     protected Logger logger = LoggerFactory.getLogger(AttributeSpans.class);
 
@@ -90,8 +90,8 @@
                 this.matchDocNumber = cs.getDoc();
                 this.matchStartPosition = cs.getStart();
                 this.matchEndPosition = cs.getEnd();
-                this.setReferentId(cs.getSpanId());
-                this.setElementEnd(cs.getElementEnd());
+				this.setSpanId(cs.getSpanId()); // referentId
+				// this.setElementEnd(cs.getElementEnd());
                 candidateList.remove(0);
                 return true;
             } else {
@@ -149,9 +149,9 @@
      * 
      * @return a span id, for instance a relation id or an element id
      */
-    public short getReferentId() {
-        return this.referentId;
-    }
+	// public short getReferentId() {
+	// return this.referentId;
+	// }
 
     /**
      * Sets the span id to which an attribute span belongs, for instance a
@@ -160,9 +160,9 @@
      * @param refId the span id to which an attribute span belongs, for
      *        instance a relation id or an element id.
      */
-    public void setReferentId(short refId) {
-        this.referentId = refId;
-    }
+	// public void setReferentId(short refId) {
+	// this.referentId = refId;
+	// }
 
     /**
      * Returns the end position of the element to which an attribute span
@@ -170,9 +170,9 @@
      * 
      * @return an element end position
      */
-    public int getElementEnd() {
-        return elementEnd;
-    }
+	// public int getElementEnd() {
+	// return elementEnd;
+	// }
 
     /**
      * Sets the end position of the element to which an attribute span belongs.
@@ -180,9 +180,9 @@
      * @param elementEnd the end position of the element to which an attribute
      *        span belongs.
      */
-    public void setElementEnd(int elementEnd) {
-        this.elementEnd = elementEnd;
-    }
+	// public void setElementEnd(int elementEnd) {
+	// this.elementEnd = elementEnd;
+	// }
 
     /**
      * Tells if the enumeration of the AttributeSpans has come to an end.
@@ -234,7 +234,7 @@
             Comparable<CandidateSpan> {
 
         private short spanId;
-        private int elementEnd;
+		// private int elementEnd;
 
         /**
          * Construct a CandidateAttributeSpan based on the given span, spanId,
@@ -251,8 +251,9 @@
         public CandidateAttributeSpan(Spans span, short spanId, int elementEnd)
                 throws IOException {
             super(span);
-            setSpanId(spanId);
-            setElementEnd(elementEnd);
+			setSpanId(spanId);
+			this.end = elementEnd;
+			// setElementEnd(elementEnd);
         }
 
         public void setSpanId(short spanId) {
@@ -263,13 +264,13 @@
             return spanId;
         }
 
-        public int getElementEnd() {
-            return elementEnd;
-        }
-
-        public void setElementEnd(int elementEnd) {
-            this.elementEnd = elementEnd;
-        }
+		// public int getElementEnd() {
+		// return elementEnd;
+		// }
+		//
+		// public void setElementEnd(int elementEnd) {
+		// this.elementEnd = elementEnd;
+		// }
 
         @Override
         public int compareTo(CandidateSpan o) {
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/CandidateSpan.java b/src/main/java/de/ids_mannheim/korap/query/spans/CandidateSpan.java
index 914556b..ca1132d 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/CandidateSpan.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/CandidateSpan.java
@@ -14,7 +14,7 @@
  * @author margaretha
  * */
 public class CandidateSpan implements Comparable<CandidateSpan>, Cloneable {
-    private int doc, start, end;
+	protected int doc, start, end;
     private long cost;
     private Collection<byte[]> payloads = new ArrayList<>();
     private int position;
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/SimpleSpans.java b/src/main/java/de/ids_mannheim/korap/query/spans/SimpleSpans.java
index 869ece0..c9ca2e6 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/SimpleSpans.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/SimpleSpans.java
@@ -8,6 +8,7 @@
 import org.apache.lucene.index.AtomicReaderContext;
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
+import org.apache.lucene.search.spans.SpanQuery;
 import org.apache.lucene.search.spans.Spans;
 import org.apache.lucene.util.Bits;
 
@@ -44,12 +45,12 @@
   		matchPayload = new ArrayList<byte[]>();
   		
   		// Get the enumeration of the two spans to match
-  		firstSpans = simpleSpanQuery.getFirstClause().
-  			getSpans(context, acceptDocs, termContexts);
+		SpanQuery sq;
+		if ((sq = simpleSpanQuery.getFirstClause()) != null)
+			firstSpans = sq.getSpans(context, acceptDocs, termContexts);
   		
-  		if (simpleSpanQuery.getSecondClause() != null)
-  			secondSpans = simpleSpanQuery.getSecondClause().
-  				getSpans(context, acceptDocs, termContexts);
+		if ((sq = simpleSpanQuery.getSecondClause()) != null)
+			secondSpans = sq.getSpans(context, acceptDocs, termContexts);
   		
   		isStartEnumeration=true;
       }
diff --git a/src/main/java/de/ids_mannheim/korap/query/spans/SpansWithAttribute.java b/src/main/java/de/ids_mannheim/korap/query/spans/SpansWithAttribute.java
index 0362bfa..6adeaf5 100644
--- a/src/main/java/de/ids_mannheim/korap/query/spans/SpansWithAttribute.java
+++ b/src/main/java/de/ids_mannheim/korap/query/spans/SpansWithAttribute.java
@@ -9,6 +9,7 @@
 import org.apache.lucene.index.Term;
 import org.apache.lucene.index.TermContext;
 import org.apache.lucene.search.spans.SpanQuery;
+import org.apache.lucene.search.spans.Spans;
 import org.apache.lucene.util.Bits;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,24 +56,62 @@
         referentSpans = spansWithId;
         referentSpans.hasSpanId = true; // dummy setting enabling reading elementRef
         hasMoreSpans = referentSpans.next();
-
-        attributeList = new ArrayList<AttributeSpans>();
-        notAttributeList = new ArrayList<AttributeSpans>();
-
-        List<SpanQuery> sqs = spanWithAttributeQuery.getClauseList();
-        if (sqs != null) {
-            for (SpanQuery sq : sqs) {
-                addAttributes((SpanAttributeQuery) sq, context, acceptDocs,
-                        termContexts);
-            }
-        } else {
-            addAttributes(
-                    (SpanAttributeQuery) spanWithAttributeQuery
-                            .getSecondClause(),
-                    context, acceptDocs, termContexts);
-        }
+		setAttributeList(spanWithAttributeQuery, context, acceptDocs,
+				termContexts);
     }
 
+	// if there is no (positive) attributes, but there are *not attributes*
+	// hasmorespan = true
+	public SpansWithAttribute(SpanWithAttributeQuery spanWithAttributeQuery,
+			AtomicReaderContext context,
+			Bits acceptDocs, Map<Term, TermContext> termContexts)
+			throws IOException {
+		super(spanWithAttributeQuery, context, acceptDocs, termContexts);
+		hasMoreSpans = true;
+		setAttributeList(spanWithAttributeQuery, context, acceptDocs,
+				termContexts);
+		if (attributeList.size() == 0){
+			throw new IllegalArgumentException("No (positive) attribute is defined."); 
+		}
+		else if (attributeList.size() > 1) {
+			referentSpans = attributeList.get(0);
+			attributeList.remove(0);
+		}
+	}
+
+	public void setAttributeList(SpanWithAttributeQuery swaq,
+			AtomicReaderContext context, Bits acceptDocs,
+			Map<Term, TermContext> termContexts) throws IOException {
+		
+		attributeList = new ArrayList<AttributeSpans>();
+		notAttributeList = new ArrayList<AttributeSpans>();
+
+		List<SpanQuery> attributeList = swaq.getClauseList();
+		if (swaq.isMultipleAttributes) {
+			if (attributeList != null) {
+				for (SpanQuery sq : attributeList) {
+					addAttributes((SpanAttributeQuery) sq, context, acceptDocs,
+							termContexts);
+				}
+			}
+			else {
+				throw new NullPointerException("Attribute list is null.");
+			}
+		} 
+		else if (swaq.getSecondClause() != null) {
+			addAttributes((SpanAttributeQuery) swaq.getSecondClause(),
+					context, acceptDocs, termContexts);
+		}
+		else if (swaq.getType().equals("spanWithAttribute") &&
+				swaq.getFirstClause() != null) {
+			addAttributes((SpanAttributeQuery) swaq.getFirstClause(),
+					context, acceptDocs, termContexts);
+		}
+		else {
+			throw new NullPointerException("No attribute is defined.");
+		}
+	}
+
     /**
      * Adds the given {@link SpanAttributeQuery} to the attributeList or
      * notAttributeList depending on the query, whether it is a negation or not.
@@ -86,12 +125,14 @@
     private void addAttributes(SpanAttributeQuery sq,
             AtomicReaderContext context, Bits acceptDocs,
             Map<Term, TermContext> termContexts) throws IOException {
+    	
         AttributeSpans as = (AttributeSpans) sq.getSpans(context, acceptDocs,
                 termContexts);
         if (sq.isNegation()) {
             notAttributeList.add(as);
             as.next();
-        } else {
+        } 
+        else {
             attributeList.add(as);
             hasMoreSpans &= as.next();
         }
@@ -100,25 +141,45 @@
     @Override
     public boolean next() throws IOException {
         isStartEnumeration = false;
-        return advance();
+		if (referentSpans == null) { // only one (positive) attribute
+			return advanceAttribute();
+		}
+		else { return advance(); }
     }
 
-    /**
-     * Searches for the next match by first identify a possible element
-     * position, and then ensuring that the element contains all the attributes
-     * and <em>do not</em> contain any of the not attributes.
-     * 
-     * @return <code>true</code> if the a match is found, <code>false</code>
-     *         otherwise.
-     * @throws IOException
-     */
+	private boolean advanceAttribute() throws IOException {
+		while(hasMoreSpans){
+			SpansWithId referentSpans = attributeList.get(0);
+			advanceNotAttributes(referentSpans);
+			if (checkNotReferentId(referentSpans)) {
+				this.matchDocNumber = referentSpans.doc();
+				this.matchStartPosition = referentSpans.start();
+				this.matchEndPosition = referentSpans.end();
+				this.matchPayload = referentSpans.getPayload();
+				this.spanId = referentSpans.getSpanId();
+				hasMoreSpans = referentSpans.next();
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Searches for the next match by first identify a possible element
+	 * position, and then ensuring that the element contains all the attributes
+	 * and <em>do not</em> contain any of the not attributes.
+	 * 
+	 * @return <code>true</code> if the a match is found, <code>false</code>
+	 *         otherwise.
+	 * @throws IOException
+	 */
     private boolean advance() throws IOException {
 
         while (hasMoreSpans && searchSpanPosition()) {
             //logger.info("element: " + withAttributeSpans.start() + ","+ withAttributeSpans.end() +
             //	" ref:"+withAttributeSpans.getSpanId());
 
-            if (checkReferentId() && checkNotReferentId()) {
+			if (checkReferentId() && checkNotReferentId(referentSpans)) {
                 this.matchDocNumber = referentSpans.doc();
                 this.matchStartPosition = referentSpans.start();
                 this.matchEndPosition = referentSpans.end();
@@ -151,8 +212,8 @@
                 continue;
             }
             if (checkAttributeListPosition()) {
-                advanceNotAttributes();
-                //				    logger.info("element is found: "+ withAttributeSpans.start());
+				advanceNotAttributes(referentSpans);
+                // logger.info("element is found: "+ withAttributeSpans.start());
                 return true;
             }
         }
@@ -216,7 +277,7 @@
      * 
      * @throws IOException
      */
-    private void advanceNotAttributes() throws IOException {
+	private void advanceNotAttributes(Spans referentSpans) throws IOException {
 
         for (AttributeSpans a : notAttributeList) {
             // advance the doc# of not AttributeSpans
@@ -231,7 +292,6 @@
                     a.setFinish(true);
             }
         }
-        //return true;
     }
 
     /**
@@ -245,8 +305,8 @@
      */
     private boolean checkReferentId() throws IOException {
         for (AttributeSpans attribute : attributeList) {
-            if (referentSpans.getSpanId() != attribute.getReferentId()) {
-                if (referentSpans.getSpanId() < attribute.getReferentId())
+			if (referentSpans.getSpanId() != attribute.getSpanId()) {
+				if (referentSpans.getSpanId() < attribute.getSpanId())
                     hasMoreSpans = attribute.next();
                 else {
                     hasMoreSpans = referentSpans.next();
@@ -268,12 +328,12 @@
      *         <code>false</code> otherwise.
      * @throws IOException
      */
-    private boolean checkNotReferentId() throws IOException {
+	private boolean checkNotReferentId(SpansWithId referentSpans) throws IOException {
         for (AttributeSpans notAttribute : notAttributeList) {
             if (!notAttribute.isFinish()
                     && referentSpans.start() == notAttribute.start()
                     && referentSpans.getSpanId() == notAttribute
-                            .getReferentId()) {
+                            .getSpanId()) {
                 hasMoreSpans = referentSpans.next();
                 return false;
             }
diff --git a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithAttributeQueryWrapper.java b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithAttributeQueryWrapper.java
index 517e4c6..7b7418f 100644
--- a/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithAttributeQueryWrapper.java
+++ b/src/main/java/de/ids_mannheim/korap/query/wrap/SpanWithAttributeQueryWrapper.java
@@ -11,11 +11,48 @@
 import de.ids_mannheim.korap.query.SpanWithIdQuery;
 import de.ids_mannheim.korap.util.QueryException;
 
+/**
+ * No optimization using expansion
+ * 
+ * @author margaretha
+ * */
 public class SpanWithAttributeQueryWrapper extends SpanQueryWrapper {
 
 	private SpanQueryWrapper withIdQueryWrapper = null;
 	private SpanQueryWrapper attrQueryWrapper = null;
 	private List<SpanQueryWrapper> queryWrapperList = null;
+	private boolean isSingleAttribute = false;
+
+	public SpanWithAttributeQueryWrapper(SpanQueryWrapper attrQuery) {
+		if (attrQuery != null) isNull = false;
+		if (attrQuery.isEmpty()) {
+			isEmpty = true;
+			return;
+		}
+		this.attrQueryWrapper = attrQuery;
+		this.isSingleAttribute = true;
+	}
+
+	public SpanWithAttributeQueryWrapper(List<SpanQueryWrapper> attrList)
+			throws QueryException {
+
+		if (attrList != null) isNull = false;
+		if (attrList.isEmpty()) {
+			throw new QueryException("No attribute is defined.");
+		}
+
+		for (SpanQueryWrapper sqw : attrList) {
+			if (sqw == null) {
+				isNull = true;
+				return;
+			}
+			if (sqw.isEmpty) {
+				isEmpty = true;
+				return;
+			}
+		}
+		this.queryWrapperList = attrList;
+	}
 
 	public SpanWithAttributeQueryWrapper(SpanQueryWrapper withIdQuery,
 			SpanQueryWrapper attrQuery) {
@@ -30,6 +67,7 @@
 
 		this.attrQueryWrapper = attrQuery;
 		this.withIdQueryWrapper = withIdQuery;
+		this.isSingleAttribute = true;
 	}
 
 	public SpanWithAttributeQueryWrapper(SpanQueryWrapper withIdQuery,
@@ -42,6 +80,9 @@
 			isEmpty = true;
 			return;
 		}
+		// if (attrList.isEmpty()) {
+			// not withattribute query, just a normal query
+		// }
 
 		for (SpanQueryWrapper sqw : attrList) {
 			if (sqw == null) {
@@ -53,68 +94,108 @@
 				return;
 			}
 		}
-		if (attrList.isEmpty()) {
-			// not withattribute query, just a normal query
-		}
+		
 		this.queryWrapperList = attrList;
 		this.withIdQueryWrapper = withIdQuery;
 	}
 
-	public SpanAttributeQuery createSpanAttributeQuery(
-			SpanQueryWrapper attrQueryWrapper) throws QueryException {
-		SpanQuery sq = attrQueryWrapper.toQuery();
-		if (sq == null) {
-			isNull = true;
-			return null;
-		}
-		if (sq instanceof SpanTermQuery) {
-			return new SpanAttributeQuery(
-					(SpanTermQuery) sq,
-					attrQueryWrapper.isNegative, true);
-		} 
-		else {
-			throw new IllegalArgumentException(
-					"The subquery is not a SpanTermQuery.");
-		}
-	}
-
 	@Override
 	public SpanQuery toQuery() throws QueryException {
 
-		if (isNull || isEmpty) return null;
-		
+		if (isNull || isEmpty) return null;		
+		if (withIdQueryWrapper != null){
+			return createSpecificSpanWithAttributeQuery();
+		}
+		else{
+			return createArbitrarySpanWithAttributeQuery(); 
+		}
+	}
+	
+	private SpanQuery createSpecificSpanWithAttributeQuery()
+			throws QueryException {
 		SpanWithIdQuery withIdQuery = (SpanWithIdQuery) withIdQueryWrapper
 				.toQuery();
 		if (withIdQuery == null) {
 			isNull = true;
 			return null;
 		}
-		
-		if (attrQueryWrapper != null){
-			SpanAttributeQuery attrQuery = createSpanAttributeQuery(attrQueryWrapper);
+		if (isSingleAttribute) {
+			return createSpanWithSingleAttributeQuery(withIdQuery);
+		}
+		else if (queryWrapperList.isEmpty()) {
+			return withIdQuery;
+		}
+		else{
+			return createSpanWithAttributeListQuery(withIdQuery);
+		}
+	}
+	
+	private SpanWithAttributeQuery createSpanWithSingleAttributeQuery(
+			SpanWithIdQuery withIdQuery)
+			throws QueryException {
+		SpanAttributeQuery attrQuery = createSpanAttributeQuery(this.attrQueryWrapper);
+		if (attrQuery != null) {
+			if (withIdQuery != null) {
+				return new SpanWithAttributeQuery(withIdQuery, attrQuery, true);
+			} 
+			else {
+				return new SpanWithAttributeQuery(attrQuery, true);
+			}
+		}
+		isNull = true;
+		return null;
+	}
+
+	private SpanAttributeQuery createSpanAttributeQuery(
+			SpanQueryWrapper attrQueryWrapper) throws QueryException {
+		SpanQuery sq = attrQueryWrapper.toQuery();
+		if (sq != null) {
+			if (sq instanceof SpanTermQuery) {
+				return new SpanAttributeQuery((SpanTermQuery) sq,
+						attrQueryWrapper.isNegative, true);
+			} 
+			else {
+				throw new IllegalArgumentException(
+						"The subquery is not a SpanTermQuery.");
+			}
+		}
+		return null;
+	}
+
+	private SpanWithAttributeQuery createSpanWithAttributeListQuery(
+			SpanWithIdQuery withIdQuery)
+			throws QueryException {
+		List<SpanQuery> attrQueries = new ArrayList<SpanQuery>();
+		SpanQuery attrQuery = null;
+		for (SpanQueryWrapper sqw : queryWrapperList) {
+			attrQuery = createSpanAttributeQuery(sqw);
 			if (attrQuery == null) {
 				isNull = true;
 				return null;
 			}
-			return new SpanWithAttributeQuery(withIdQuery, attrQuery, true);
+			attrQueries.add(attrQuery);
 		}
-		else if (queryWrapperList != null) {
-			if (queryWrapperList.isEmpty()) {
-				return withIdQuery;
-			}
-
-			List<SpanQuery> attrQueries = new ArrayList<SpanQuery>();
-			SpanQuery attrQuery;
-			for (SpanQueryWrapper sqw : queryWrapperList) {
-				attrQuery = createSpanAttributeQuery(sqw);
-				if (attrQuery == null) {
-					isNull = true;
-					return null;
-				}
-				attrQueries.add(attrQuery);
-			}
+		
+		if (withIdQuery != null) {
 			return new SpanWithAttributeQuery(withIdQuery, attrQueries, true);
+		} 
+		else {
+			return new SpanWithAttributeQuery(attrQueries, true);
 		}
-		return null;		
 	}
+
+	private SpanQuery createArbitrarySpanWithAttributeQuery()
+			throws QueryException {
+		if (isSingleAttribute) {
+			return createSpanWithSingleAttributeQuery(null);
+		}
+		else if (queryWrapperList.isEmpty()) {
+			throw new QueryException("No attribute is defined.");
+		}
+		else{
+			return createSpanWithAttributeListQuery(null);
+		}				
+	}
+
+
 }
diff --git a/src/test/java/de/ids_mannheim/korap/index/TestAttributeIndex.java b/src/test/java/de/ids_mannheim/korap/index/TestAttributeIndex.java
index d4a9ae2..aa14142 100644
--- a/src/test/java/de/ids_mannheim/korap/index/TestAttributeIndex.java
+++ b/src/test/java/de/ids_mannheim/korap/index/TestAttributeIndex.java
@@ -72,7 +72,7 @@
                         + "[(1-2)s:e|_2#1-2|<>:div#1-2$<i>2<s>1|<>:a#1-2$<i>2<s>2|@:class=book$<s>2<i>2|@:class=header$<s>1<i>2]"
                         + "[(2-3)s:b|_3#2-3|<>:div#2-3$<i>5<s>1|<>:a#1-2$<i>2<s>2|@:class=header$<s>2<i>2|@:class=book$<s>1<i>5]"
                         + "[(3-4)s:a|_4#3-4|<>:div#3-5$<i>5<s>1|@:class=title$<s>1<i>5]"
-                        + "[(4-5)s:b|_5#4-5|<>:div#4-5$<i>5<s>1|@:class=header$<s>1<i>5|@:class=book$<s>1<i>5|@:class=book$<s>1<i>5]"
+						+ "[(4-5)s:b|_5#4-5|<>:div#4-5$<i>5<s>1|@:class=header$<s>1<i>5|@:class=book$<s>1<i>5]"
                         + "[(5-6)s:d|_6#5-6|<>:s#5-6$<i>6<s>-1|<>:div#5-6$<i>6<s>1|@:class=header$<s>1<i>6]"
                         + "[(6-7)s:d|_7#6-7|<>:s#6-7$<i>7<s>2|<>:div#6-7$<i>7<s>1|@:class=header$<s>1<i>7|@:class=book$<s>2<i>7]");
 
@@ -171,6 +171,51 @@
         assertEquals(6, kr.getMatch(0).getEndPos());
     }
 
+	/**
+	 * Element with only not attributes
+	 * 
+	 * @throws IOException
+	 * */
+	@Test
+	public void testcase9() throws IOException {
+
+		ki.addDoc(createFieldDoc2());
+		ki.commit();
+
+		SpanAttributeQuery saq = new SpanAttributeQuery(new SpanTermQuery(
+				new Term("base", "@:class=book")), true, true);
+		SpanQuery sq = new SpanWithAttributeQuery(new SpanElementQuery("base",
+				"div"), saq, true);
+
+		kr = ki.search(sq, (short) 10);
+		assertEquals(4, kr.getTotalResults());
+		assertEquals(1, kr.getMatch(0).getStartPos());
+		assertEquals(2, kr.getMatch(0).getEndPos());
+		assertEquals(3, kr.getMatch(1).getStartPos());
+		assertEquals(5, kr.getMatch(1).getEndPos());
+		assertEquals(5, kr.getMatch(2).getStartPos());
+		assertEquals(6, kr.getMatch(2).getEndPos());
+		assertEquals(6, kr.getMatch(3).getStartPos());
+		assertEquals(7, kr.getMatch(3).getEndPos());
+
+		List<SpanQuery> sql = new ArrayList<>();
+		sql.add(saq);
+		sql.add(new SpanAttributeQuery(new SpanTermQuery(new Term("base",
+				"@:class=header")), true, true));
+		sq = new SpanWithAttributeQuery(new SpanElementQuery("base", "div"),
+				sql, true);
+
+		kr = ki.search(sq, (short) 10);
+		assertEquals(1, kr.getTotalResults());
+		assertEquals(3, kr.getMatch(0).getStartPos());
+		assertEquals(5, kr.getMatch(0).getEndPos());
+
+//		for (int i = 0; i < kr.getTotalResults(); i++) {
+//			System.out.println(kr.getMatch(i).getLocalDocID() + " "
+//					+ kr.getMatch(i).startPos + " " + kr.getMatch(i).endPos);
+//		}
+	}
+
     /**
      * same attribute types referring to different element types
      * */
@@ -230,23 +275,111 @@
     }
 
     /**
-     * Arbitrary elements with a specific attribute This is just spanAttribute
-     * query, to get the elementEnd, you have to use getElementEnd().
-     * Alternatives (unimplemented): 1) store in payload? 2) wrap as a span
-     * */
+	 * Arbitrary elements with a specific attribute.
+	 * */
     @Test
     public void testCase5() throws IOException {
-        ki.addDoc(createFieldDoc1());
+		ki.addDoc(createFieldDoc2());
         ki.commit();
-        SpanAttributeQuery saq = new SpanAttributeQuery(new SpanTermQuery(
-                new Term("base", "@:class=book")), true);
-        kr = ki.search(saq, (short) 10);
-        assertEquals((long) 3, kr.getTotalResults());
+		SpanAttributeQuery saq = new SpanAttributeQuery(new SpanTermQuery(
+				new Term("base", "@:class=book")), true);
 
-        /*
-         * for (int i=0; i< kr.getTotalResults(); i++){ System.out.println(
-         * kr.match(i).getLocalDocID()+" "+ kr.match(i).startPos + " " +
-         * kr.match(i).endPos ); }
-         */
+		SpanWithAttributeQuery swaq = new SpanWithAttributeQuery(saq, true);
+		kr = ki.search(swaq, (short) 10);
+		assertEquals(6, kr.getTotalResults());
+
+		assertEquals(0, kr.getMatch(0).getStartPos());
+		assertEquals(3, kr.getMatch(0).getEndPos());
+		assertEquals(0, kr.getMatch(1).getStartPos());
+		assertEquals(5, kr.getMatch(1).getEndPos());
+		assertEquals(1, kr.getMatch(2).getStartPos());
+		assertEquals(2, kr.getMatch(2).getEndPos());
+		assertEquals(2, kr.getMatch(3).getStartPos());
+		assertEquals(5, kr.getMatch(3).getEndPos());
+		assertEquals(4, kr.getMatch(4).getStartPos());
+		assertEquals(5, kr.getMatch(4).getEndPos());
+		assertEquals(6, kr.getMatch(5).getStartPos());
+		assertEquals(7, kr.getMatch(5).getEndPos());
     }
+
+	/**
+	 * Arbitrary elements with multiple attributes.
+	 * */
+	@Test
+	public void testCase6() throws IOException {
+		ki.addDoc(createFieldDoc2());
+		ki.commit();
+
+		List<SpanQuery> sql = new ArrayList<>();
+		sql.add(new SpanAttributeQuery(new SpanTermQuery(new Term("base",
+				"@:class=header")), true));
+		sql.add(new SpanAttributeQuery(new SpanTermQuery(new Term("base",
+				"@:class=book")), true));
+
+		SpanWithAttributeQuery swaq = new SpanWithAttributeQuery(sql, true);
+		kr = ki.search(swaq, (short) 10);
+		assertEquals(2, kr.getTotalResults());
+
+		assertEquals(0, kr.getMatch(0).getStartPos());
+		assertEquals(3, kr.getMatch(0).getEndPos());
+		assertEquals(4, kr.getMatch(1).getStartPos());
+		assertEquals(5, kr.getMatch(1).getEndPos());
+
+//		for (int i = 0; i < kr.getTotalResults(); i++) {
+//			System.out.println(kr.getMatch(i).getLocalDocID() + " "
+//					+ kr.getMatch(i).startPos + " " + kr.getMatch(i).endPos);
+//		}
+	}
+
+	/**
+	 * Arbitrary elements with an attribute and a not attribute.
+	 * */
+	@Test
+	public void testCase7() throws IOException {
+		ki.addDoc(createFieldDoc2());
+		ki.commit();
+
+		List<SpanQuery> sql = new ArrayList<>();
+		sql.add(new SpanAttributeQuery(new SpanTermQuery(new Term("base",
+				"@:class=header")), true, true));
+		sql.add(new SpanAttributeQuery(new SpanTermQuery(new Term("base",
+				"@:class=book")), true));
+
+		SpanWithAttributeQuery swaq = new SpanWithAttributeQuery(sql, true);
+		kr = ki.search(swaq, (short) 10);
+		assertEquals(4, kr.getTotalResults());
+
+		assertEquals(0, kr.getMatch(0).getStartPos());
+		assertEquals(5, kr.getMatch(0).getEndPos());
+		assertEquals(1, kr.getMatch(1).getStartPos());
+		assertEquals(2, kr.getMatch(1).getEndPos());
+		assertEquals(2, kr.getMatch(2).getStartPos());
+		assertEquals(5, kr.getMatch(2).getEndPos());
+		assertEquals(6, kr.getMatch(3).getStartPos());
+		assertEquals(7, kr.getMatch(3).getEndPos());
+
+//		for (int i = 0; i < kr.getTotalResults(); i++) {
+//			System.out.println(kr.getMatch(i).getLocalDocID() + " "
+//					+ kr.getMatch(i).startPos + " " + kr.getMatch(i).endPos);
+//		}
+	}
+
+	/**
+	 * Arbitrary elements with only not attributes.
+	 * */
+	@Test(expected = IllegalArgumentException.class)
+	public void testCase8() throws IOException {
+		ki.addDoc(createFieldDoc2());
+		ki.commit();
+
+		List<SpanQuery> sql = new ArrayList<>();
+		sql.add(new SpanAttributeQuery(new SpanTermQuery(new Term("base",
+				"@:class=header")), true, true));
+		sql.add(new SpanAttributeQuery(new SpanTermQuery(new Term("base",
+				"@:class=book")), true, true));
+
+		SpanWithAttributeQuery swaq = new SpanWithAttributeQuery(sql, true);
+		kr = ki.search(swaq, (short) 10);
+	}
+
 }