Beispiel #1
0
  /**
   * Insert the specified node into the DOM after this node (i.e. as a following sibling).
   *
   * @param node to add after this node
   * @return this node, for chaining
   * @see #before(Node)
   */
  public Node after(Node node) {
    Validate.notNull(node);
    Validate.notNull(parentNode);

    parentNode.addChildren(siblingIndex() + 1, node);
    return this;
  }
Beispiel #2
0
  private void addSiblingHtml(int index, String html) {
    Validate.notNull(html);
    Validate.notNull(parentNode);

    Element context = parent() instanceof Element ? (Element) parent() : null;
    List<Node> nodes = Parser.parseFragment(html, context, baseUri());
    parentNode.addChildren(index, nodes.toArray(new Node[nodes.size()]));
  }
Beispiel #3
0
  /**
   * Create a new Node.
   *
   * @param baseUri base URI
   * @param attributes attributes (not null, but may be empty)
   */
  protected Node(String baseUri, Attributes attributes) {
    Validate.notNull(baseUri);
    Validate.notNull(attributes);

    childNodes = new ArrayList<Node>(4);
    this.baseUri = baseUri.trim();
    this.attributes = attributes;
  }
Beispiel #4
0
  private static <E extends Element> Integer indexInList(Element search, List<E> elements) {
    Validate.notNull(search);
    Validate.notNull(elements);

    for (int i = 0; i < elements.size(); i++) {
      E element = elements.get(i);
      if (element.equals(search)) return i;
    }
    return null;
  }
Beispiel #5
0
  protected void initialiseParse(String input, String baseUri, ParseErrorList errors) {
    Validate.notNull(input, "String input must not be null");
    Validate.notNull(baseUri, "BaseURI must not be null");

    doc = new Document(baseUri);
    reader = new CharacterReader(input, this);
    this.errors = errors;
    tokeniser = new Tokeniser(reader, errors);
    stack = new DescendableLinkedList<Element>();
    this.baseUri = baseUri;
    this.input = input;
    this.resetFlag = false;
  }
Beispiel #6
0
 /**
  * Get this node's previous sibling.
  *
  * @return the previous sibling, or null if this is the first sibling
  */
 public Node previousSibling() {
   List<Node> siblings = parentNode.childNodes;
   Integer index = siblingIndex();
   Validate.notNull(index);
   if (index > 0) return siblings.get(index - 1);
   else return null;
 }
Beispiel #7
0
  /**
   * Add inner HTML into this element. The supplied HTML will be parsed, and each node prepended to
   * the start of the element's children.
   *
   * @param html HTML to add inside this element, before the existing HTML
   * @return this element
   * @see #html(String)
   */
  public Element prepend(String html) {
    Validate.notNull(html);

    List<Node> nodes = Parser.parseFragment(html, this, baseUri());
    addChildren(0, nodes.toArray(new Node[nodes.size()]));
    return this;
  }
Beispiel #8
0
 public void put(Attribute attribute) {
   Validate.notNull(attribute);
   if (this.attributes == null) {
     this.attributes = new LinkedHashMap(2);
   }
   this.attributes.put(attribute.getKey(), attribute);
 }
Beispiel #9
0
  /**
   * Add allowed URL protocols for an element's URL attribute. This restricts the possible values of
   * the attribute to URLs with the defined protocol.
   *
   * <p>E.g.: <code>addProtocols("a", "href", "ftp", "http", "https")</code>
   *
   * @param tag Tag the URL protocol is for
   * @param key Attribute key
   * @param protocols List of valid protocols
   * @return this, for chaining
   */
  public Whitelist addProtocols(String tag, String key, String... protocols) {
    Validate.notEmpty(tag);
    Validate.notEmpty(key);
    Validate.notNull(protocols);

    TagName tagName = TagName.valueOf(tag);
    AttributeKey attrKey = AttributeKey.valueOf(key);
    Map<AttributeKey, Set<Protocol>> attrMap;
    Set<Protocol> protSet;

    if (this.protocols.containsKey(tagName)) {
      attrMap = this.protocols.get(tagName);
    } else {
      attrMap = new HashMap<AttributeKey, Set<Protocol>>();
      this.protocols.put(tagName, attrMap);
    }
    if (attrMap.containsKey(attrKey)) {
      protSet = attrMap.get(attrKey);
    } else {
      protSet = new HashSet<Protocol>();
      attrMap.put(attrKey, protSet);
    }
    for (String protocol : protocols) {
      Validate.notEmpty(protocol);
      Protocol prot = Protocol.valueOf(protocol);
      protSet.add(prot);
    }
    return this;
  }
