You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2014/10/09 17:06:15 UTC

[08/19] git commit: Line number tracking and exception messages

Line number tracking and exception messages

Signed-off-by: Christian Amend <ch...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/olingo-odata2/repo
Commit: http://git-wip-us.apache.org/repos/asf/olingo-odata2/commit/f0dc0f74
Tree: http://git-wip-us.apache.org/repos/asf/olingo-odata2/tree/f0dc0f74
Diff: http://git-wip-us.apache.org/repos/asf/olingo-odata2/diff/f0dc0f74

Branch: refs/heads/master
Commit: f0dc0f74593a3184c84dbf43337b23ec52f57abb
Parents: 05d20c1
Author: Christian Holzer <c....@sap.com>
Authored: Mon Oct 6 13:01:40 2014 +0200
Committer: Christian Amend <ch...@apache.org>
Committed: Mon Oct 6 13:54:39 2014 +0200

----------------------------------------------------------------------
 .../olingo/odata2/api/batch/BatchException.java |    2 +-
 .../odata2/core/batch/v2/BatchBodyPart.java     |   31 +-
 .../core/batch/v2/BatchChangeSetPart.java       |    7 +-
 .../odata2/core/batch/v2/BatchParser.java       |   11 +-
 .../odata2/core/batch/v2/BatchParserCommon.java |  101 +-
 .../core/batch/v2/BatchQueryOperation.java      |   22 +-
 .../batch/v2/BatchRequestTransformator.java     |   74 +-
 .../batch/v2/BatchResponseTransformator.java    |   10 +-
 .../core/batch/v2/BatchTransformatorCommon.java |   31 +-
 .../v2/BufferedReaderIncludingLineEndings.java  |   60 +-
 .../olingo/odata2/core/batch/v2/Header.java     |  142 +-
 .../src/main/resources/i18n.properties          |   37 +-
 .../core/batch/BatchParserCommonTest.java       |   55 +-
 .../core/batch/BatchRequestParserTest.java      |   82 +-
 .../odata2/core/batch/BatchRequestTest.java     |    5 +-
 .../batch/BatchTransformatorCommonTest.java     |    8 +-
 .../BufferedReaderIncludingLineEndingsTest.java |   25 +-
 .../olingo/odata2/core/batch/HeaderTest.java    |   54 +-
 .../src/test/resources/batchLarge.batch         | 2505 ++++++++++++++++++
 19 files changed, 2911 insertions(+), 351 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchException.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchException.java b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchException.java
