/**
   * Writes the opening RDF tag (with namespaces) to the print Writer.
   *
   * @param out PrintWriter
   * @throws IOException
   */
  protected void writeRDFHeader(PrintWriter out) throws IOException {

    // validate
    if (out != null) {

      // print opening RDF tag (including namespaces)
      out.print("<rdf:RDF ");

      // print namespaces
      Set<String> keys = namespaces.keySet();

      if (keys != null) {

        for (String currentKey : keys) {
          String currentValue = namespaces.get(currentKey);

          if ((currentKey != null) && (currentValue != null)) {

            // use entities: xmlns:ns="&ns;"
            out.print(NEWLINE + "  xmlns:" + currentKey + "=\"&" + currentKey + ";\"");
          }
        }
      }

      // close the opening tag (add a space for readability)
      out.print(">" + NEWLINE + NEWLINE);
    } else {

      throw new IllegalArgumentException("Cannot write to null Writer.");
    }
  }
  /**
   * If the URI contains the URI of a known namespace, it is replaced.
   *
   * @param original original URI.
   * @return new URI with namespace references.
   */
  protected String replaceNamespace(String original) throws GraphException {

    // value to be returned
    String uri = original;

    // validate URI (only replace uri's with fragments)
    if (original != null) {

      // replace any URI occurances with namespace prefixes
      for (String currentKey : namespaces.keySet()) {

        String currentValue = namespaces.get(currentKey);

        // validate the Objects
        if ((currentKey != null) && (currentValue != null)) {

          // if the entire namespace is used, replace it with an entity
          if (original.equals(currentValue)) {
            uri = "&" + currentKey + ";";
          } else if (original.startsWith(currentValue.toString())) {
            // replace with namespace
            uri = original.replaceAll(currentValue.toString(), currentKey + ":");
          }
        }
      }
    }

    // return the URI with any collection/container items renamed
    return replaceCollection(uri);
  }
  /**
   * Writes the XML Entities (used for namespaces) to the print Writer.
   *
   * @param out PrintWriter
   * @throws IOException
   */
  protected void writeXMLEntities(PrintWriter out) throws IOException {

    // validate
    if (out != null) {

      // print opening DOCTYPE DECLARATION tag
      out.print(NEWLINE + "<!DOCTYPE rdf:RDF [");

      // print namespaces
      Set<String> keys = namespaces.keySet();

      if (keys != null) {

        for (String currentKey : keys) {
          String currentValue = namespaces.get(currentKey);

          if ((currentKey != null) && (currentValue != null)) {

            // write as: <!ENTITY ns 'http://example.org/abc#'>
            out.print(NEWLINE + "  <!ENTITY " + currentKey + " '" + currentValue + "'>");
          }
        }
      }

      // close the opening tag (add a space for readability)
      out.print("]>" + NEWLINE + NEWLINE);
    } else {

      throw new IllegalArgumentException("Cannot write to null Writer.");
    }
  }
  /**
   * Populates the namespaces map with default namespaces and namespaces used by the graph.
   *
   * @param graph Graph
   * @throws GraphException
   */
  protected void populateNamespaces(Graph graph) throws GraphException {

    // default namespaces
    namespaces = new HashMap<String, String>();
    namespaces.put(RDF_PREFIX, RDF.BASE_URI.toString());
    namespaces.put(RDFS_PREFIX, RDFS.BASE_URI.toString());
    namespaces.put("owl", "http://www.w3.org/2002/07/owl#");
    namespaces.put("dc", "http://purl.org/dc/elements/1.1/");

    // validate graph before reading
    if (graph == null) {
      throw new IllegalArgumentException("Graph argument is null.");
    }

    // get all statements
    ClosableIterator<Triple> tripleIter = graph.find(null, null, null);

    if (tripleIter != null) {

      while (tripleIter.hasNext()) {

        // get the next triple
        Triple triple = tripleIter.next();

        if (triple != null) {

          // evaluate subject
          SubjectNode subject = triple.getSubject();
          if (subject instanceof URIReference) {
            addNamespaceURI(((URIReference) subject).getURI());
          }

          // evaluate predicate (must be URIReference)
          PredicateNode predicate = triple.getPredicate();
          addNamespaceURI(((URIReference) predicate).getURI());

          // evaluate object
          ObjectNode object = triple.getObject();
          if (object instanceof URIReference) {
            addNamespaceURI(((URIReference) object).getURI());
          }
        }
      }

      // close the Iterator
      tripleIter.close();
    }
  }
  /**
   * Evaluates a URI and adds it to the namespace map as a namespace.
   *
   * @param uri URI
   */
  protected void addNamespaceURI(URI uri) {

    if (uri == null) {
      throw new IllegalArgumentException("URI argument is null.");
    }

    // extract URI without fragment
    String uriString = uri.toString();
    String newURI = null;

    if (uriString != null) {

      // determine what comes last a '#' or '/'
      int hashindex = uriString.lastIndexOf('#');
      int slashindex = uriString.lastIndexOf('/');

      // validate (URI must contain a forward slash)
      if (slashindex == -1) {
        // namespace may have been evaluated already
        return;
      }

      // is there a '/' after the '#'?
      if (slashindex > hashindex) {

        // remove everything after the last '/'
        int index = uriString.lastIndexOf('/');
        newURI = uriString.substring(0, index) + "/";
      } else {

        // '#' comes after last '/' (remove entire fragment)
        newURI = uriString.replaceAll(uri.getFragment(), "");
      }

      // only add namespace if it is new
      if ((newURI != null) && (!namespaces.containsValue(newURI))) {
        // add to namespaces
        namespaces.put("ns" + namespaces.size(), newURI);
      }
    }
  }