/** * 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; }
/** * Get an absolute URL from a URL attribute that may be relative (i.e. an <code><a href></code> * or <code><img src></code>). * * <p>E.g.: <code>String absUrl = linkEl.absUrl("href");</code> * * <p>If the attribute value is already absolute (i.e. it starts with a protocol, like <code> * http://</code> or <code>https://</code> etc), and it successfully parses as a URL, the * attribute is returned directly. Otherwise, it is treated as a URL relative to the element's * {@link #baseUri}, and made absolute using that. * * <p>As an alternate, you can use the {@link #attr} method with the <code>abs:</code> prefix, * e.g.: <code>String absUrl = linkEl.attr("abs:href");</code> * * @param attributeKey The attribute key * @return An absolute URL if one could be made, or an empty string (not null) if the attribute * was missing or could not be made successfully into a URL. * @see #attr * @see java.net.URL#URL(java.net.URL, String) */ public String absUrl(String attributeKey) { Validate.notEmpty(attributeKey); String relUrl = attr(attributeKey); if (!hasAttr(attributeKey)) { return ""; // nothing to make absolute with } else { URL base; try { try { base = new URL(baseUri); } catch (MalformedURLException e) { // the base is unsuitable, but the attribute may be abs on its own, so try that URL abs = new URL(relUrl); return abs.toExternalForm(); } // workaround: java resolves '//path/file + ?foo' to '//path/?foo', not '//path/file?foo' as // desired if (relUrl.startsWith("?")) relUrl = base.getPath() + relUrl; URL abs = new URL(base, relUrl); return abs.toExternalForm(); } catch (MalformedURLException e) { return ""; } } }
/** * Find an element by ID, including or under this element. * * <p>Note that this finds the first matching ID, starting with this element. If you search down * from a different starting point, it is possible to find a different element by ID. For unique * element by ID within a Document, use {@link Document#getElementById(String)} * * @param id The ID to search for. * @return The first matching element by ID, starting with this element, or null if none found. */ public Element getElementById(String id) { Validate.notEmpty(id); Elements elements = Collector.collect(new Evaluator.Id(id), this); if (elements.size() > 0) return elements.get(0); else return null; }
public String get(String key) { Validate.notEmpty(key); if (this.attributes == null) { return UnsupportedUrlFragment.DISPLAY_NAME; } Attribute attr = (Attribute) this.attributes.get(key.toLowerCase()); return attr != null ? attr.getValue() : UnsupportedUrlFragment.DISPLAY_NAME; }
/** * 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; }
/** * Add an enforced attribute to a tag. An enforced attribute will always be added to the element. * If the element already has the attribute set, it will be overridden. * * <p>E.g.: <code>addEnforcedAttribute("a", "rel", "nofollow")</code> will make all <code>a</code> * tags output as <code><a href="..." rel="nofollow"></code> * * @param tag The tag the enforced attribute is for. The tag will be added to the allowed tag list * if necessary. * @param key The attribute key * @param value The enforced attribute value * @return this (for chaining) */ public Whitelist addEnforcedAttribute(String tag, String key, String value) { Validate.notEmpty(tag); Validate.notEmpty(key); Validate.notEmpty(value); TagName tagName = TagName.valueOf(tag); if (!tagNames.contains(tagName)) tagNames.add(tagName); AttributeKey attrKey = AttributeKey.valueOf(key); AttributeValue attrVal = AttributeValue.valueOf(value); if (enforcedAttributes.containsKey(tagName)) { enforcedAttributes.get(tagName).put(attrKey, attrVal); } else { Map<AttributeKey, AttributeValue> attrMap = new HashMap<AttributeKey, AttributeValue>(); attrMap.put(attrKey, attrVal); enforcedAttributes.put(tagName, attrMap); } return this; }
/** * 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; }
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)); } }
/** * 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 context = parent() instanceof Element ? (Element) parent() : null; List<Node> wrapChildren = Parser.parseFragment(html, context, baseUri()); Node wrapNode = wrapChildren.get(0); if (wrapNode == null || !(wrapNode instanceof Element)) // nothing to wrap with; noop return null; Element wrap = (Element) wrapNode; 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() > 0) { for (int i = 0; i < wrapChildren.size(); i++) { Node remainder = wrapChildren.get(i); remainder.parentNode.removeChild(remainder); wrap.appendChild(remainder); } } return this; }
public void remove(String key) { Validate.notEmpty(key); if (this.attributes != null) { this.attributes.remove(key.toLowerCase()); } }
/** * Change the tag of this element. For example, convert a {@code <span>} to a {@code <div>} with * {@code el.tagName("div");}. * * @param tagName new tag name for this element * @return this element, for chaining */ public Element tagName(String tagName) { Validate.notEmpty(tagName, "Tag name must not be empty."); tag = Tag.valueOf(tagName); return this; }
/** * Find elements that have an attribute name starting with the supplied prefix. Use {@code data-} * to find elements that have HTML5 datasets. * * @param keyPrefix name prefix of the attribute e.g. {@code data-} * @return elements that have attribute names that start with with the prefix, empty if none. */ public Elements getElementsByAttributeStarting(String keyPrefix) { Validate.notEmpty(keyPrefix); keyPrefix = keyPrefix.trim().toLowerCase(); return Collector.collect(new Evaluator.AttributeStarting(keyPrefix), this); }
/** * Find elements that have a named attribute set. Case insensitive. * * @param key name of the attribute, e.g. {@code href} * @return elements that have this attribute, empty if none */ public Elements getElementsByAttribute(String key) { Validate.notEmpty(key); key = key.trim().toLowerCase(); return Collector.collect(new Evaluator.Attribute(key), this); }
/** * Find elements that have this class, including or under this element. Case insensitive. * * <p>Elements can have multiple classes (e.g. {@code <div class="header round first">}. This * method checks each class, so you can find the above with {@code * el.getElementsByClass("header");}. * * @param className the name of the class to search for. * @return elements with the supplied class name, empty if none * @see #hasClass(String) * @see #classNames() */ public Elements getElementsByClass(String className) { Validate.notEmpty(className); return Collector.collect(new Evaluator.Class(className), this); }
/** * Finds elements, including and recursively under this element, with the specified tag name. * * @param tagName The tag name to search for (case insensitively). * @return a matching unmodifiable list of elements. Will be empty if this element and none of its * children match. */ public Elements getElementsByTag(String tagName) { Validate.notEmpty(tagName); tagName = tagName.toLowerCase().trim(); return Collector.collect(new Evaluator.Tag(tagName), this); }