index 1171719..96aa4dd 100644
--- a/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchException.java
+++ b/odata2-lib/odata-api/src/main/java/org/apache/olingo/odata2/api/batch/BatchException.java
@@ -94,7 +94,7 @@ public class BatchException extends ODataMessageException {
   public static final MessageReference INVALID_ACCEPT_LANGUAGE_HEADER = createMessageReference(BatchException.class,
       "INVALID_ACCEPT_LANGUAGE_HEADER");
 
-  /** INVALID_CONTENT_TRANSFER_ENCODING requires no content value */
+  /** INVALID_CONTENT_TRANSFER_ENCODING requires 1 content value */
   public static final MessageReference INVALID_CONTENT_TRANSFER_ENCODING = createMessageReference(BatchException.class,
       "INVALID_CONTENT_TRANSFER_ENCODING");
 

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchBodyPart.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchBodyPart.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchBodyPart.java
index f74ea85..288ca1c 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchBodyPart.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchBodyPart.java
@@ -23,17 +23,19 @@ import java.util.List;
 
 import org.apache.olingo.odata2.api.batch.BatchException;
 import org.apache.olingo.odata2.api.commons.HttpHeaders;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
+import org.apache.olingo.odata2.core.batch.v2.Header.HeaderField;
 
 public class BatchBodyPart implements BatchPart {
   final private String boundary;
   final private boolean isStrict;
-  final List<String> remainingMessage = new LinkedList<String>();
+  final List<Line> remainingMessage = new LinkedList<Line>();
 
   private Header headers;
   private boolean isChangeSet;
   private List<BatchQueryOperation> requests;
 
-  public BatchBodyPart(final List<String> bodyPartMessage, final String boundary, final boolean isStrict)
+  public BatchBodyPart(final List<Line> bodyPartMessage, final String boundary, final boolean isStrict)
       throws BatchException {
     this.boundary = boundary;
     this.isStrict = isStrict;
@@ -55,7 +57,7 @@ public class BatchBodyPart implements BatchPart {
     boolean isChangeSet = false;
 
     if (contentTypes.size() == 0) {
-      throw new BatchException(BatchException.MISSING_CONTENT_TYPE);
+      throw new BatchException(BatchException.MISSING_CONTENT_TYPE.addContent(headers.getLineNumber()));
     }
 
     for (String contentType : contentTypes) {
@@ -71,7 +73,7 @@ public class BatchBodyPart implements BatchPart {
     return BatchParserCommon.PATTERN_MULTIPART_BOUNDARY.matcher(contentType).matches();
   }
 
-  private List<BatchQueryOperation> consumeRequest(final List<String> remainingMessage) throws BatchException {
+  private List<BatchQueryOperation> consumeRequest(final List<Line> remainingMessage) throws BatchException {
     if (isChangeSet) {
       return consumeChangeSet(remainingMessage);
     } else {
@@ -79,28 +81,30 @@ public class BatchBodyPart implements BatchPart {
     }
   }
 
-  private List<BatchQueryOperation> consumeChangeSet(final List<String> remainingMessage)
+  private List<BatchQueryOperation> consumeChangeSet(final List<Line> remainingMessage)
       throws BatchException {
-    final List<List<String>> changeRequests = splitChangeSet(remainingMessage);
+    final List<List<Line>> changeRequests = splitChangeSet(remainingMessage);
     final List<BatchQueryOperation> requestList = new LinkedList<BatchQueryOperation>();
 
-    for (List<String> changeRequest : changeRequests) {
+    for (List<Line> changeRequest : changeRequests) {
       requestList.add(new BatchChangeSetPart(changeRequest, isStrict).parse());
     }
 
     return requestList;
   }
 
-  private List<List<String>> splitChangeSet(final List<String> remainingMessage)
+  private List<List<Line>> splitChangeSet(final List<Line> remainingMessage)
       throws BatchException {
 
-    final String changeSetBoundary = BatchParserCommon.getBoundary(headers.getHeaderNotNull(HttpHeaders.CONTENT_TYPE));
-    validateChangeSetBoundary(changeSetBoundary);
+    final HeaderField contentTypeField = headers.getHeaderField(HttpHeaders.CONTENT_TYPE);
+    final String changeSetBoundary =
+        BatchParserCommon.getBoundary(contentTypeField.getValueNotNull(), contentTypeField.getLineNumber());
+    validateChangeSetBoundary(changeSetBoundary, headers);
 
     return BatchParserCommon.splitMessageByBoundary(remainingMessage, changeSetBoundary);
   }
 
-  private List<BatchQueryOperation> consumeQueryOperation(final List<String> remainingMessage)
+  private List<BatchQueryOperation> consumeQueryOperation(final List<Line> remainingMessage)
       throws BatchException {
     final List<BatchQueryOperation> requestList = new LinkedList<BatchQueryOperation>();
     requestList.add(new BatchQueryOperation(remainingMessage, isStrict).parse());
@@ -108,9 +112,10 @@ public class BatchBodyPart implements BatchPart {
     return requestList;
   }
 
-  private void validateChangeSetBoundary(final String changeSetBoundary) throws BatchException {
+  private void validateChangeSetBoundary(final String changeSetBoundary, Header header) throws BatchException {
     if (changeSetBoundary.equals(boundary)) {
-      throw new BatchException(BatchException.INVALID_BOUNDARY);
+      throw new BatchException(BatchException.INVALID_BOUNDARY.addContent(header.getHeaderField(
+          HttpHeaders.CONTENT_TYPE).getLineNumber()));
     }
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchChangeSetPart.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchChangeSetPart.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchChangeSetPart.java
index 746c368..f3b0699 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchChangeSetPart.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchChangeSetPart.java
@@ -21,11 +21,12 @@ package org.apache.olingo.odata2.core.batch.v2;
 import java.util.List;
 
 import org.apache.olingo.odata2.api.batch.BatchException;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
 
 public class BatchChangeSetPart extends BatchQueryOperation {
   private BatchQueryOperation request;
 
-  public BatchChangeSetPart(final List<String> message, final boolean isStrict) throws BatchException {
+  public BatchChangeSetPart(final List<Line> message, final boolean isStrict) throws BatchException {
     super(message, isStrict);
   }
 
@@ -44,12 +45,12 @@ public class BatchChangeSetPart extends BatchQueryOperation {
   }
 
   @Override
-  public List<String> getBody() {
+  public List<Line> getBody() {
     return request.getBody();
   }
 
   @Override
-  public String getHttpStatusLine() {
+  public Line getHttpStatusLine() {
     return request.getHttpStatusLine();
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParser.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParser.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParser.java
index 6fb7dbd..00a1f2a 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParser.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParser.java
@@ -31,6 +31,7 @@ import org.apache.olingo.odata2.api.client.batch.BatchSingleResponse;
 import org.apache.olingo.odata2.api.ep.EntityProviderBatchProperties;
 import org.apache.olingo.odata2.api.uri.PathInfo;
 import org.apache.olingo.odata2.api.uri.PathSegment;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
 import org.apache.olingo.odata2.core.exception.ODataRuntimeException;
 
 public class BatchParser {
@@ -78,11 +79,11 @@ public class BatchParser {
       final BatchTransformator transformator) throws BatchException, IOException {
 
     final String baseUri = getBaseUri();
-    final String boundary = BatchParserCommon.getBoundary(contentTypeMime);
+    final String boundary = BatchParserCommon.getBoundary(contentTypeMime, 1);
     final List<BatchParserResult> resultList = new LinkedList<BatchParserResult>();
-    final List<List<String>> bodyPartStrings = splitBodyParts(in, boundary);
+    final List<List<Line>> bodyPartStrings = splitBodyParts(in, boundary);
 
-    for (List<String> bodyPartString : bodyPartStrings) {
+    for (List<Line> bodyPartString : bodyPartStrings) {
       BatchBodyPart bodyPart = new BatchBodyPart(bodyPartString, boundary, isStrict).parse();
       resultList.addAll(transformator.transform(bodyPart, batchRequestPathInfo, baseUri));
     }
@@ -90,11 +91,11 @@ public class BatchParser {
     return resultList;
   }
 
-  private List<List<String>> splitBodyParts(final InputStream in, final String boundary)
+  private List<List<Line>> splitBodyParts(final InputStream in, final String boundary)
       throws IOException, BatchException {
 
     final BufferedReaderIncludingLineEndings reader = new BufferedReaderIncludingLineEndings(new InputStreamReader(in));
-    final List<String> message = reader.toList();
+    final List<Line> message = reader.toList();
     reader.close();
 
     return BatchParserCommon.splitMessageByBoundary(message, boundary);

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java
index df62994..b028759 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchParserCommon.java
@@ -40,6 +40,7 @@ import org.apache.olingo.odata2.api.uri.PathSegment;
 import org.apache.olingo.odata2.core.ODataPathSegmentImpl;
 import org.apache.olingo.odata2.core.PathInfoImpl;
 import org.apache.olingo.odata2.core.batch.AcceptParser;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
 import org.apache.olingo.odata2.core.commons.Decoder;
 
 public class BatchParserCommon {
@@ -55,53 +56,52 @@ public class BatchParserCommon {
   public static final Pattern PATTERN_CONTENT_TYPE_APPLICATION_HTTP = Pattern.compile(REG_EX_APPLICATION_HTTP,
       Pattern.CASE_INSENSITIVE);
 
-  public static String trimStringListToStringLength(final List<String> list, final int length) {
+  public static String trimLineListToLength(final List<Line> list, final int length) {
     final String message = stringListToString(list);
     final int lastIndex = Math.min(length, message.length());
 
     return (lastIndex > 0) ? message.substring(0, lastIndex) : "";
   }
 
-  public static String stringListToString(final List<String> list) {
+  public static String stringListToString(final List<Line> list) {
     StringBuilder builder = new StringBuilder();
 
-    for (String currentLine : list) {
-      builder.append(currentLine);
+    for (Line currentLine : list) {
+      builder.append(currentLine.toString());
     }
 
     return builder.toString();
   }
 
-  public static InputStream convertMessageToInputStream(final List<String> messageList, final int contentLength)
+  public static InputStream convertMessageToInputStream(final List<Line> messageList, final int contentLength)
       throws BatchException {
-    final String message = trimStringListToStringLength(messageList, contentLength);
+    final String message = trimLineListToLength(messageList, contentLength);
 
     return new ByteArrayInputStream(message.getBytes());
   }
 
-  public static InputStream convertMessageToInputStream(final List<String> messageList)
+  public static InputStream convertMessageToInputStream(final List<Line> messageList)
       throws BatchException {
     final String message = stringListToString(messageList);
 
     return new ByteArrayInputStream(message.getBytes());
   }
 
-  // TODO Splitten von InputStream, sodass nur eine Iteration erfolgen muss
-  static List<List<String>> splitMessageByBoundary(final List<String> message, final String boundary)
+  static List<List<Line>> splitMessageByBoundary(final List<Line> message, final String boundary)
       throws BatchException {
-    final List<List<String>> messageParts = new LinkedList<List<String>>();
-    List<String> currentPart = new ArrayList<String>();
+    final List<List<Line>> messageParts = new LinkedList<List<Line>>();
+    List<Line> currentPart = new ArrayList<Line>();
     boolean isEndReached = false;
 
-    for (String currentLine : message) {
-      if (currentLine.contains("--" + boundary + "--")) {
+    for (Line currentLine : message) {
+      if (currentLine.toString().contains("--" + boundary + "--")) {
         removeEndingCRLFFromList(currentPart);
         messageParts.add(currentPart);
         isEndReached = true;
-      } else if (currentLine.contains("--" + boundary)) {
+      } else if (currentLine.toString().contains("--" + boundary)) {
         removeEndingCRLFFromList(currentPart);
         messageParts.add(currentPart);
-        currentPart = new LinkedList<String>();
+        currentPart = new LinkedList<Line>();
       } else {
         currentPart.add(currentLine);
       }
@@ -111,52 +111,57 @@ public class BatchParserCommon {
       }
     }
 
+    final int lineNumer = (message.size() > 0) ? message.get(0).getLineNumber() : 0;
     // Remove preamble
     if (messageParts.size() > 0) {
       messageParts.remove(0);
     } else {
-      throw new BatchException(BatchException.MISSING_BOUNDARY_DELIMITER);
-    }
 
-    if (messageParts.size() == 0) {
-      throw new BatchException(BatchException.NO_MATCH_WITH_BOUNDARY_STRING);
+      throw new BatchException(BatchException.MISSING_BOUNDARY_DELIMITER.addContent(lineNumer));
     }
 
     if (!isEndReached) {
-      throw new BatchException(BatchException.MISSING_CLOSE_DELIMITER);
+      throw new BatchException(BatchException.MISSING_CLOSE_DELIMITER.addContent(lineNumer));
+    }
+
+    if (messageParts.size() == 0) {
+      throw new BatchException(BatchException.NO_MATCH_WITH_BOUNDARY_STRING.addContent(boundary).addContent(lineNumer));
     }
 
     return messageParts;
   }
 
-  private static void removeEndingCRLFFromList(final List<String> list) {
+  private static void removeEndingCRLFFromList(final List<Line> list) {
     if (list.size() > 0) {
-      String lastLine = list.remove(list.size() - 1);
+      Line lastLine = list.remove(list.size() - 1);
       list.add(removeEndingCRLF(lastLine));
     }
   }
 
-  public static String removeEndingCRLF(final String line) {
+  public static Line removeEndingCRLF(final Line line) {
     Pattern pattern = Pattern.compile("(.*)(\r\n){1}( *)", Pattern.DOTALL);
-    Matcher matcher = pattern.matcher(line);
+    Matcher matcher = pattern.matcher(line.toString());
 
     if (matcher.matches()) {
-      return matcher.group(1);
+      return new Line(matcher.group(1), line.getLineNumber());
     } else {
       return line;
     }
   }
 
-  public static Header consumeHeaders(final List<String> remainingMessage) throws BatchException {
-    final Header headers = new Header();
+  public static Header consumeHeaders(final List<Line> remainingMessage) throws BatchException {
+    final int lineNumberOfHeader = remainingMessage.size() != 0 ? remainingMessage.get(0).getLineNumber() : 0;
+    final Header headers = new Header(lineNumberOfHeader);
     boolean isHeader = true;
-    final Iterator<String> iter = remainingMessage.iterator();
+    final Iterator<Line> iter = remainingMessage.iterator();
     final AcceptParser acceptParser = new AcceptParser();
-    String currentLine;
+    Line currentLine;
+    int acceptLineNumber = 0;
+    int acceptLanguageLineNumber = 0;
 
     while (iter.hasNext() && isHeader) {
       currentLine = iter.next();
-      final Matcher headerMatcher = PATTERN_HEADER_LINE.matcher(currentLine);
+      final Matcher headerMatcher = PATTERN_HEADER_LINE.matcher(currentLine.toString());
 
       if (headerMatcher.matches() && headerMatcher.groupCount() == 2) {
         iter.remove();
@@ -166,34 +171,37 @@ public class BatchParserCommon {
 
         if (HttpHeaders.ACCEPT.equalsIgnoreCase(headerName)) {
           acceptParser.addAcceptHeaderValue(headerValue);
+          acceptLineNumber = currentLine.getLineNumber();
         } else if (HttpHeaders.ACCEPT_LANGUAGE.equalsIgnoreCase(headerName)) {
           acceptParser.addAcceptLanguageHeaderValue(headerValue);
+          acceptLanguageLineNumber = currentLine.getLineNumber();
         } else {
-          headers.addHeader(headerName, Header.splitValuesByComma(headerValue));
+          headers.addHeader(headerName, Header.splitValuesByComma(headerValue), currentLine.getLineNumber());
         }
       } else {
         isHeader = false;
       }
     }
 
-    headers.addHeader(HttpHeaders.ACCEPT, acceptParser.parseAcceptHeaders());
-    headers.addHeader(HttpHeaders.ACCEPT_LANGUAGE, acceptParser.parseAcceptableLanguages());
+    headers.addHeader(HttpHeaders.ACCEPT, acceptParser.parseAcceptHeaders(), acceptLineNumber);
+    headers.addHeader(HttpHeaders.ACCEPT_LANGUAGE, acceptParser.parseAcceptableLanguages(), acceptLanguageLineNumber);
 
     return headers;
   }
 
-  public static void consumeBlankLine(final List<String> remainingMessage, final boolean isStrict)
+  public static void consumeBlankLine(final List<Line> remainingMessage, final boolean isStrict)
       throws BatchException {
-    if (remainingMessage.size() > 0 && "".equals(remainingMessage.get(0).trim())) {
+    if (remainingMessage.size() > 0 && "".equals(remainingMessage.get(0).toString().trim())) {
       remainingMessage.remove(0);
     } else {
       if (isStrict) {
-        throw new BatchException(BatchException.MISSING_BLANK_LINE);
+        final int lineNumber = (remainingMessage.size() > 0) ? remainingMessage.get(0).getLineNumber() : 0;
+        throw new BatchException(BatchException.MISSING_BLANK_LINE.addContent("[None]").addContent(lineNumber));
       }
     }
   }
 
-  public static String getBoundary(final String contentType) throws BatchException {
+  public static String getBoundary(final String contentType, final int line) throws BatchException {
     final Matcher boundaryMatcher = PATTERN_MULTIPART_BOUNDARY.matcher(contentType);
 
     if (boundaryMatcher.matches()) {
@@ -201,17 +209,17 @@ public class BatchParserCommon {
       if (boundary.matches(REG_EX_BOUNDARY)) {
         return trimQuota(boundary);
       } else {
-        throw new BatchException(BatchException.INVALID_BOUNDARY);
+        throw new BatchException(BatchException.INVALID_BOUNDARY.addContent(line));
       }
     } else {
       throw new BatchException(BatchException.INVALID_CONTENT_TYPE.addContent(HttpContentType.MULTIPART_MIXED));
     }
   }
 
-  public static Map<String, List<String>> parseQueryParameter(final String httpRequest) {
+  public static Map<String, List<String>> parseQueryParameter(final Line httpRequest) {
     Map<String, List<String>> queryParameter = new HashMap<String, List<String>>();
 
-    String[] requestParts = httpRequest.split(" ");
+    String[] requestParts = httpRequest.toString().split(" ");
     if (requestParts.length == 3) {
 
       String[] parts = requestParts[1].split("\\?");
@@ -239,8 +247,8 @@ public class BatchParserCommon {
     return queryParameter;
   }
 
-  public static PathInfo parseRequestUri(final String httpRequest, final PathInfo batchRequestPathInfo,
-      final String baseUri)
+  public static PathInfo parseRequestUri(final Line httpStatusLine, final PathInfo batchRequestPathInfo,
+      final String baseUri, final int line)
       throws BatchException {
 
     final String odataPathSegmentsAsString;
@@ -250,7 +258,7 @@ public class BatchParserCommon {
     pathInfo.setServiceRoot(batchRequestPathInfo.getServiceRoot());
     pathInfo.setPrecedingPathSegment(batchRequestPathInfo.getPrecedingSegments());
 
-    String[] requestParts = httpRequest.split(" ");
+    String[] requestParts = httpStatusLine.toString().split(" ");
     if (requestParts.length == 3) {
       String uri = requestParts[1];
       Pattern regexRequestUri;
@@ -277,14 +285,15 @@ public class BatchParserCommon {
           }
 
         } else {
-          throw new BatchException(BatchException.INVALID_URI);
+          throw new BatchException(BatchException.INVALID_URI.addContent(httpStatusLine.getLineNumber()));
         }
 
       } catch (URISyntaxException e) {
-        throw new BatchException(BatchException.INVALID_URI, e);
+        throw new BatchException(BatchException.INVALID_URI.addContent(line), e);
       }
     } else {
-      throw new BatchException(BatchException.INVALID_REQUEST_LINE);
+      throw new BatchException(BatchException.INVALID_REQUEST_LINE.addContent(httpStatusLine.toString())
+          .addContent(line));
     }
 
     return pathInfo;

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchQueryOperation.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchQueryOperation.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchQueryOperation.java
index 87dcb23..9bbd019 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchQueryOperation.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchQueryOperation.java
@@ -21,17 +21,18 @@ package org.apache.olingo.odata2.core.batch.v2;
 import java.util.List;
 
 import org.apache.olingo.odata2.api.batch.BatchException;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
 
 public class BatchQueryOperation implements BatchPart {
 
   protected final boolean isStrict;
-  protected String httpStatusLine;
+  protected Line httpStatusLine;
   protected Header headers;
-  protected List<String> body;
+  protected List<Line> body;
   protected int bodySize;
-  protected List<String> message;
+  protected List<Line> message;
 
-  public BatchQueryOperation(final List<String> message, final boolean isStrict) {
+  public BatchQueryOperation(final List<Line> message, final boolean isStrict) {
     this.isStrict = isStrict;
     this.message = message;
   }
@@ -45,22 +46,23 @@ public class BatchQueryOperation implements BatchPart {
     return this;
   }
 
-  protected String consumeHttpStatusLine(final List<String> message) throws BatchException {
-    if (message.size() > 0 && !message.get(0).trim().equals("")) {
-      String method = message.get(0);
+  protected Line consumeHttpStatusLine(final List<Line> message) throws BatchException {
+    if (message.size() > 0 && !message.get(0).toString().trim().equals("")) {
+      final Line method = message.get(0);
       message.remove(0);
 
       return method;
     } else {
-      throw new BatchException(BatchException.INVALID_QUERY_OPERATION_METHOD);
+      final int line = (message.size() > 0) ? message.get(0).getLineNumber() : 0;
+      throw new BatchException(BatchException.MISSING_METHOD.addContent(line));
     }
   }
 
-  public String getHttpStatusLine() {
+  public Line getHttpStatusLine() {
     return httpStatusLine;
   }
 
-  public List<String> getBody() {
+  public List<Line> getBody() {
     return body;
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java
index a49a2e5..c7ffa88 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchRequestTransformator.java
@@ -32,12 +32,13 @@ import org.apache.olingo.odata2.api.batch.BatchException;
 import org.apache.olingo.odata2.api.batch.BatchParserResult;
 import org.apache.olingo.odata2.api.commons.HttpHeaders;
 import org.apache.olingo.odata2.api.commons.ODataHttpMethod;
-import org.apache.olingo.odata2.api.exception.MessageReference;
 import org.apache.olingo.odata2.api.processor.ODataRequest;
 import org.apache.olingo.odata2.api.processor.ODataRequest.ODataRequestBuilder;
 import org.apache.olingo.odata2.api.uri.PathInfo;
 import org.apache.olingo.odata2.core.batch.BatchHelper;
 import org.apache.olingo.odata2.core.batch.BatchRequestPartImpl;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
+import org.apache.olingo.odata2.core.batch.v2.Header.HeaderField;
 
 public class BatchRequestTransformator implements BatchTransformator {
 
@@ -88,9 +89,10 @@ public class BatchRequestTransformator implements BatchTransformator {
   private ODataRequest createRequest(final BatchQueryOperation operation, final Header headers,
       final PathInfo pathInfo, final String baseUri, final boolean isChangeSet) throws BatchException {
 
+    final int httpLineNumber = operation.getHttpStatusLine().getLineNumber();
     ODataHttpMethod httpMethod = getHttpMethod(operation.getHttpStatusLine());
-    validateHttpMethod(httpMethod, isChangeSet);
-    validateBody(httpMethod, operation);
+    validateHttpMethod(httpMethod, isChangeSet, httpLineNumber);
+    validateBody(httpMethod, operation, httpLineNumber);
     InputStream bodyStrean = getBodyStream(operation, headers, httpMethod);
 
     ODataRequestBuilder requestBuilder = ODataRequest.method(httpMethod)
@@ -99,27 +101,26 @@ public class BatchRequestTransformator implements BatchTransformator {
         .allQueryParameters(BatchParserCommon.parseQueryParameter(operation.getHttpStatusLine()))
         .body(bodyStrean)
         .requestHeaders(headers.toMultiMap())
-        .pathInfo(BatchParserCommon.parseRequestUri(operation.getHttpStatusLine(), pathInfo, baseUri));
-    
-    final String contentType =headers.getHeader(HttpHeaders.CONTENT_TYPE);
-    if(contentType != null) {
+        .pathInfo(BatchParserCommon.parseRequestUri(operation.getHttpStatusLine(), pathInfo, baseUri, 0));
+
+    final String contentType = headers.getHeader(HttpHeaders.CONTENT_TYPE);
+    if (contentType != null) {
       requestBuilder.contentType(contentType);
     }
-      
-    
+
     return requestBuilder.build();
   }
 
-  private void validateBody(final ODataHttpMethod httpMethod, final BatchQueryOperation operation)
+  private void validateBody(final ODataHttpMethod httpStatusLine, final BatchQueryOperation operation, final int line)
       throws BatchException {
-    if (HTTP_BATCH_METHODS.contains(httpMethod.toString()) && isUnvalidGetRequestBody(operation)) {
-      throw new BatchException(BatchException.INVALID_REQUEST_LINE);
+    if (HTTP_BATCH_METHODS.contains(httpStatusLine.toString()) && isUnvalidGetRequestBody(operation)) {
+      throw new BatchException(BatchException.INVALID_REQUEST_LINE.addContent(httpStatusLine).addContent(line));
     }
   }
 
   private boolean isUnvalidGetRequestBody(final BatchQueryOperation operation) {
     return (operation.getBody().size() > 1)
-        || (operation.getBody().size() == 1 && !operation.getBody().get(0).trim().equals(""));
+        || (operation.getBody().size() == 1 && !operation.getBody().get(0).toString().trim().equals(""));
   }
 
   private InputStream getBodyStream(final BatchQueryOperation operation, Header headers,
@@ -140,28 +141,32 @@ public class BatchRequestTransformator implements BatchTransformator {
 
   private Header transformHeader(final BatchPart operation, final BatchPart parentPart) {
     final Header headers = operation.getHeaders().clone();
-    headers.removeHeaders(BatchHelper.HTTP_CONTENT_ID);
-    final List<String> operationContentIds = operation.getHeaders().getHeaders(BatchHelper.HTTP_CONTENT_ID);
-    final List<String> parentContentIds = parentPart.getHeaders().getHeaders(BatchHelper.HTTP_CONTENT_ID);
+    headers.removeHeader(BatchHelper.HTTP_CONTENT_ID);
+    final HeaderField operationHeader = operation.getHeaders().getHeaderField(BatchHelper.HTTP_CONTENT_ID);
+    final HeaderField parentHeader = parentPart.getHeaders().getHeaderField(BatchHelper.HTTP_CONTENT_ID);
 
-    if (operationContentIds.size() != 0) {
-      headers.addHeader(BatchHelper.REQUEST_HEADER_CONTENT_ID, operationContentIds);
+    if (operationHeader != null && operationHeader.getValues().size() != 0) {
+      headers.addHeader(BatchHelper.REQUEST_HEADER_CONTENT_ID, operationHeader.getValues(), operationHeader
+          .getLineNumber());
     }
 
-    if (parentContentIds.size() != 0) {
-      headers.addHeader(BatchHelper.MIME_HEADER_CONTENT_ID, parentContentIds);
+    if (parentHeader != null && parentHeader.getValues().size() != 0) {
+      headers.addHeader(BatchHelper.MIME_HEADER_CONTENT_ID, parentHeader.getValues(), parentHeader.getLineNumber());
     }
 
     return headers;
   }
 
-  private void validateHttpMethod(final ODataHttpMethod httpMethod, final boolean isChangeSet) throws BatchException {
+  private void validateHttpMethod(final ODataHttpMethod httpMethod, final boolean isChangeSet, final int line)
+      throws BatchException {
     Set<String> validMethods = (isChangeSet) ? HTTP_CHANGE_SET_METHODS : HTTP_BATCH_METHODS;
 
     if (!validMethods.contains(httpMethod.toString())) {
-      MessageReference message =
-          (isChangeSet) ? BatchException.INVALID_CHANGESET_METHOD : BatchException.INVALID_QUERY_OPERATION_METHOD;
-      throw new BatchException(message);
+      if (isChangeSet) {
+        throw new BatchException(BatchException.INVALID_CHANGESET_METHOD.addContent(line));
+      } else {
+        throw new BatchException(BatchException.INVALID_QUERY_OPERATION_METHOD.addContent(line));
+      }
     }
   }
 
@@ -183,23 +188,20 @@ public class BatchRequestTransformator implements BatchTransformator {
     return acceptLanguages;
   }
 
-  private ODataHttpMethod getHttpMethod(final String httpRequest) throws BatchException {
+  private ODataHttpMethod getHttpMethod(final Line httpRequest) throws BatchException {
     ODataHttpMethod result = null;
 
-    if (httpRequest != null) {
-      String[] parts = httpRequest.split(" ");
+    String[] parts = httpRequest.toString().split(" ");
 
-      if (parts.length == 3) {
-        try {
-          result = ODataHttpMethod.valueOf(parts[0]);
-        } catch (IllegalArgumentException e) {
-          throw new BatchException(BatchException.MISSING_METHOD, e);
-        }
-      } else {
-        throw new BatchException(BatchException.INVALID_REQUEST_LINE);
+    if (parts.length == 3) {
+      try {
+        result = ODataHttpMethod.valueOf(parts[0]);
+      } catch (IllegalArgumentException e) {
+        throw new BatchException(BatchException.MISSING_METHOD.addContent(httpRequest.getLineNumber()), e);
       }
     } else {
-      throw new BatchException(BatchException.INVALID_REQUEST_LINE);
+      throw new BatchException(BatchException.INVALID_REQUEST_LINE.addContent(httpRequest.toString()).addContent(
+          httpRequest.getLineNumber()));
     }
 
     return result;

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchResponseTransformator.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchResponseTransformator.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchResponseTransformator.java
index ab983ac..e800673 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchResponseTransformator.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchResponseTransformator.java
@@ -29,6 +29,7 @@ import org.apache.olingo.odata2.api.client.batch.BatchSingleResponse;
 import org.apache.olingo.odata2.api.uri.PathInfo;
 import org.apache.olingo.odata2.core.batch.BatchHelper;
 import org.apache.olingo.odata2.core.batch.BatchSingleResponseImpl;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
 
 public class BatchResponseTransformator implements BatchTransformator {
 
@@ -92,14 +93,15 @@ public class BatchResponseTransformator implements BatchTransformator {
     return response;
   }
 
-  private Matcher prepareStatusLineMatcher(String httpStatusLine) throws BatchException {
+  private Matcher prepareStatusLineMatcher(final Line httpStatusLine) throws BatchException {
     final Pattern regexPattern = Pattern.compile(REG_EX_STATUS_LINE);
-    final Matcher matcher = regexPattern.matcher(httpStatusLine);
+    final Matcher matcher = regexPattern.matcher(httpStatusLine.toString());
 
     if (matcher.find()) {
       return matcher;
     } else {
-      throw new BatchException(BatchException.INVALID_STATUS_LINE);
+      throw new BatchException(BatchException.INVALID_STATUS_LINE.addContent(httpStatusLine.toString())
+          .addContent(httpStatusLine.getLineNumber()));
     }
   }
 
@@ -109,7 +111,7 @@ public class BatchResponseTransformator implements BatchTransformator {
     if (contentLength == -1) {
       return BatchParserCommon.stringListToString(operation.getBody());
     } else {
-      return BatchParserCommon.trimStringListToStringLength(operation.getBody(), contentLength);
+      return BatchParserCommon.trimLineListToLength(operation.getBody(), contentLength);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchTransformatorCommon.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchTransformatorCommon.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchTransformatorCommon.java
index 9c67f49..498dd2d 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchTransformatorCommon.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BatchTransformatorCommon.java
@@ -6,6 +6,7 @@ import org.apache.olingo.odata2.api.batch.BatchException;
 import org.apache.olingo.odata2.api.commons.HttpContentType;
 import org.apache.olingo.odata2.api.commons.HttpHeaders;
 import org.apache.olingo.odata2.core.batch.BatchHelper;
+import org.apache.olingo.odata2.core.batch.v2.Header.HeaderField;
 
 public class BatchTransformatorCommon {
 
@@ -24,39 +25,45 @@ public class BatchTransformatorCommon {
 
   public static void validateContentTransferEncoding(final Header headers, final boolean isChangeRequest)
       throws BatchException {
-    final List<String> contentTransferEncodings = headers.getHeaders(BatchHelper.HTTP_CONTENT_TRANSFER_ENCODING);
+    final HeaderField contentTransferField = headers.getHeaderField(BatchHelper.HTTP_CONTENT_TRANSFER_ENCODING);
 
-    if (contentTransferEncodings.size() != 0) {
-      if (contentTransferEncodings.size() == 1) {
-        String encoding = contentTransferEncodings.get(0);
+    if (contentTransferField != null) {
+      final List<String> contentTransferValues = contentTransferField.getValues();
+      if (contentTransferValues.size() == 1) {
+        String encoding = contentTransferValues.get(0);
 
         if (!BatchHelper.BINARY_ENCODING.equalsIgnoreCase(encoding)) {
-          throw new BatchException(BatchException.INVALID_CONTENT_TRANSFER_ENCODING);
+          throw new BatchException(
+              BatchException.INVALID_CONTENT_TRANSFER_ENCODING.addContent(contentTransferField.getLineNumber()));
         }
       } else {
-        throw new BatchException(BatchException.INVALID_HEADER);
+        throw new BatchException(BatchException.INVALID_HEADER.addContent(contentTransferField.getLineNumber()));
       }
     } else {
       if (isChangeRequest) {
-        throw new BatchException(BatchException.INVALID_CONTENT_TRANSFER_ENCODING);
+        throw new BatchException(BatchException.INVALID_CONTENT_TRANSFER_ENCODING.addContent(headers.getLineNumber()));
       }
     }
   }
 
   public static int getContentLength(final Header headers) throws BatchException {
-    final List<String> contentLengths = headers.getHeaders(HttpHeaders.CONTENT_LENGTH);
+    final HeaderField contentLengthField = headers.getHeaderField(HttpHeaders.CONTENT_LENGTH);
+
+    if (contentLengthField != null && contentLengthField.getValues().size() == 1) {
+      final List<String> contentLengthValues = contentLengthField.getValues();
 
-    if (contentLengths.size() == 1) {
       try {
-        int contentLength = Integer.parseInt(contentLengths.get(0));
+        int contentLength = Integer.parseInt(contentLengthValues.get(0));
 
         if (contentLength < 0) {
-          throw new BatchException(BatchException.INVALID_HEADER);
+          throw new BatchException(BatchException.INVALID_HEADER.addContent(contentLengthField.getValue()).addContent(
+              contentLengthField.getLineNumber()));
         }
 
         return contentLength;
       } catch (NumberFormatException e) {
-        throw new BatchException(BatchException.INVALID_HEADER, e);
+        throw new BatchException(BatchException.INVALID_HEADER.addContent(contentLengthField.getValue()).addContent(
+            contentLengthField.getLineNumber()), e);
       }
     }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BufferedReaderIncludingLineEndings.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BufferedReaderIncludingLineEndings.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BufferedReaderIncludingLineEndings.java
index 5e411ff..295f4c6 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BufferedReaderIncludingLineEndings.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/BufferedReaderIncludingLineEndings.java
@@ -92,12 +92,13 @@ public class BufferedReaderIncludingLineEndings extends Reader {
     return bytesRead;
   }
 
-  public List<String> toList() throws IOException {
-    final List<String> result = new ArrayList<String>();
+  public List<Line> toList() throws IOException {
+    final List<Line> result = new ArrayList<Line>();
     String currentLine;
+    int counter = 1;
 
     while ((currentLine = readLine()) != null) {
-      result.add(currentLine);
+      result.add(new Line(currentLine, counter++));
     }
 
     return result;
@@ -217,4 +218,57 @@ public class BufferedReaderIncludingLineEndings extends Reader {
 
     return limit;
   }
+
+  public static class Line {
+    private final int lineNumber;
+    private final String content;
+
+    public Line(final String content, final int lineNumber) {
+      this.content = content;
+      this.lineNumber = lineNumber;
+    }
+
+    public int getLineNumber() {
+      return lineNumber;
+    }
+
+    @Override
+    public String toString() {
+      return content;
+    }
+
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((content == null) ? 0 : content.hashCode());
+      result = prime * result + lineNumber;
+      return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+      if (this == obj) {
+        return true;
+      }
+      if (obj == null) {
+        return false;
+      }
+      if (getClass() != obj.getClass()) {
+        return false;
+      }
+      Line other = (Line) obj;
+      if (content == null) {
+        if (other.content != null) {
+          return false;
+        }
+      } else if (!content.equals(other.content)) {
+        return false;
+      }
+      if (lineNumber != other.lineNumber) {
+        return false;
+      }
+      return true;
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/Header.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/Header.java b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/Header.java
index 7901b7b..c9daa6a 100644
--- a/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/Header.java
+++ b/odata2-lib/odata-core/src/main/java/org/apache/olingo/odata2/core/batch/v2/Header.java
@@ -10,20 +10,14 @@ import java.util.regex.Pattern;
 public class Header implements Cloneable {
 
   private final Map<String, HeaderField> headers = new HashMap<String, HeaderField>();
+  private int lineNumber;
   
-  public static List<String> splitValuesByComma(final String headerValue) {
-    final List<String> singleValues = new ArrayList<String>();
-
-    String[] parts = headerValue.split(",");
-    for (final String value : parts) {
-      singleValues.add(value.trim());
-    }
-
-    return singleValues;
+  public Header(int lineNumer) {
+    this.lineNumber = lineNumer;
   }
   
-  public void addHeader(final String name, final String value) {
-    final HeaderField headerField = getHeaderFieldOrDefault(name);
+  public void addHeader(final String name, final String value, final int lineNumber) {
+    final HeaderField headerField = getHeaderFieldOrDefault(name, lineNumber);
     final List<String> headerValues = headerField.getValues();
 
     if (!headerValues.contains(value)) {
@@ -31,8 +25,8 @@ public class Header implements Cloneable {
     }
   }
 
-  public void addHeader(final String name, final List<String> values) {
-    final HeaderField headerField = getHeaderFieldOrDefault(name);
+  public void addHeader(final String name, final List<String> values, final int lineNumber) {
+    final HeaderField headerField = getHeaderFieldOrDefault(name, lineNumber);
     final List<String> headerValues = headerField.getValues();
 
     for (final String value : values) {
@@ -41,45 +35,29 @@ public class Header implements Cloneable {
       }
     }
   }
-  
+
   public boolean isHeaderMatching(final String name, final Pattern pattern) {
-    if(getHeaders(name).size() != 1 ) {
+    if (getHeaders(name).size() != 1) {
       return false;
     } else {
       return pattern.matcher(getHeaders(name).get(0)).matches();
     }
   }
-  
-  public void removeHeaders(final String name) {
+
+  public void removeHeader(final String name) {
     headers.remove(name.toLowerCase(Locale.ENGLISH));
   }
 
   public String getHeader(final String name) {
     final HeaderField headerField = getHeaderField(name);
-
-    if (headerField == null) {
-      return null;
-    } else {
-      final List<String> headerValues = headerField.getValues();
-      final StringBuilder result = new StringBuilder();
-
-      for (final String value : headerValues) {
-        result.append(value);
-        result.append(", ");
-      }
-      
-      if(result.length()>0) {
-        result.delete(result.length() - 2, result.length());
-      }
-      
-      return result.toString();
-    }
+    
+    return (headerField == null) ? null : headerField.getValue();
   }
 
   public String getHeaderNotNull(final String name) {
-    final String value = getHeader(name);
-
-    return (value == null) ? "" : value;
+    final HeaderField headerField = getHeaderField(name);
+    
+    return (headerField == null) ? "" : headerField.getValueNotNull();
   }
 
   public List<String> getHeaders(final String name) {
@@ -91,7 +69,11 @@ public class Header implements Cloneable {
   public HeaderField getHeaderField(final String name) {
     return headers.get(name.toLowerCase(Locale.ENGLISH));
   }
-
+  
+  public int getLineNumber() {
+    return lineNumber;
+  }
+  
   public Map<String, String> toSingleMap() {
     final Map<String, String> singleMap = new HashMap<String, String>();
 
@@ -114,11 +96,11 @@ public class Header implements Cloneable {
     return singleMap;
   }
 
-  private HeaderField getHeaderFieldOrDefault(final String name) {
+  private HeaderField getHeaderFieldOrDefault(final String name, final int lineNumber) {
     HeaderField headerField = headers.get(name.toLowerCase(Locale.ENGLISH));
 
     if (headerField == null) {
-      headerField = new HeaderField(name);
+      headerField = new HeaderField(name, lineNumber);
       headers.put(name.toLowerCase(Locale.ENGLISH), headerField);
     }
 
@@ -127,7 +109,7 @@ public class Header implements Cloneable {
 
   @Override
   public Header clone() {
-    final Header newInstance = new Header();
+    final Header newInstance = new Header(lineNumber);
 
     for (final String key : headers.keySet()) {
       newInstance.headers.put(key, headers.get(key).clone());
@@ -136,17 +118,30 @@ public class Header implements Cloneable {
     return newInstance;
   }
 
+  public static List<String> splitValuesByComma(final String headerValue) {
+    final List<String> singleValues = new ArrayList<String>();
+
+    String[] parts = headerValue.split(",");
+    for (final String value : parts) {
+      singleValues.add(value.trim());
+    }
+
+    return singleValues;
+  }
+
   public static class HeaderField implements Cloneable {
-    private String fieldName;
-    private List<String> values;
+    private final String fieldName;
+    private final List<String> values;
+    private final int lineNumber;
 
-    public HeaderField(final String fieldName) {
-      this(fieldName, new ArrayList<String>());
+    public HeaderField(final String fieldName, final int lineNumber) {
+      this(fieldName, new ArrayList<String>(), lineNumber);
     }
 
-    public HeaderField(final String fieldName, final List<String> values) {
+    public HeaderField(final String fieldName, final List<String> values, final int lineNumber) {
       this.fieldName = fieldName;
       this.values = values;
+      this.lineNumber = lineNumber;
     }
 
     public String getFieldName() {
@@ -157,8 +152,37 @@ public class Header implements Cloneable {
       return values;
     }
 
-    public void setValues(final List<String> values) {
-      this.values = values;
+    public String getValue() {
+      final StringBuilder result = new StringBuilder();
+
+      for (final String value : values) {
+        result.append(value);
+        result.append(", ");
+      }
+
+      if (result.length() > 0) {
+        result.delete(result.length() - 2, result.length());
+      }
+
+      return result.toString();
+    }
+
+    public String getValueNotNull() {
+      final String value = getValue();
+
+      return (value == null) ? "" : value;
+    }
+
+    @Override
+    public HeaderField clone() {
+      List<String> newValues = new ArrayList<String>();
+      newValues.addAll(values);
+
+      return new HeaderField(fieldName, newValues, lineNumber);
+    }
+
+    public int getLineNumber() {
+      return lineNumber;
     }
 
     @Override
@@ -166,6 +190,8 @@ public class Header implements Cloneable {
       final int prime = 31;
       int result = 1;
       result = prime * result + ((fieldName == null) ? 0 : fieldName.hashCode());
+      result = prime * result + lineNumber;
+      result = prime * result + ((values == null) ? 0 : values.hashCode());
       return result;
     }
 
@@ -188,15 +214,17 @@ public class Header implements Cloneable {
       } else if (!fieldName.equals(other.fieldName)) {
         return false;
       }
+      if (lineNumber != other.lineNumber) {
+        return false;
+      }
+      if (values == null) {
+        if (other.values != null) {
+          return false;
+        }
+      } else if (!values.equals(other.values)) {
+        return false;
+      }
       return true;
     }
-
-    @Override
-    public HeaderField clone() {
-      List<String> newValues = new ArrayList<String>();
-      newValues.addAll(values);
-
-      return new HeaderField(fieldName, newValues);
-    }
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/main/resources/i18n.properties
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/main/resources/i18n.properties b/odata2-lib/odata-core/src/main/resources/i18n.properties
index a89c3e9..47e50a2 100644
--- a/odata2-lib/odata-core/src/main/resources/i18n.properties
+++ b/odata2-lib/odata-core/src/main/resources/i18n.properties
@@ -118,31 +118,30 @@ org.apache.olingo.odata2.api.ep.EntityProviderException.INVALID_DELETED_ENTRY_ME
 ##################################
 # BatchParserexceptions
 ##################################
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_CHANGESET_BOUNDARY=The boundary of the ChangeSet should be different from that used by the Batch: line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_ACCEPT_LANGUAGE_HEADER=Invalid Accept-Language: '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_ACCEPT_HEADER=Invalid Accept header: '%1$s'.
 org.apache.olingo.odata2.api.batch.BatchException.INVALID_BOUNDARY_DELIMITER=The boundary delimiter must begin with two hyphen characters: line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.MISSING_BOUNDARY_DELIMITER=Missing boundary delimiter at line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.MISSING_CLOSE_DELIMITER=Missing close delimiter at line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_QUERY_OPERATION_METHOD=Invalid method: a Query Operation cannot contain insert, update or delete requests at line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_CHANGESET_METHOD= Invalid method: a ChangeSet cannot contain retrieve requests at line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_QUERY_PARAMETER=Invalid query parameters.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_URI=Invalid URI: line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.UNSUPPORTED_ABSOLUTE_PATH = An absolute-path in request line is not supported: line '%1$s'.
 org.apache.olingo.odata2.api.batch.BatchException.INVALID_BOUNDARY=Invalid boundary at line '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.NO_MATCH_WITH_BOUNDARY_STRING=The boundary string does not match the boundary from the Content-Type header field '%1$s': line '%2$s'.
-org.apache.olingo.odata2.api.batch.BatchException.MISSING_CONTENT_TYPE=No Content-Type field for MIME-header is present.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_CONTENT_TYPE=Content-Type should be '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.MISSING_PARAMETER_IN_CONTENT_TYPE=The Content-Type field for multipart entities requires boundary parameter.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_CONTENT_TRANSFER_ENCODING=The Content-Transfer-Encoding should be binary.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_ACCEPT_HEADER=Invalid Accept header: '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_ACCEPT_LANGUAGE_HEADER=Invalid Accept-Language: '%1$s'.
 org.apache.olingo.odata2.api.batch.BatchException.INVALID_HEADER=Invalid header: '%1$s' at line '%2$s'.
-org.apache.olingo.odata2.api.batch.BatchException.MISSING_BLANK_LINE=Expected empty line but was '%1$s': line '%2$s'  .
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_CONTENT_TRANSFER_ENCODING=The Content-Transfer-Encoding should be binary: line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_CONTENT_TYPE=Content-Type should be '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_CHANGESET_BOUNDARY=The boundary of the ChangeSet should be different from that used by the Batch: line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_CHANGESET_METHOD= Invalid method: a ChangeSet cannot contain retrieve requests at line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_REQUEST_LINE=Invalid request line '%1$s' at line '%2$s'.
 org.apache.olingo.odata2.api.batch.BatchException.INVALID_PATHINFO=PathInfo should not be null.
-org.apache.olingo.odata2.api.batch.BatchException.MISSING_METHOD=Missing method in request line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_URI=Invalid URI: line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_QUERY_OPERATION_METHOD=Invalid method: a Query Operation cannot contain insert, update or delete requests at line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.INVALID_QUERY_PARAMETER=Invalid query parameters.
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_BLANK_LINE=Expected empty line but was '%1$s': line '%2$s'  .
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_BOUNDARY_DELIMITER=Missing boundary delimiter at line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_CONTENT_TYPE=No Content-Type field for MIME-header is present.
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_CLOSE_DELIMITER=Missing close delimiter at line '%1$s'.
 org.apache.olingo.odata2.api.batch.BatchException.MISSING_MANDATORY_HEADER=Missing mandatory header '%1$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_REQUEST_LINE=Invalid request line '%1$s' at line '%2$s'.
-org.apache.olingo.odata2.api.batch.BatchException.INVALID_REQUEST_LINE=Invalid status line '%1$s' at line '%2$s'.
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_METHOD=Missing method in request line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.MISSING_PARAMETER_IN_CONTENT_TYPE=The Content-Type field for multipart entities requires boundary parameter.
+org.apache.olingo.odata2.api.batch.BatchException.NO_MATCH_WITH_BOUNDARY_STRING=The boundary string does not match the boundary from the Content-Type header field '%1$s': line '%2$s'.
 org.apache.olingo.odata2.api.batch.BatchException.TRUNCATED_BODY=Body is truncated: line '%1$s'.
+org.apache.olingo.odata2.api.batch.BatchException.UNSUPPORTED_ABSOLUTE_PATH = An absolute-path in request line is not supported: line '%1$s'.
 
 ##################################
 # HttpExceptions

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchParserCommonTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchParserCommonTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchParserCommonTest.java
index 56cbeeb..4369c1e 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchParserCommonTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchParserCommonTest.java
@@ -3,12 +3,12 @@ package org.apache.olingo.odata2.core.batch;
 import static org.junit.Assert.*;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 import org.apache.olingo.odata2.api.batch.BatchException;
 import org.apache.olingo.odata2.api.commons.HttpHeaders;
 import org.apache.olingo.odata2.core.batch.v2.BatchParserCommon;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
 import org.apache.olingo.odata2.core.batch.v2.Header;
 import org.junit.Test;
 
@@ -24,9 +24,7 @@ public class BatchParserCommonTest {
         "content-type: Application/http" + CRLF,
         "content-transfer-encoding: Binary" + CRLF
       };
-    List<String> message = new ArrayList<String>();
-    message.addAll(Arrays.asList(messageRaw));
-    
+    List<Line> message = toLineList(messageRaw);
     
     final Header header = BatchParserCommon.consumeHeaders(message);
     assertNotNull(header);
@@ -46,9 +44,7 @@ public class BatchParserCommonTest {
         "content-type: Application/http" + CRLF,
         "content-transfer-encoding: Binary" + CRLF
       };
-    List<String> message = new ArrayList<String>();
-    message.addAll(Arrays.asList(messageRaw));
-    
+    List<Line> message = toLineList(messageRaw);
     
     final Header header = BatchParserCommon.consumeHeaders(message);
     assertNotNull(header);
@@ -67,9 +63,7 @@ public class BatchParserCommonTest {
         "content-type: Application/http" + CRLF,
         "content-transfer-encoding: Binary" + CRLF
       };
-    List<String> message = new ArrayList<String>();
-    message.addAll(Arrays.asList(messageRaw));
-    
+    List<Line> message = toLineList(messageRaw);
     
     final Header header = BatchParserCommon.consumeHeaders(message);
     assertNotNull(header);
@@ -92,9 +86,7 @@ public class BatchParserCommonTest {
         "content-type: Application/http" + CRLF,
         "content-transfer-encoding: Binary" + CRLF
       };
-    List<String> message = new ArrayList<String>();
-    message.addAll(Arrays.asList(messageRaw));
-    
+    List<Line> message = toLineList(messageRaw);
     
     final Header header = BatchParserCommon.consumeHeaders(message);
     assertNotNull(header);
@@ -113,9 +105,7 @@ public class BatchParserCommonTest {
         "content-type: Application/http" + CRLF,
         "content-transfer-encoding: Binary" + CRLF
       };
-    List<String> message = new ArrayList<String>();
-    message.addAll(Arrays.asList(messageRaw));
-    
+    List<Line> message = toLineList(messageRaw);
     
     final Header header = BatchParserCommon.consumeHeaders(message);
     assertNotNull(header);
@@ -133,8 +123,7 @@ public class BatchParserCommonTest {
         "content-type: Application/http" + CRLF,
         "content-transfer-encoding: Binary" + CRLF
       };
-    List<String> message = new ArrayList<String>();
-    message.addAll(Arrays.asList(messageRaw));
+    List<Line> message = toLineList(messageRaw);
     
     final Header header = BatchParserCommon.consumeHeaders(message);
     assertNotNull(header);
@@ -152,8 +141,7 @@ public class BatchParserCommonTest {
         "content-type: Application/http" + CRLF,
         "content-transfer-encoding: Binary" + CRLF
       };
-    List<String> message = new ArrayList<String>();
-    message.addAll(Arrays.asList(messageRaw));
+    List<Line> message = toLineList(messageRaw);
     
     final Header header = BatchParserCommon.consumeHeaders(message);
     assertNotNull(header);
@@ -166,48 +154,59 @@ public class BatchParserCommonTest {
   @Test
   public void testRemoveEndingCRLF() {
     String line = "Test\r\n";
-    assertEquals("Test", BatchParserCommon.removeEndingCRLF(line));
+    assertEquals("Test", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
   }
 
   @Test
   public void testRemoveLastEndingCRLF() {
     String line = "Test\r\n\r\n";
-    assertEquals("Test\r\n", BatchParserCommon.removeEndingCRLF(line));
+    assertEquals("Test\r\n", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
   }
 
   @Test
   public void testRemoveEndingCRLFWithWS() {
     String line = "Test\r\n            ";
-    assertEquals("Test", BatchParserCommon.removeEndingCRLF(line));
+    assertEquals("Test", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
   }
 
   @Test
   public void testRemoveEndingCRLFNothingToRemove() {
     String line = "Hallo\r\nBla";
-    assertEquals("Hallo\r\nBla", BatchParserCommon.removeEndingCRLF(line));
+    assertEquals("Hallo\r\nBla", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
   }
 
   @Test
   public void testRemoveEndingCRLFAll() {
     String line = "\r\n";
-    assertEquals("", BatchParserCommon.removeEndingCRLF(line));
+    assertEquals("", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
   }
 
   @Test
   public void testRemoveEndingCRLFSpace() {
     String line = "\r\n                      ";
-    assertEquals("", BatchParserCommon.removeEndingCRLF(line));
+    assertEquals("", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
   }
 
   @Test
   public void testRemoveLastEndingCRLFWithWS() {
     String line = "Test            \r\n";
-    assertEquals("Test            ", BatchParserCommon.removeEndingCRLF(line));
+    assertEquals("Test            ", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
   }
 
   @Test
   public void testRemoveLastEndingCRLFWithWSLong() {
     String line = "Test            \r\nTest2    \r\n";
-    assertEquals("Test            \r\nTest2    ", BatchParserCommon.removeEndingCRLF(line));
+    assertEquals("Test            \r\nTest2    ", BatchParserCommon.removeEndingCRLF(new Line(line,1)).toString());
+  }
+  
+  private List<Line> toLineList(String[] messageRaw) {
+    final List<Line> lineList = new ArrayList<Line>();
+    int counter = 1;
+    
+    for(final String currentLine : messageRaw) {
+      lineList.add(new Line(currentLine, counter++));
+    }
+    
+    return lineList;
   }
 }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
index 4cd0b67..142d355 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestParserTest.java
@@ -44,9 +44,6 @@ import org.junit.BeforeClass;
 import org.junit.Ignore;
 import org.junit.Test;
 
-/**
- *
- */
 public class BatchRequestParserTest {
 
   private static final String CRLF = "\r\n";
@@ -257,7 +254,7 @@ public class BatchRequestParserTest {
 
   @Test(expected = BatchException.class)
   public void testBoundaryParameterWithoutQuota() throws BatchException {
-    String invalidContentType = "multipart;boundary=batch_1740-bb:84-2f7f";
+    String invalidContentType = "multipart/mixed;boundary=batch_1740-bb:84-2f7f";
     String batch = "--batch_1740-bb:84-2f7f" + CRLF
         + GET_REQUEST
         + "--batch_1740-bb:84-2f7f--";
@@ -607,32 +604,6 @@ public class BatchRequestParserTest {
   }
 
   @SuppressWarnings("unused")
-  @Test(expected = BatchException.class)
-  @Ignore("This header should not be validated")
-  public void testNegativeContentLength() throws BatchException, IOException {
-    String batch = ""
-        + "--batch_8194-cf13-1f56" + CRLF
-        + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
-        + "Content-Length: -2" + CRLF
-        + CRLF
-        + "--changeset_f980-1cb6-94dd" + CRLF
-        + MIME_HEADERS
-        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
-        + CRLF
-        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
-        + "Content-Type: application/json;odata=verbose" + CRLF
-        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
-        + CRLF
-        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
-        + "--changeset_f980-1cb6-94dd--" + CRLF
-        + CRLF
-        + "--batch_8194-cf13-1f56--";
-    InputStream in = new ByteArrayInputStream(batch.getBytes());
-    BatchParser parser = new BatchParser(contentType, batchProperties, true);
-    List<BatchRequestPart> batchRequestParts = parser.parseBatchRequest(in);
-  }
-
-  @SuppressWarnings("unused")
   @Test
   public void testNegativeContentLengthChangeSet() throws BatchException, IOException {
     String batch = ""
@@ -747,44 +718,6 @@ public class BatchRequestParserTest {
   }
 
   @Test(expected = BatchException.class)
-  @Ignore("This header should not be validated")
-  public void testCutChangeSetDelimiter() throws BatchException, IOException {
-    String batch = ""
-        + "--batch_8194-cf13-1f56" + CRLF
-        + "Content-Type: multipart/mixed; boundary=changeset_f980-1cb6-94dd" + CRLF
-        + "Content-Length: 582" + CRLF
-        + CRLF
-        + "--changeset_f980-1cb6-94dd" + CRLF
-        + MIME_HEADERS
-        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
-        + CRLF
-        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
-        + "Content-Type: application/json;odata=verbose" + CRLF
-        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
-        + "Content-Length: 10" + CRLF
-        + CRLF
-        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
-        + CRLF
-        + "--changeset_f980-1cb6-94dd" + CRLF
-        + MIME_HEADERS
-        + "Content-ID: " + PUT_MIME_HEADER_CONTENT_ID + CRLF
-        + CRLF
-        + "PUT $" + CONTENT_ID_REFERENCE + "/EmployeeName HTTP/1.1" + CRLF
-        + "Content-Type: application/json;odata=verbose" + CRLF
-        + "Content-Id:" + PUT_REQUEST_HEADER_CONTENT_ID + CRLF
-        + "Content-Length: 100000" + CRLF
-        + CRLF
-        + "{\"EmployeeName\":\"Peter Fall\"}" + CRLF
-        + "--changeset_f980-1cb6-94dd--" + CRLF
-        + CRLF
-        + "--batch_8194-cf13-1f56--";
-
-    InputStream in = new ByteArrayInputStream(batch.getBytes());
-    BatchParser parser = new BatchParser(contentType, batchProperties, true);
-    parser.parseBatchRequest(in);
-  }
-
-  @Test(expected = BatchException.class)
   public void testNonNumericContentLength() throws BatchException {
     String batch = ""
         + "--batch_8194-cf13-1f56" + CRLF
@@ -1138,7 +1071,20 @@ public class BatchRequestParserTest {
     assertEquals("{\"EmployeeName\":\"Peter Fall\"}",
         inputStreamToString(changeSetPart.getRequests().get(1).getBody()));
   }
+  
+  @Test
+  public void testLargeBatch() throws BatchException, IOException {
+    String fileName = "/batchLarge.batch";
+    InputStream in = ClassLoader.class.getResourceAsStream(fileName);
+    if (in == null) {
+      throw new IOException("Requested file '" + fileName + "' was not found.");
+    }
+    
+    BatchParser parser = new BatchParser(contentType, batchProperties, true);
+    parser.parseBatchRequest(in);
+  }
 
+  
   private List<BatchRequestPart> parse(final String batch) throws BatchException {
     InputStream in = new ByteArrayInputStream(batch.getBytes());
     BatchParser parser = new BatchParser(contentType, batchProperties, true);

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
index 6c604f8..bcf13e4 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchRequestTest.java
@@ -132,7 +132,6 @@ public class BatchRequestTest {
   }
 
   @Test
-  @Ignore
   // TODO
       /*
        * --batch_123
@@ -146,8 +145,8 @@ public class BatchRequestTest {
        * ...
        * ....
        */
-      public
-      void testBatchWithGetAndPost() throws BatchException, IOException {
+  @Ignore
+  public void testBatchWithGetAndPost() throws BatchException, IOException {
     List<BatchPart> batch = new ArrayList<BatchPart>();
     Map<String, String> headers = new HashMap<String, String>();
     headers.put("Accept", "application/json");

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchTransformatorCommonTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchTransformatorCommonTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchTransformatorCommonTest.java
index e98a295..7437caf 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchTransformatorCommonTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BatchTransformatorCommonTest.java
@@ -50,7 +50,7 @@ public class BatchTransformatorCommonTest {
 
   @Test(expected = BatchException.class)
   public void testValidateContentTypeMissingHeader() throws BatchException {
-    final Header headers = new Header();
+    final Header headers = new Header(1);
     
     BatchTransformatorCommon.validateContentType(headers);
   }
@@ -82,7 +82,7 @@ public class BatchTransformatorCommonTest {
 
   @Test(expected = BatchException.class)
   public void testValidateContentTransferEncodingMissingHeader() throws BatchException {
-    final Header headers = new Header();
+    final Header headers = new Header(1);
     
     BatchTransformatorCommon.validateContentTransferEncoding(headers, true);
   }
@@ -96,8 +96,8 @@ public class BatchTransformatorCommonTest {
   }
 
   private Header makeHeaders(final String headerName, final List<String> values) {
-    final Header headers = new Header();
-    headers.addHeader(headerName, values);
+    final Header headers = new Header(1);
+    headers.addHeader(headerName, values, 1);
 
     return headers;
   }

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BufferedReaderIncludingLineEndingsTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BufferedReaderIncludingLineEndingsTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BufferedReaderIncludingLineEndingsTest.java
index bd3607a..482dc3b 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BufferedReaderIncludingLineEndingsTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/BufferedReaderIncludingLineEndingsTest.java
@@ -9,6 +9,7 @@ import java.io.UnsupportedEncodingException;
 import java.util.List;
 
 import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings;
+import org.apache.olingo.odata2.core.batch.v2.BufferedReaderIncludingLineEndings.Line;
 import org.junit.Test;
 
 public class BufferedReaderIncludingLineEndingsTest {
@@ -422,20 +423,20 @@ public class BufferedReaderIncludingLineEndingsTest {
   @Test
   public void testToList() throws IOException {
     BufferedReaderIncludingLineEndings reader = create(TEXT_COMBINED);
-    List<String> stringList = reader.toList();
+    List<Line> stringList = reader.toList();
 
     assertEquals(11, stringList.size());
-    assertEquals("Test\r", stringList.get(0));
-    assertEquals("Test2\r\n", stringList.get(1));
-    assertEquals("Test3\n", stringList.get(2));
-    assertEquals("Test4\r", stringList.get(3));
-    assertEquals("\r", stringList.get(4));
-    assertEquals("\r\n", stringList.get(5));
-    assertEquals("\r\n", stringList.get(6));
-    assertEquals("Test5\n", stringList.get(7));
-    assertEquals("Test6\r\n", stringList.get(8));
-    assertEquals("Test7\n", stringList.get(9));
-    assertEquals("\n", stringList.get(10));
+    assertEquals("Test\r", stringList.get(0).toString());
+    assertEquals("Test2\r\n", stringList.get(1).toString());
+    assertEquals("Test3\n", stringList.get(2).toString());
+    assertEquals("Test4\r", stringList.get(3).toString());
+    assertEquals("\r", stringList.get(4).toString());
+    assertEquals("\r\n", stringList.get(5).toString());
+    assertEquals("\r\n", stringList.get(6).toString());
+    assertEquals("Test5\n", stringList.get(7).toString());
+    assertEquals("Test6\r\n", stringList.get(8).toString());
+    assertEquals("Test7\n", stringList.get(9).toString());
+    assertEquals("\n", stringList.get(10).toString());
     reader.close();
   }
 

http://git-wip-us.apache.org/repos/asf/olingo-odata2/blob/f0dc0f74/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/HeaderTest.java
----------------------------------------------------------------------
diff --git a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/HeaderTest.java b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/HeaderTest.java
index 128aa2e..bfe7b24 100644
--- a/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/HeaderTest.java
+++ b/odata2-lib/odata-core/src/test/java/org/apache/olingo/odata2/core/batch/HeaderTest.java
@@ -15,8 +15,8 @@ public class HeaderTest {
 
   @Test
   public void test() {
-    Header header = new Header();
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED);
+    Header header = new Header(1);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
 
     assertEquals(HttpContentType.MULTIPART_MIXED, header.getHeader(HttpHeaders.CONTENT_TYPE));
     assertEquals(1, header.getHeaders(HttpHeaders.CONTENT_TYPE).size());
@@ -25,7 +25,7 @@ public class HeaderTest {
 
   @Test
   public void testNotAvailable() {
-    Header header = new Header();
+    Header header = new Header(1);
 
     assertNull(header.getHeader(HttpHeaders.CONTENT_TYPE));
     assertEquals(0, header.getHeaders(HttpHeaders.CONTENT_TYPE).size());
@@ -34,8 +34,8 @@ public class HeaderTest {
 
   @Test
   public void testCaseInsensitive() {
-    Header header = new Header();
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED);
+    Header header = new Header(1);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
 
     assertEquals(HttpContentType.MULTIPART_MIXED, header.getHeader("cOnTenT-TyPE"));
     assertEquals(1, header.getHeaders("cOnTenT-TyPE").size());
@@ -44,9 +44,9 @@ public class HeaderTest {
 
   @Test
   public void testDuplicatedAdd() {
-    Header header = new Header();
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED);
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED);
+    Header header = new Header(1);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 2);
 
     assertEquals(HttpContentType.MULTIPART_MIXED, header.getHeader(HttpHeaders.CONTENT_TYPE));
     assertEquals(1, header.getHeaders(HttpHeaders.CONTENT_TYPE).size());
@@ -55,16 +55,16 @@ public class HeaderTest {
 
   @Test
   public void testMatcher() {
-    Header header = new Header();
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED + ";boundary=123");
+    Header header = new Header(1);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED + ";boundary=123", 1);
 
     assertTrue(header.isHeaderMatching(HttpHeaders.CONTENT_TYPE, BatchParserCommon.PATTERN_MULTIPART_BOUNDARY));
   }
 
   @Test
   public void testFieldName() {
-    Header header = new Header();
-    header.addHeader("MyFieldNamE", "myValue");
+    Header header = new Header(0);
+    header.addHeader("MyFieldNamE", "myValue", 1);
 
     assertEquals("MyFieldNamE", header.getHeaderField("myfieldname").getFieldName());
     assertEquals("MyFieldNamE", header.toSingleMap().keySet().toArray(new String[0])[0]);
@@ -76,8 +76,8 @@ public class HeaderTest {
 
   @Test
   public void testDeepCopy() {
-    Header header = new Header();
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED + ";boundary=123");
+    Header header = new Header(1);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED + ";boundary=123", 1);
 
     Header copy = header.clone();
     assertEquals(header.getHeaders(HttpHeaders.CONTENT_TYPE), copy.getHeaders(HttpHeaders.CONTENT_TYPE));
@@ -90,25 +90,25 @@ public class HeaderTest {
 
   @Test
   public void testMatcherNoHeader() {
-    Header header = new Header();
+    Header header = new Header(1);
 
     assertFalse(header.isHeaderMatching(HttpHeaders.CONTENT_TYPE, BatchParserCommon.PATTERN_MULTIPART_BOUNDARY));
   }
 
   @Test
   public void testMatcherFail() {
-    Header header = new Header();
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED + ";boundary=123");
+    Header header = new Header(1);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED + ";boundary=123", 1);
 
     assertFalse(header.isHeaderMatching(HttpHeaders.CONTENT_TYPE, BatchParserCommon.PATTERN_HEADER_LINE));
   }
 
   @Test
   public void testDuplicatedAddList() {
-    Header header = new Header();
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED);
+    Header header = new Header(1);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
     header.addHeader(HttpHeaders.CONTENT_TYPE, Arrays.asList(new String[] { HttpContentType.MULTIPART_MIXED,
-        HttpContentType.APPLICATION_ATOM_SVC }));
+        HttpContentType.APPLICATION_ATOM_SVC }), 2);
 
     assertEquals(HttpContentType.MULTIPART_MIXED + ", " + HttpContentType.APPLICATION_ATOM_SVC, header
         .getHeader(HttpHeaders.CONTENT_TYPE));
@@ -119,9 +119,9 @@ public class HeaderTest {
 
   @Test
   public void testRemove() {
-    Header header = new Header();
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED);
-    header.removeHeaders(HttpHeaders.CONTENT_TYPE);
+    Header header = new Header(1);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
+    header.removeHeader(HttpHeaders.CONTENT_TYPE);
 
     assertNull(header.getHeader(HttpHeaders.CONTENT_TYPE));
     assertEquals(0, header.getHeaders(HttpHeaders.CONTENT_TYPE).size());
@@ -129,10 +129,10 @@ public class HeaderTest {
 
   @Test
   public void testMultipleValues() {
-    Header header = new Header();
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED);
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.APPLICATION_ATOM_SVC);
-    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.APPLICATION_ATOM_XML);
+    Header header = new Header(1);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.MULTIPART_MIXED, 1);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.APPLICATION_ATOM_SVC, 2);
+    header.addHeader(HttpHeaders.CONTENT_TYPE, HttpContentType.APPLICATION_ATOM_XML, 3);
 
     final String fullHeaderString =
         HttpContentType.MULTIPART_MIXED + ", " + HttpContentType.APPLICATION_ATOM_SVC + ", "