Beispiel #10
0
 /**
  * Gets the previous element sibling of this element.
  *
  * @return the previous element, or null if there is no previous element
  * @see #nextElementSibling()
  */
 public Element previousElementSibling() {
   if (parentNode == null) return null;
   List<Element> siblings = parent().children();
   Integer index = indexInList(this, siblings);
   Validate.notNull(index);
   if (index > 0) return siblings.get(index - 1);
   else return null;
 }
Beispiel #11
0
 /**
  * Gets the next sibling element of this element. E.g., if a {@code div} contains two {@code p}s,
  * the {@code nextElementSibling} of the first {@code p} is the second {@code p}.
  *
  * <p>This is similar to {@link #nextSibling()}, but specifically finds only Elements
  *
  * @return the next element, or null if there is no next element
  * @see #previousElementSibling()
  */
 public Element nextElementSibling() {
   if (parentNode == null) return null;
   List<Element> siblings = parent().children();
   Integer index = indexInList(this, siblings);
   Validate.notNull(index);
   if (siblings.size() > index + 1) return siblings.get(index + 1);
   else return null;
 }
Beispiel #12
0
  /**
   * Get an attribute's value by its key.
   *
   * <p>To get an absolute URL from an attribute that may be a relative URL, prefix the key with
   * <code><b>abs</b></code>, which is a shortcut to the {@link #absUrl} method. E.g.:
   *
   * <blockquote>
   *
   * <code>String url = a.attr("abs:href");</code>
   *
   * </blockquote>
   *
   * @param attributeKey The attribute key.
   * @return The attribute, or empty string if not present (to avoid nulls).
   * @see #attributes()
   * @see #hasAttr(String)
   * @see #absUrl(String)
   */
  public String attr(String attributeKey) {
    Validate.notNull(attributeKey);

    if (attributes.hasKey(attributeKey)) return attributes.get(attributeKey);
    else if (attributeKey.toLowerCase().startsWith("abs:"))
      return absUrl(attributeKey.substring("abs:".length()));
    else return "";
  }
Beispiel #13
0
  /**
   * Set the text of this element. Any existing contents (text or elements) will be cleared
   *
   * @param text unencoded text
   * @return this element
   */
  public Element text(String text) {
    Validate.notNull(text);

    empty();
    TextNode textNode = new TextNode(text, baseUri);
    appendChild(textNode);

    return this;
  }
Beispiel #14
0
  /**
   * Remove a class name from this element's {@code class} attribute.
   *
   * @param className class name to remove
   * @return this element
   */
  public Element removeClass(String className) {
    Validate.notNull(className);

    Set<String> classes = classNames();
    classes.remove(className);
    classNames(classes);

    return this;
  }
Beispiel #15
0
  /**
   * Test if this element has an attribute.
   *
   * @param attributeKey The attribute key to check.
   * @return true if the attribute exists, false if not.
   */
  public boolean hasAttr(String attributeKey) {
    Validate.notNull(attributeKey);

    if (attributeKey.toLowerCase().startsWith("abs:")) {
      String key = attributeKey.substring("abs:".length());
      if (attributes.hasKey(key) && !absUrl(key).equals("")) return true;
    }
    return attributes.hasKey(attributeKey);
  }
Beispiel #16
0
  /**
   * Get this node's next sibling.
   *
   * @return next sibling, or null if this is the last sibling
   */
  public Node nextSibling() {
    if (parentNode == null) return null; // root

    List<Node> siblings = parentNode.childNodes;
    Integer index = siblingIndex();
    Validate.notNull(index);
    if (siblings.size() > index + 1) return siblings.get(index + 1);
    else return null;
  }
