Meet warnings

Change-Id: I0ad1a67f1f352e8a691cd8f0dabae89fd9bf31f4
Reviewed-on: https://korap.ids-mannheim.de/gerrit/c/KorAP/Koral/+/7383
Reviewed-by: Nils Diewald <nils@diewald-online.de>
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/Antlr4AbstractQueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/Antlr4AbstractQueryProcessor.java
index 1ea35cf..ded5d21 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/Antlr4AbstractQueryProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/Antlr4AbstractQueryProcessor.java
@@ -100,6 +100,21 @@
         return false;
     }
 
+    protected boolean hasAncestorWithCat (ParseTree node, String childCat) {
+        
+        if (node.getParent()!=null)    
+        {
+            ParseTree ancestor = node.getParent();
+            if (getNodeCat(ancestor).equals(childCat)) {
+                return true;
+            }
+            if (hasAncestorWithCat(ancestor, childCat)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
 
     /**
      * Returns all children of a node.
diff --git a/src/main/java/de/ids_mannheim/korap/query/serialize/CQPQueryProcessor.java b/src/main/java/de/ids_mannheim/korap/query/serialize/CQPQueryProcessor.java
index fb41fe9..c62987a 100644
--- a/src/main/java/de/ids_mannheim/korap/query/serialize/CQPQueryProcessor.java
+++ b/src/main/java/de/ids_mannheim/korap/query/serialize/CQPQueryProcessor.java
@@ -476,41 +476,48 @@
         }
 
         if ((foffs1 > 0 && soffs1 > 0 && ooffs1 > 0) ||
-            (foffs1 > 0 && soffs1 > 0 && ooffs1 < 0) ||
+            (foffs1 < 0 && soffs1 < 0 && ooffs1 < 0) ||
             (foffs1 > 0 && soffs1 == 0 && ooffs1 > 0) ||
             (foffs1 < 0 && soffs1 == 0 && ooffs1 < 0)) { 
             if (Math.abs(ooffs1) - Math.abs(foffs1) - 1 >= 0) {
                 distance = Math.abs(ooffs1)-Math.abs(foffs1)-1;
             } else {
-                System.out.println("Incompatible offset values! The differene between the absolute values of the outer offsets and the first inner offsets should not be smaller than 1!");
+                addError(StatusCodes.MALFORMED_QUERY, "Incompatible offset values. The difference between the absolute values of the outer offsets and the first inner offsets should not be smaller than 1.");
+                
             }
         } else {
-        	if ((foffs1 < 0 && soffs1 >0 && ooffs1 > 0) ||
-                (foffs1 < 0 && soffs1 ==0 && ooffs1 > 0) ||
-                (foffs1 > 0 && soffs1 ==0 && ooffs1 < 0) ||
-                (foffs1 == 0 && soffs1 >0 && ooffs1 > 0)) { 
+        	if ((foffs1 > 0 && soffs1 < 0 && ooffs1 < 0) ||
+                (foffs1 < 0 && soffs1 > 0 && ooffs1 > 0) ||    
+                (foffs1 < 0 && soffs1 == 0 && ooffs1 > 0) ||
+                (foffs1 > 0 && soffs1 == 0 && ooffs1 < 0) ||
+                (foffs1 == 0 && soffs1 > 0 && ooffs1 > 0) ||
+                (foffs1 == 0 && soffs1 < 0 && ooffs1 < 0) ||
+                (foffs1 == 0 && soffs1 < 0 && ooffs1 > 0)) { 
                 if (Math.abs(ooffs1) - 1 >= 0) {
                     distance = Math.abs(ooffs1)-1;
                 } else {
-                    // this is also checked in the processMeetUnion function, when comparing offsets with zero
-                    System.out.println("Incompatible offset values! The absolute values of the outer offsets should not be smaller than 1!");
+                    // not necessary, this is also checked in the processMeetUnion function, when comparing offsets with zero
+                    //System.out.println("Incompatible offset values! The absolute values of the outer offsets should not be smaller than 1!");
                 }
             } else {
-        		if (foffs1 < 0 && soffs1 > 0 && ooffs1 < 0) { 
+        		if ((foffs1 < 0 && soffs1 > 0 && ooffs1 < 0) ||
+                    (foffs1 > 0 && soffs1 < 0 && ooffs1 > 0))  { 
                     if (Math.abs(ooffs1) - Math.abs(foffs1) - Math.abs(soffs1) - 1 >= 0) {
                         distance = Math.abs(ooffs1) - Math.abs(foffs1) - Math.abs(soffs1) - 1;
                     } else {
-                        System.out.println("Incompatible offset values! The differene between the absolute values of the outer offsets and the sum of the first inner and the second inner offsets should not be smaller than 1!");
+                        addError(StatusCodes.MALFORMED_QUERY, "Incompatible offset values. The difference between the absolute values of the outer offsets and the sum of the first inner and the second inner offsets should not be smaller than 1.");
                     }
                 } else {
-        			if (foffs1 == 0 && soffs1 > 0 && ooffs1 < 0) { 
+        			if ((foffs1 == 0 && soffs1 > 0 && ooffs1 < 0) ||
+                        (foffs1 > 0 && soffs1 > 0 && ooffs1 < 0) ||
+                        (foffs1 < 0 && soffs1 < 0 && ooffs1 > 0)) { 
                         if (Math.abs(ooffs1) - Math.abs(soffs1) - 1 >= 0) {
                             distance = Math.abs(ooffs1) - Math.abs(soffs1) - 1;
                         } else {
-                            System.out.println("Incompatible offset values! The differene between the absolute values of the outer offsets and the second inner offsets should not be smaller than 1!");
+                            addError(StatusCodes.MALFORMED_QUERY, "Incompatible offset values. The difference between the absolute values of the outer offsets and the second inner offsets should not be smaller than 1.");
                         }
                     } else {
-        				System.out.println("Incompatible offset values! See the cqp tutorial!");
+        				addError(StatusCodes.MALFORMED_QUERY, "Incompatible offset values! See the cqp tutorial!");
         			}
         		}
         	}
@@ -542,11 +549,11 @@
             }; // if fails, it is a meet span
 
             if ((window[0]==0)||(window[1]==0)) {
-                addError(StatusCodes.MALFORMED_QUERY,"The MeetUnion offsets cannot be 0!!");
+                addError(StatusCodes.MALFORMED_QUERY,"The MeetUnion offsets cannot be 0.");
                 return;
             } else {
                 if (window[0] > window[1]) {
-                    addError(StatusCodes.MALFORMED_QUERY, "Left meetunion offset is bigger than the right one!");
+                    addError(StatusCodes.MALFORMED_QUERY, "Left meetunion offset cannot be bigger than the right one.");
                     return;
                 } else {
                     /****  correct offsets ****/
@@ -633,93 +640,112 @@
         				}
                     } else {
                         /*** the offsets are not equal; we don't use putvisited here, because we did not implement recursive meet for this situation ***/
-                        if (recurrence==true) {
-                            addError(StatusCodes.MALFORMED_QUERY,"We did not implement recursive meet with different offsets!!");
-                            return;
-                        } else {
-                            if ((window[0] < 0) && (window[1] > 0)) {
+                        // i want warnings and still want the query tree to be complete with disjunction
+                        if (recurrence==true) {  
+                            addError(StatusCodes.MALFORMED_QUERY,"Recursiveness is not stable for meet queries with different offsets.");
+                            // addWarning("Recursiveness is not stable for meet queries with different offsets.");
+                          //  return;
+                        } 
+                        else {
+                            if (hasAncestorWithCat(node,"meetunion"))
+                            {
+                                // the current node has a recursive meet node as ancestor
+                                    addError(StatusCodes.MALFORMED_QUERY, "Recursiveness is not stable for meet queries with different offsets.");
+                                    // addWarning("Recursiveness is not stable for meet queries with different offsets.");
+                            //        return;
+                            }
+                        } 
+                         //   else {
+                        if ((window[0] < 0) && (window[1] > 0)) {
 
-                                /***  implementing disjunction of sequences ***/
+                                    /***  implementing disjunction of sequences ***/
         				    
-                                Map<String, Object> disjunction = KoralObjectGenerator.makeGroup(KoralOperation.DISJUNCTION);
-                                putIntoSuperObject(disjunction);
-                                objectStack.push(disjunction);
-                                stackedObjects++;
+                                    Map<String, Object> disjunction = KoralObjectGenerator.makeGroup(KoralOperation.DISJUNCTION);
+                                    putIntoSuperObject(disjunction);
+                                    objectStack.push(disjunction);
+                                    stackedObjects++;
         		
-                                /**** first disjunct, keep order of segments, distance is []{0,window[1]} ****/
+                                    /**** first disjunct, keep order of segments, distance is []{0,window[1]} ****/
         				    
-                                Map<String, Object> firstsequence = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
-                                putIntoSuperObject(firstsequence);
-                                objectStack.push(firstsequence);
-                                stackedObjects++;
-                                processNode(firstsegment, false);
-                                if (window[1] > 1) {
-                                    ArrayList<Object> distances = new ArrayList<Object>();
-                                    firstsequence.put("distances", distances);
-                                    ((ArrayList<Object>) firstsequence.get("distances")).add(
-                                        KoralObjectGenerator.makeDistance("w", 0, window[1]-1)
-                                        );
-                                }
-                                processNode(secondsegment, false);
+                                    Map<String, Object> firstsequence = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
+                                    putIntoSuperObject(firstsequence);
+                                    objectStack.push(firstsequence);
+                                    stackedObjects++;
+                                    processNode(firstsegment, false);
+                                    if (window[1] > 1) {
+                                        ArrayList<Object> distances = new ArrayList<Object>();
+                                        firstsequence.put("distances", distances);
+                                        ((ArrayList<Object>) firstsequence.get("distances")).add(
+                                            KoralObjectGenerator.makeDistance("w", 0, window[1]-1)
+                                            );
+                                        }
+                                    processNode(secondsegment, false);
 				
-                                /*** second disjunct, change order of segments, distance is []{0,-window[0]} ***/
+                                    /*** second disjunct, change order of segments, distance is []{0,-window[0]} ***/
         				    
-                                Map<String, Object> secondsequence = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
-                                objectStack.push(secondsequence);
-                                stackedObjects++;
-                                ((ArrayList<Object>) disjunction.get("operands")).add(secondsequence);
-                                processNode(secondsegment, true);
-                                if (window[0] < -1) {
-                                    ArrayList<Object> distances = new ArrayList<Object>();
-                                    secondsequence.put("distances", distances);
-                                    ((ArrayList<Object>) secondsequence.get("distances")).add(
-                                        KoralObjectGenerator.makeDistance("w", 0, (-window[0])-1)
-                                        );
-                                }
-                                processNode(firstsegment, true);
-                            } else {
-                                /**** offsets are either both > 0 or both < 0 ****/
-                                Map<String, Object> sequence = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
-                                putIntoSuperObject(sequence);
-                                objectStack.push(sequence);
-                                stackedObjects++;
+                                    Map<String, Object> secondsequence = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
+                                    objectStack.push(secondsequence);
+                                    stackedObjects++;
+                                    ((ArrayList<Object>) disjunction.get("operands")).add(secondsequence);
+                                    processNode(secondsegment, true);
+                                    if (window[0] < -1) {
+                                        ArrayList<Object> distances = new ArrayList<Object>();
+                                        secondsequence.put("distances", distances);
+                                        ((ArrayList<Object>) secondsequence.get("distances")).add(
+                                            KoralObjectGenerator.makeDistance("w", 0, (-window[0])-1)
+                                            );
+                                         }
+                                    processNode(firstsegment, true);
+                                    } else {
+                                    /**** offsets are either both > 0 or both < 0 ****/
+                                    Map<String, Object> sequence = KoralObjectGenerator.makeGroup(KoralOperation.SEQUENCE);
+                                    putIntoSuperObject(sequence);
+                                    objectStack.push(sequence);
+                                    stackedObjects++;
                             
-                                if (window[0] > 0 && window[1] > 0) {
+                                    if (window[0] > 0 && window[1] > 0) {
                                     /*** both offsets are positive ***/
                                     /**** first meet segment ****/
                                			
-                                    processNode(firstsegment, true);
-                                    ArrayList<Object> distances = new ArrayList<Object>();
-                                    sequence.put("distances", distances);
-                                    ((ArrayList<Object>) sequence.get("distances")).add(
-                                        KoralObjectGenerator.makeDistance("w", window[0]-1, window[1]-1)
-                                        );
+                                        processNode(firstsegment, true);
+                                        ArrayList<Object> distances = new ArrayList<Object>();
+                                        sequence.put("distances", distances);
+                                        ((ArrayList<Object>) sequence.get("distances")).add(
+                                            KoralObjectGenerator.makeDistance("w", window[0]-1, window[1]-1)
+                                            );
             					
-                                    processNode(secondsegment, true);
-                                } else {
+                                        processNode(secondsegment, true);
+                                    } else {
                                     /*** both offsets are negative; change order of segments ****/
                                     /**** second meet segment ****/
     				         		
-                                    processNode(secondsegment, true);
+                                        processNode(secondsegment, true);
     				            
-                                    ArrayList<Object> distances = new ArrayList<Object>();
-                                    sequence.put("distances", distances);
-                                    ((ArrayList<Object>) sequence.get("distances")).add(
-                                        KoralObjectGenerator.makeDistance("w", (-window[1])-1,(-window[0])-1)
-                                        );
+                                        ArrayList<Object> distances = new ArrayList<Object>();
+                                        sequence.put("distances", distances);
+                                        ((ArrayList<Object>) sequence.get("distances")).add(
+                                            KoralObjectGenerator.makeDistance("w", (-window[1])-1,(-window[0])-1)
+                                            );
 
-                                    /**** first meet segment ******/
-                                    processNode(firstsegment, true);
+                                        /**** first meet segment ******/
+                                        processNode(firstsegment, true);
+                                    }
                                 }
                             }
                         }
                     }
-                }
-            }
+              //  }
+           // }
         } catch (NumberFormatException nfe) {
     	
             /**** process meet  with span ***/
-    	
+            if (hasAncestorWithCat(node,"meetunion"))
+                {
+                    // the current node has a recursive meet node as ancestor
+                    addError(StatusCodes.MALFORMED_QUERY, "Recursiveness is not stable for span meet queries as an inner query.");
+                    // addWarning("Recursiveness is not stable for span meet queries as an inner query.");
+                            //        return;
+                }
             ParseTree firstsequence = firstsegment;
             ParseTree secondsequence = secondsegment;
             Map<String, Object> position = parseFrame(node.getChild(node.getChildCount()-1));
diff --git a/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPMeetProcessorTest.java b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPMeetProcessorTest.java
index 5fe4391..2945ed5 100644
--- a/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPMeetProcessorTest.java
+++ b/src/test/java/de/ids_mannheim/korap/query/test/cqp/CQPMeetProcessorTest.java
@@ -5,6 +5,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 
+import org.junit.Ignore;
 import org.junit.Test;
 // import org.junit.Ignore;
 
@@ -24,28 +25,201 @@
     QuerySerializer qs = new QuerySerializer();
     ObjectMapper mapper = new ObjectMapper();
     JsonNode res;
+
+
     
     @Test
-    public void testMeetVsFocusErrors () throws JsonProcessingException, IOException {
+    public void testMeetErrors () throws JsonProcessingException, IOException {
+      // both error and warnings + query tree are returned;
+     
+     
+      // tests for recursive queries with different offsets;     
+       query = "MU(meet (meet \"in\" \"due\" 1 1) \"course\" -3 1))";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/errors/0/1").asText());
+       // assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/warnings/0/0").asText());
+       // query is returned
+       assertEquals(0, res.at("/query/operands/0/operands/0/distances/0/boundary/min").asInt());
+       
+       query = "MU(meet (meet \"in\" \"due\" -1 1) \"course\" -3 1))";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/errors/0/1").asText()); 
+       // assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/warnings/0/0").asText());
+       // query is returned
+       assertEquals(0, res.at("/query/operands/0/operands/0/distances/0/boundary/min").asInt());
+
+       query = "MU(meet (meet \"in\" \"due\" -1 1) \"course\" -3 -3))";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/errors/0/1").asText()); 
+       // assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/warnings/0/0").asText());
+       // query is returned
+       assertEquals(0, res.at("/query/operands/0/operands/0/distances/0/boundary/min").asInt());
+       
+       query = "MU(meet (meet \"x\" \"y\" -1 1) \"due\" 1 1) \"course\" -3 -3))";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/errors/0/1").asText()); 
+       // query is returned
+       assertEquals(0, res.at("/query/operands/0/operands/0/distances/0/boundary/min").asInt());
+
+       query = "MU(meet (meet \"in\" \"due\" -3 2) (meet \"time\" \".*\" -1 1) -4 4);";
+       qs.setQuery(query, "CQP");
+       //assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(0, res.at("/query/operands/0/operands/0/distances/0/boundary/min").asInt());
+       assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/errors/0/1").asText()); 
+     
+     // tests for zero offsets
+     
        query = "MU(meet \"in\" \"due\" 0 -1);";
        qs.setQuery(query, "CQP");
        assertTrue(qs.hasErrors());
        res = mapper.readTree(qs.toJSON());
        assertEquals(302, res.at("/errors/0/0").asInt());;
-       assertEquals("The MeetUnion offsets cannot be 0!!", res.at("/errors/0/1").asText());
+       assertEquals("The MeetUnion offsets cannot be 0.", res.at("/errors/0/1").asText());
+
+       // first processes the outer meet and calls computedistance on it, adding "icompatible offset values" error
+       // and only later processes the firt inner meet and adds "The MeetUnion offsets cannot be 0" error.
+       query = "MU(meet (meet \"x\" \"y\" 0 1) (meet \"z\" \"w\" 1 1) -1 -1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/1/0").asInt());;
+       assertEquals("The MeetUnion offsets cannot be 0.", res.at("/errors/1/1").asText());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the second inner offsets should not be smaller than 1.", res.at("/errors/0/1").asText());
+
+       
+       query = "MU(meet (meet \"x\" \"y\" 0 0) (meet \"z\" \"w\" 1 1) -1 -1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/1/0").asInt());;
+       assertEquals("The MeetUnion offsets cannot be 0.", res.at("/errors/1/1").asText());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the second inner offsets should not be smaller than 1.", res.at("/errors/0/1").asText());
+
+       // does'n get to computeDistance for the outer offsets
+       query = "MU(meet (meet \"x\" \"y\" 1 1) (meet \"z\" \"w\" 1 1) 0 -1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("The MeetUnion offsets cannot be 0.", res.at("/errors/0/1").asText());
+       assertNotEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the second inner offsets should not be smaller than 1.", res.at("/errors/1/1").asText());
+       
+       query = "MU(meet (meet \"x\" \"y\" 2 2) (meet \"z\" \"w\" 1 1) 0 0);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertNotEquals(302, res.at("/errors/1/0").asInt());;
+       assertNotEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the first inner offsets should not be smaller than 1.", res.at("/errors/1/1").asText());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("The MeetUnion offsets cannot be 0.", res.at("/errors/0/1").asText());
+     
+       query = "MU(meet (meet \"x\" \"y\" 2 2) (meet \"z\" \"w\" -1 -1) -1 -1);";
+       qs.setQuery(query, "CQP");
+       assertFalse(qs.hasErrors());
+
+       query = "MU(meet (meet \"x\" \"y\" 2 2) (meet \"z\" \"w\" 0 0) -1 -1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("The MeetUnion offsets cannot be 0.", res.at("/errors/0/1").asText());
+
+       
+       // tests for equal offsets; distance incompatibilities, see computeSegmentDistance()
+       // foffs = first ofsetss; soffs = second offsets; oofs = outer offsets;
+     
+     // Math.abs(ooffs1) - Math.abs(foffs1) - Math.abs(soffs1) - 1 >= 0  
+       query = "MU(meet (meet \"x\" \"y\" -2 -2) (meet \"z\" \"w\" 1 1) -1 -1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the sum of the first inner and the second inner offsets should not be smaller than 1.", res.at("/errors/0/1").asText());
+      
+       query = "MU(meet (meet \"x\" \"y\" 2 2) (meet \"z\" \"w\" -1 -1) 1 1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the sum of the first inner and the second inner offsets should not be smaller than 1.", res.at("/errors/0/1").asText());
+     
+       // (Math.abs(ooffs1) - Math.abs(soffs1) - 1 >= 0)
+       query = "MU(meet \"x\" (meet \"z\" \"w\" 1 1) -1 -1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the second inner offsets should not be smaller than 1.", res.at("/errors/0/1").asText());
+
+       query = "MU(meet (meet \"x\" \"y\" 2 2) (meet \"z\" \"w\" 1 1) -1 -1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the second inner offsets should not be smaller than 1.", res.at("/errors/0/1").asText());
+
+       query = "MU(meet (meet \"x\" \"y\" -2 -2) (meet \"z\" \"w\" -1 -1) 1 1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the second inner offsets should not be smaller than 1.", res.at("/errors/0/1").asText());
+
+       // (Math.abs(ooffs1) - Math.abs(foffs1) - 1 >= 0)
+
+       query = "MU(meet (meet \"x\" \"y\" 2 2) (meet \"z\" \"w\" 1 1) 1 1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the first inner offsets should not be smaller than 1.", res.at("/errors/0/1").asText());
+
+       query = "MU(meet (meet \"x\" \"y\" -2 -2) (meet \"z\" \"w\" -1 -1) -1 -1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the first inner offsets should not be smaller than 1.", res.at("/errors/0/1").asText());
+
+       query = "MU(meet (meet \"x\" \"y\" -2 -2) \"z\" -1 -1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the first inner offsets should not be smaller than 1.", res.at("/errors/0/1").asText());
+
+       query = "MU(meet (meet \"x\" \"y\" 2 2) \"z\" 1 1);";
+       qs.setQuery(query, "CQP");
+       assertTrue(qs.hasErrors());
+       res = mapper.readTree(qs.toJSON());
+       assertEquals(302, res.at("/errors/0/0").asInt());;
+       assertEquals("Incompatible offset values. The difference between the absolute values of the outer offsets and the first inner offsets should not be smaller than 1.", res.at("/errors/0/1").asText());
+
+       
+
+
   }
     
     
     @Test
     public void testFocusMeetDisjunction() throws JsonProcessingException, IOException {     
     	
-        // ((color de | de color) pelo ) | (pelo (color de | de color))
-        query = "MU(meet(meet \"color\" \"de\" -1 1) \"pelo\"  -4 4);";
-    	   qs.setQuery(query, "CQP");
-        res = mapper.readTree(qs.toJSON());
-        assertEquals(302, res.at("/errors/0/0").asInt());;
-        assertEquals("We did not implement recursive meet with different offsets!!", res.at("/errors/0/1").asText()); // any ideea for a better message here?
-        
+       
      
         /*query = "focus('der' {'Baum'}) | focus ({'Baum'} 'der');";*/
         query = "MU(meet \"Baum\" \"der\" -1 1);";
@@ -177,12 +351,16 @@
    	
    @Test
    public void testMeetSpan2 () throws JsonProcessingException, IOException {
-   	    
-     //"in due" | "due in" []* time in a span s
      
+     //"in due" | "due in" []* time in a span s
+     // for this type of query, at the moment it throws a warning, an error and also returns the tree; 
+     // this could be an useful type of query (this specific tree is correct), but also if the user complicates it with more level of imbrication, it becomes unstable/uncovered in the code;
+     // is it necessary to have both an error and warning message?
      query = "MU(meet (meet \"in\" \"due\" -1 1) \"time\" s)";  
      qs.setQuery(query, "CQP");
      res = mapper.readTree(qs.toJSON());
+     assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/errors/0/1").asText());
+     // assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/warnings/0/0").asText());
      assertEquals("koral:reference", res.at("/query/@type").asText());
      assertEquals("operation:focus", res.at("/query/operation").asText());
      assertEquals(1, res.at("/query/classRef/0").asInt());
@@ -212,7 +390,7 @@
      assertEquals(1, res.at("/meta/highlight/1").asInt());
      
      
-     //"piel" []* "azul" in a span np,  folowed by "de" at any distance, in a span s
+     //"piel" []* "azul" in a span np,  folowed by "de" at any distance, in a span s, inOrder = false on both levels
          query = "MU(meet (meet \"piel\" \"azul\" np)  \"de\" s)";
          qs.setQuery(query, "CQP");
          res = mapper.readTree(qs.toJSON());
@@ -288,6 +466,29 @@
 	    assertEquals("piel", res.at("/query/operands/0/operands/1/operands/1/wrap/key").asText());
          assertEquals("type:string", res.at("/query/operands/0/operands/1/operands/1/wrap/type").asText());
 	   
+           // the querry tree is not correct here! this situations are covered just with warning/error;
+          query = "MU(meet (meet \"in\" \"due\" s) \"time\" 2 2)";  
+          qs.setQuery(query, "CQP");
+          res = mapper.readTree(qs.toJSON());
+          assertEquals("Recursiveness is not stable for span meet queries as an inner query.", res.at("/errors/0/1").asText());
+          // assertEquals("Recursiveness is not stable for span meet queries as an inner query.", res.at("/warnings/0/0").asText());
+          assertEquals("koral:reference", res.at("/query/@type").asText());
+         
+          query = "MU(meet (meet \"in\" \"due\" s) \"time\" -2 2)";  
+          qs.setQuery(query, "CQP");
+          res = mapper.readTree(qs.toJSON());
+          assertEquals("302", res.at("/errors/0/0").asText());
+          assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/errors/0/1").asText());
+          // assertEquals("Recursiveness is not stable for meet queries with different offsets.", res.at("/warnings/0/0").asText());
+          assertEquals("302", res.at("/errors/1/0").asText());
+          // the span warning/error is thrown for each term of the disjunction
+          assertEquals("Recursiveness is not stable for span meet queries as an inner query.", res.at("/errors/1/1").asText());
+          // assertEquals("Recursiveness is not stable for span meet queries as an inner query.", res.at("/warnings/1/0").asText());
+          assertEquals("302", res.at("/errors/2/0").asText());
+          assertEquals("Recursiveness is not stable for span meet queries as an inner query.", res.at("/errors/2/1").asText());
+          // assertEquals("Recursiveness is not stable for span meet queries as an inner query.", res.at("/warnings/2/0").asText());
+
+          assertEquals("koral:reference", res.at("/query/@type").asText()); 
    }
 
    // looking for 2 words with different/variable distances in between