Skip to content

Commit

Permalink
Moved .wrap, .before, and .after from Element to Node for flexibility…
Browse files Browse the repository at this point in the history
…. Overriding implementations in Element still return Element.
  • Loading branch information
jhy committed Feb 3, 2011
1 parent e3ddbb8 commit eea130b
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 47 deletions.
62 changes: 15 additions & 47 deletions src/main/java/org/jsoup/nodes/Element.java
Expand Up @@ -303,37 +303,31 @@ public Element prepend(String html) {
addChildren(0, fragment.childNodesAsArray());
return this;
}

/**
* Insert the specified HTML into the DOM before this element (i.e. as a preceeding sibling).
*
* @param html HTML to add before this element
* @return this element, for chaining
* @see #after(String)
*/
@Override
public Element before(String html) {
addSiblingHtml(siblingIndex(), html);
return this;
return (Element) super.before(html);
}

/**
* Insert the specified HTML into the DOM after this element (i.e. as a following sibling).
*
* @param html HTML to add after this element
* @return this element, for chaining
* @see #before(String)
*/
@Override
public Element after(String html) {
addSiblingHtml(siblingIndex()+1, html);
return this;
}

private void addSiblingHtml(int index, String html) {
Validate.notNull(html);
Validate.notNull(parentNode);

Element fragment = Parser.parseBodyFragmentRelaxed(html, baseUri()).body();
parentNode.addChildren(index, fragment.childNodesAsArray());
return (Element) super.after(html);
}

/**
* Remove all of the element's child nodes. Any attributes are left as-is.
* @return this element
Expand All @@ -344,42 +338,16 @@ public Element empty() {
}

/**
Wrap the supplied HTML around this element.
@param html HTML to wrap around this element, e.g. {@code <div class="head"></div>}. Can be arbitralily deep.
@return this element, for chaining.
* Wrap the supplied HTML around this element.
*
* @param html HTML to wrap around this element, e.g. {@code <div class="head"></div>}. Can be arbitrarily deep.
* @return this element, for chaining.
*/
@Override
public Element wrap(String html) {
Validate.notEmpty(html);

Element wrapBody = Parser.parseBodyFragmentRelaxed(html, baseUri).body();
Elements wrapChildren = wrapBody.children();
Element wrap = wrapChildren.first();
if (wrap == null) // nothing to wrap with; noop
return null;

Element deepest = getDeepChild(wrap);
parentNode.replaceChild(this, wrap);
deepest.addChildren(this);

// remainder (unbalananced wrap, like <div></div><p></p> -- The <p> is remainder
if (wrapChildren.size() > 1) {
for (int i = 1; i < wrapChildren.size(); i++) { // skip first
Element remainder = wrapChildren.get(i);
remainder.parentNode.removeChild(remainder);
wrap.appendChild(remainder);
}
}
return this;
return (Element) super.wrap(html);
}

private Element getDeepChild(Element el) {
List<Element> children = el.children();
if (children.size() > 0)
return getDeepChild(children.get(0));
else
return el;
}

/**
* Get sibling elements.
* @return sibling elements
Expand Down
69 changes: 69 additions & 0 deletions src/main/java/org/jsoup/nodes/Node.java
Expand Up @@ -2,6 +2,8 @@

import org.jsoup.helper.StringUtil;
import org.jsoup.helper.Validate;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import org.jsoup.select.NodeTraversor;
import org.jsoup.select.NodeVisitor;

Expand Down Expand Up @@ -230,6 +232,73 @@ public void remove() {
Validate.notNull(parentNode);
parentNode.removeChild(this);
}

/**
* Insert the specified HTML into the DOM before this node (i.e. as a preceeding sibling).
* @param html HTML to add before this element
* @return this node, for chaining
* @see #after(String)
*/
public Node before(String html) {
addSiblingHtml(siblingIndex(), html);
return this;
}

/**
* Insert the specified HTML into the DOM after this node (i.e. as a following sibling).
* @param html HTML to add after this element
* @return this node, for chaining
* @see #before(String)
*/
public Node after(String html) {
addSiblingHtml(siblingIndex()+1, html);
return this;
}

private void addSiblingHtml(int index, String html) {
Validate.notNull(html);
Validate.notNull(parentNode);

Element fragment = Parser.parseBodyFragmentRelaxed(html, baseUri()).body();
parentNode.addChildren(index, fragment.childNodesAsArray());
}

/**
Wrap the supplied HTML around this node.
@param html HTML to wrap around this element, e.g. {@code <div class="head"></div>}. Can be arbitrarily deep.
@return this node, for chaining.
*/
public Node wrap(String html) {
Validate.notEmpty(html);

Element wrapBody = Parser.parseBodyFragmentRelaxed(html, baseUri).body();
Elements wrapChildren = wrapBody.children();
Element wrap = wrapChildren.first();
if (wrap == null) // nothing to wrap with; noop
return null;

Element deepest = getDeepChild(wrap);
parentNode.replaceChild(this, wrap);
deepest.addChildren(this);

// remainder (unbalanced wrap, like <div></div><p></p> -- The <p> is remainder
if (wrapChildren.size() > 1) {
for (int i = 1; i < wrapChildren.size(); i++) { // skip first
Element remainder = wrapChildren.get(i);
remainder.parentNode.removeChild(remainder);
wrap.appendChild(remainder);
}
}
return this;
}

private Element getDeepChild(Element el) {
List<Element> children = el.children();
if (children.size() > 0)
return getDeepChild(children.get(0));
else
return el;
}

/**
* Replace this node in the DOM with the supplied node.
Expand Down
10 changes: 10 additions & 0 deletions src/test/java/org/jsoup/nodes/TextNodeTest.java
Expand Up @@ -56,4 +56,14 @@ public class TextNodeTest {
assertEquals("Hello there!", div.text());
assertTrue(tn.parent() == tail.parent());
}

@Test public void testSplitAnEmbolden() {
Document doc = Jsoup.parse("<div>Hello there</div>");
Element div = doc.select("div").first();
TextNode tn = (TextNode) div.childNode(0);
TextNode tail = tn.splitText(6);
tail.wrap("<b></b>");

assertEquals("Hello <b>there</b>", TextUtil.stripNewlines(div.html())); // not great that we get \n<b>there there... must correct
}
}

0 comments on commit eea130b

Please sign in to comment.