Beispiel #17
0
  /**
   * Add a list of allowed elements to a whitelist. (If a tag is not allowed, it will be removed
   * from the HTML.)
   *
   * @param tags tag names to allow
   * @return this (for chaining)
   */
  public Whitelist addTags(String... tags) {
    Validate.notNull(tags);

    for (String tagName : tags) {
      Validate.notEmpty(tagName);
      tagNames.add(TagName.valueOf(tagName));
    }
    return this;
  }
Beispiel #18
0
  /**
   * Toggle a class name on this element's {@code class} attribute: if present, remove it; otherwise
   * add it.
   *
   * @param className class name to toggle
   * @return this element
   */
  public Element toggleClass(String className) {
    Validate.notNull(className);

    Set<String> classes = classNames();
    if (classes.contains(className)) classes.remove(className);
    else classes.add(className);
    classNames(classes);

    return this;
  }
Beispiel #19
0
  /**
   * Removes this node from the DOM, and moves its children up into the node's parent. This has the
   * effect of dropping the node but keeping its children.
   *
   * <p>For example, with the input html:<br>
   * {@code <div>One <span>Two <b>Three</b></span></div>}<br>
   * Calling {@code element.unwrap()} on the {@code span} element will result in the html:<br>
   * {@code <div>One Two <b>Three</b></div>}<br>
   * and the {@code "Two "} {@link TextNode} being returned.
   *
   * @return the first child of this node, after the node has been unwrapped. Null if the node had
   *     no children.
   * @see #remove()
   * @see #wrap(String)
   */
  public Node unwrap() {
    Validate.notNull(parentNode);

    int index = siblingIndex;
    Node firstChild = childNodes.size() > 0 ? childNodes.get(0) : null;
    parentNode.addChildren(index, this.childNodesAsArray());
    this.remove();

    return firstChild;
  }
Beispiel #20
0
  /**
   * Creates a new, clean document, from the original dirty document, containing only elements
   * allowed by the whitelist. The original document is not modified. Only elements from the dirt
   * document's <code>body</code> are used.
   *
   * @param dirtyDocument Untrusted base document to clean.
   * @return cleaned document.
   */
  public Document clean(Document dirtyDocument) {
    Validate.notNull(dirtyDocument);

    Document clean = Document.createShell(dirtyDocument.baseUri());
    if (dirtyDocument.body()
        != null) // frameset documents won't have a body. the clean doc will have empty body.
    copySafeNodes(dirtyDocument.body(), clean.body());

    return clean;
  }
Beispiel #21
0
  /**
   * Determines if the input document <b>body</b>is valid, against the whitelist. It is considered
   * valid if all the tags and attributes in the input HTML are allowed by the whitelist, and that
   * there is no content in the <code>head</code>.
   *
   * <p>This method can be used as a validator for user input. An invalid document will still be
   * cleaned successfully using the {@link #clean(Document)} document. If using as a validator, it
   * is recommended to still clean the document to ensure enforced attributes are set correctly, and
   * that the output is tidied.
   *
   * @param dirtyDocument document to test
   * @return true if no tags or attributes need to be removed; false if they do
   */
  public boolean isValid(Document dirtyDocument) {
    Validate.notNull(dirtyDocument);

    Document clean = Document.createShell(dirtyDocument.baseUri());
    int numDiscarded = copySafeNodes(dirtyDocument.body(), clean.body());
    return numDiscarded == 0
        && dirtyDocument.head().childNodes().size()
            == 0; // because we only look at the body, but we start from a shell, make sure there's
                  // nothing in the head
  }
Beispiel #22
0
  /**
   * Inserts the given child nodes into this element at the specified index. Current nodes will be
   * shifted to the right. The inserted nodes will be moved from their current parent. To prevent
   * moving, copy the nodes first.
   *
   * @param index 0-based index to insert children at. Specify {@code 0} to insert at the start,
   *     {@code -1} at the end
   * @param children child nodes to insert
   * @return this element, for chaining.
   */
  public Element insertChildren(int index, Collection<? extends Node> children) {
    Validate.notNull(children, "Children collection to be inserted must not be null.");
    int currentSize = childNodeSize();
    if (index < 0) index += currentSize + 1; // roll around
    Validate.isTrue(index >= 0 && index <= currentSize, "Insert position out of bounds.");

    ArrayList<Node> nodes = new ArrayList<Node>(children);
    Node[] nodeArray = nodes.toArray(new Node[nodes.size()]);
    addChildren(index, nodeArray);
    return this;
  }
