docs: protected ArrayList<Range> parseRange(final HttpServletRequest request, final HttpServletResponse response, final Attributes resourceAttributes)
  			throws IOException {
  
  		// Checking If-Range
  		final String headerValue = request.getHeader("If-Range");
  
  		if (headerValue != null) {
  
  			long headerValueTime = (-1L);
  			try {
  				headerValueTime = request.getDateHeader("If-Range");
  			} catch (final IllegalArgumentException e) {
  				// Ignore
  			}
  
  			final String eTag = resourceAttributes.getETag();
  			final long lastModified = resourceAttributes.getLastModified();
  
  			if (headerValueTime == (-1L)) {
  
  				// If the ETag the client gave does not match the entity
  				// etag, then the entire entity is returned.
  				if (!eTag.equals(headerValue.trim()))
  					return FULL;
  
  			} else {
  
  				// If the timestamp of the entity the client got is older than
  				// the last modification date of the entity, the entire entity
  				// is returned.
  				if (lastModified > (headerValueTime + 1000))
  					return FULL;
  
  			}
  
  		}
  
  		final long fileLength = resourceAttributes.getContentLength();
  
  		if (fileLength == 0)
  			return null;
  
  		// Retrieving the range header (if any is specified
  		String rangeHeader = request.getHeader("Range");
  
  		if (rangeHeader == null)
  			return null;
  		// bytes is the only range unit supported (and I don't see the point
  		// of adding new ones).
  		if (!rangeHeader.startsWith("bytes")) {
  			response.addHeader("Content-Range", "bytes */" + fileLength);
  			response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  			return null;
  		}
  
  		rangeHeader = rangeHeader.substring(6);
  
  		// Vector which will contain all the ranges which are successfully
  		// parsed.
  		final ArrayList<Range> result = new ArrayList<Range>();
  		final StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
  
  		// Parsing the range list
  		while (commaTokenizer.hasMoreTokens()) {
  			final String rangeDefinition = commaTokenizer.nextToken().trim();
  
  			final Range currentRange = new Range();
  			currentRange.length = fileLength;
  
  			final int dashPos = rangeDefinition.indexOf('-');
  
  			if (dashPos == -1) {
  				response.addHeader("Content-Range", "bytes */" + fileLength);
  				response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  				return null;
  			}
  
  			if (dashPos == 0) {
  
  				try {
  					final long offset = Long.parseLong(rangeDefinition);
  					currentRange.start = fileLength + offset;
  					currentRange.end = fileLength - 1;
  				} catch (final NumberFormatException e) {
  					response.addHeader("Content-Range", "bytes */" + fileLength);
  					response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  					return null;
  				}
  
  			} else {
  
  				try {
  					currentRange.start = Long.parseLong(rangeDefinition.substring(0, dashPos));
  					if (dashPos < rangeDefinition.length() - 1)
  						currentRange.end = Long.parseLong(rangeDefinition.substring(dashPos + 1, rangeDefinition.length()));
  					else
  						currentRange.end = fileLength - 1;
  				} catch (final NumberFormatException e) {
  					response.addHeader("Content-Range", "bytes */" + fileLength);
  					response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  					return null;
  				}
  
  			}
  
  			if (!currentRange.validate()) {
  				response.addHeader("Content-Range", "bytes */" + fileLength);
  				response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  				return null;
  			}
  
  			result.add(currentRange);
  		}
  
  		return result;
  	}