/** {@inheritDoc } */
  @Override
  protected NavigationLinks retrieveNavigationInfo(final String entitySetName, final InputStream is)
      throws Exception {

    final NavigationLinks links = new NavigationLinks();

    final XMLEventReader reader = getEventReader(is);

    try {
      final List<Map.Entry<String, String>> filter = new ArrayList<Map.Entry<String, String>>();
      filter.add(
          new AbstractMap.SimpleEntry<String, String>("type", "application/atom+xml;type=entry"));
      filter.add(
          new AbstractMap.SimpleEntry<String, String>("type", "application/atom+xml;type=feed"));

      int startDepth = 0;

      while (true) {
        // a. search for link with type attribute equals to "application/atom+xml;type=entry/feed"
        final Map.Entry<Integer, XMLElement> linkInfo =
            extractElement(
                reader,
                null,
                Collections.<String>singletonList(Constants.get(ConstantKey.LINK)),
                filter,
                true,
                startDepth,
                2,
                2);
        final XMLElement link = linkInfo.getValue();
        startDepth = linkInfo.getKey();

        final String title = link.getStart().getAttributeByName(new QName("title")).getValue();

        final Attribute hrefAttr = link.getStart().getAttributeByName(new QName("href"));
        final String href = hrefAttr == null ? null : hrefAttr.getValue();

        try {
          final XMLElement inlineElement =
              extractElement(
                      link.getContentReader(),
                      null,
                      Collections.<String>singletonList(Constants.get(ConstantKey.INLINE)),
                      0,
                      -1,
                      -1)
                  .getValue();
          final XMLEventReader inlineReader = inlineElement.getContentReader();

          try {
            while (true) {
              final XMLElement entry =
                  extractElement(
                          inlineReader, null, Collections.<String>singletonList("entry"), 0, -1, -1)
                      .getValue();
              links.addInlines(title, entry.toStream());
            }
          } catch (Exception e) {
            // Reached the end of document
          }

          inlineReader.close();
        } catch (Exception ignore) {
          // inline element not found (inlines are not mondatory).
          if (StringUtils.isNotBlank(href) && ENTITY_URI_PATTERN.matcher(href).matches()) {
            links.addLinks(title, href.substring(href.lastIndexOf('/') + 1));
          }
        }
      }
    } catch (Exception ignore) {
      // ignore
    } finally {
      reader.close();
    }

    return links;
  }
  @Override
  public InputStream readEntities(
      final List<String> links, final String linkName, final String next, final boolean forceFeed)
      throws Exception {

    if (links.isEmpty()) {
      throw new NotFoundException();
    }

    final Charset encoding = Charset.forName("UTF-8");

    final ByteArrayOutputStream bos = new ByteArrayOutputStream();
    final OutputStreamWriter writer = new OutputStreamWriter(bos, encoding);

    writer.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>".toCharArray());

    if (forceFeed || links.size() > 1) {
      // build a feed

      writer.write(
          ("<feed xml:base=\""
                  + Constants.get(ConstantKey.DEFAULT_SERVICE_URL)
                  + "\" "
                  + "xmlns=\"http://www.w3.org/2005/Atom\" "
                  + "xmlns:d=\"http://schemas.microsoft.com/ado/2007/08/dataservices\" "
                  + "xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\">")
              .toCharArray());

      writer.write(
          ("<id>"
                  + Constants.get(ConstantKey.DEFAULT_SERVICE_URL)
                  + "entityset(entityid)/"
                  + linkName
                  + "</id>")
              .toCharArray());

      writer.write(("<title type=\"text\">" + linkName + "</title>").toCharArray());
      writer.write("<updated>2014-03-03T13:40:49Z</updated>".toCharArray());
      writer.write(
          ("<link rel=\"self\" title=\"" + linkName + "\" href=\"" + linkName + "\" />")
              .toCharArray());
    }

    for (String link : links) {
      try {
        final Map.Entry<String, String> uri = Commons.parseEntityURI(link);

        final XMLElement entry =
            extractElement(
                    getEventReader(
                        readEntity(uri.getKey(), uri.getValue(), Accept.ATOM).getValue()),
                    null,
                    Collections.<String>singletonList("entry"),
                    0,
                    1,
                    1)
                .getValue();

        IOUtils.copy(entry.toStream(), writer, encoding);
      } catch (Exception e) {
        // log and ignore link
        LOG.warn("Error parsing uri {}", link, e);
      }
    }

    if (forceFeed || links.size() > 1) {

      if (StringUtils.isNotBlank(next)) {
        writer.write(String.format("<link rel=\"next\" href=\"%s\" />", next).toCharArray());
      }

      writer.write("</feed>".toCharArray());
    }

    writer.flush();
    writer.close();

    return new ByteArrayInputStream(bos.toByteArray());
  }
  @Override
  public Map<String, InputStream> getChanges(final InputStream src) throws Exception {
    final Map<String, InputStream> res = new HashMap<String, InputStream>();

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    IOUtils.copy(src, bos);
    IOUtils.closeQuietly(src);

    // retrieve properties ...
    XMLEventReader reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));

    final Map.Entry<Integer, XMLElement> propertyElement =
        extractElement(
            reader,
            null,
            Collections.<String>singletonList(Constants.get(ConstantKey.PROPERTIES)),
            0,
            2,
            3);
    reader.close();

    reader = propertyElement.getValue().getContentReader();

    try {
      while (true) {
        final XMLElement property = extractElement(reader, null, null, 0, -1, -1).getValue();
        res.put(property.getStart().getName().getLocalPart(), property.toStream());
      }
    } catch (Exception ignore) {
      // end
    }

    reader.close();

    // retrieve links ...
    reader = getEventReader(new ByteArrayInputStream(bos.toByteArray()));

    try {
      int pos = 0;
      while (true) {
        final Map.Entry<Integer, XMLElement> linkElement =
            extractElement(
                reader,
                null,
                Collections.<String>singletonList(Constants.get(ConstantKey.LINK)),
                pos,
                2,
                2);

        res.put(
            "[Constants.get(ConstantKey.LINK)]"
                + linkElement
                    .getValue()
                    .getStart()
                    .getAttributeByName(new QName("title"))
                    .getValue(),
            linkElement.getValue().toStream());

        pos = linkElement.getKey();
      }
    } catch (Exception ignore) {
      // end
    }

    return res;
  }