Beispiel #23
0
  protected void replaceChild(Node out, Node in) {
    Validate.isTrue(out.parentNode == this);
    Validate.notNull(in);
    if (in.parentNode != null) in.parentNode.removeChild(in);

    Integer index = out.siblingIndex();
    childNodes.set(index, in);
    in.parentNode = this;
    in.setSiblingIndex(index);
    out.parentNode = null;
  }
Beispiel #24
0
 public static Chat createChat(Skype client, String identity) throws SkypeException {
   Validate.notNull(client, "Client must not be null");
   Validate.isTrue(
       client instanceof SkypeImpl,
       String.format("Now is not the time to use that, %s", client.getUsername()));
   Validate.notEmpty(identity, "Identity must not be null/empty");
   if (identity.startsWith("19:")) {
     if (identity.endsWith("@thread.skype")) {
       return new ChatGroup((SkypeImpl) client, identity);
     } else {
       client.getLogger().info(String.format("Skipping P2P chat with identity %s", identity));
       return null;
     }
   } else if (identity.startsWith("8:")) {
     return new ChatIndividual((SkypeImpl) client, identity);
   } else {
     throw new IllegalArgumentException(
         String.format("Unknown group type with identity %s", identity));
   }
 }
Beispiel #25
0
  /**
   * Add a list of allowed attributes to a tag. (If an attribute is not allowed on an element, it
   * will be removed.)
   *
   * <p>E.g.: <code>addAttributes("a", "href", "class")</code> allows <code>href</code> and <code>
   * class</code> attributes on <code>a</code> tags.
   *
   * <p>To make an attribute valid for <b>all tags</b>, use the pseudo tag <code>:all</code>, e.g.
   * <code>addAttributes(":all", "class")</code>.
   *
   * @param tag The tag the attributes are for. The tag will be added to the allowed tag list if
   *     necessary.
   * @param keys List of valid attributes for the tag
   * @return this (for chaining)
   */
  public Whitelist addAttributes(String tag, String... keys) {
    Validate.notEmpty(tag);
    Validate.notNull(keys);
    Validate.isTrue(keys.length > 0, "No attributes supplied.");

    TagName tagName = TagName.valueOf(tag);
    if (!tagNames.contains(tagName)) tagNames.add(tagName);
    Set<AttributeKey> attributeSet = new HashSet<AttributeKey>();
    for (String key : keys) {
      Validate.notEmpty(key);
      attributeSet.add(AttributeKey.valueOf(key));
    }
    if (attributes.containsKey(tagName)) {
      Set<AttributeKey> currentSet = attributes.get(tagName);
      currentSet.addAll(attributeSet);
    } else {
      attributes.put(tagName, attributeSet);
    }
    return this;
  }
Beispiel #26
0
 /**
  * Replace this node in the DOM with the supplied node.
  *
  * @param in the node that will will replace the existing node.
  */
 public void replaceWith(Node in) {
   Validate.notNull(in);
   Validate.notNull(parentNode);
   parentNode.replaceChild(this, in);
 }
Beispiel #27
0
 /**
  * Create a new cleaner, that sanitizes documents using the supplied whitelist.
  *
  * @param whitelist white-list to clean with
  */
 public Cleaner(Whitelist whitelist) {
   Validate.notNull(whitelist);
   this.whitelist = whitelist;
 }
Beispiel #28
0
 /**
  * Remove (delete) this node from the DOM tree. If this node has children, they are also removed.
  */
 public void remove() {
   Validate.notNull(parentNode);
   parentNode.removeChild(this);
 }
Beispiel #29
0
 /**
  * Update the base URI of this node.
  *
  * @param baseUri base URI to set
  */
 public void setBaseUri(String baseUri) {
   Validate.notNull(baseUri);
   this.baseUri = baseUri;
 }
Beispiel #30
0
 /**
  * Remove an attribute from this element.
  *
  * @param attributeKey The attribute to remove.
  * @return this (for chaining)
  */
 public Node removeAttr(String attributeKey) {
   Validate.notNull(attributeKey);
   attributes.remove(attributeKey);
   return this;
 }