| Index: adblockplussbrowser/src/main/java/org/adblockplus/sbrowser/contentblocker/engine/DefaultSubscriptions.java |
| =================================================================== |
| new file mode 100644 |
| --- /dev/null |
| +++ b/adblockplussbrowser/src/main/java/org/adblockplus/sbrowser/contentblocker/engine/DefaultSubscriptions.java |
| @@ -0,0 +1,293 @@ |
| +/* |
| + * This file is part of Adblock Plus <https://adblockplus.org/>, |
| + * Copyright (C) 2006-present eyeo GmbH |
| + * |
| + * Adblock Plus is free software: you can redistribute it and/or modify |
| + * it under the terms of the GNU General Public License version 3 as |
| + * published by the Free Software Foundation. |
| + * |
| + * Adblock Plus is distributed in the hope that it will be useful, |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| + * GNU General Public License for more details. |
| + * |
| + * You should have received a copy of the GNU General Public License |
| + * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
| + */ |
| + |
| +package org.adblockplus.sbrowser.contentblocker.engine; |
| + |
| +import java.io.IOException; |
| +import java.io.InputStream; |
| +import java.net.URL; |
| +import java.util.ArrayList; |
| +import java.util.HashMap; |
| +import java.util.LinkedList; |
| +import java.util.List; |
| + |
| +import javax.xml.parsers.ParserConfigurationException; |
| +import javax.xml.parsers.SAXParser; |
| +import javax.xml.parsers.SAXParserFactory; |
| + |
| +import org.xml.sax.Attributes; |
| +import org.xml.sax.SAXException; |
| +import org.xml.sax.helpers.DefaultHandler; |
| + |
| +final class DefaultSubscriptions |
| +{ |
| + private final List<DefaultSubscriptionInfo> subscriptions = new ArrayList<>(); |
| + private final List<DefaultSubscriptionInfo> linearSubscriptions = new ArrayList<>(); |
| + private final HashMap<String, DefaultSubscriptionInfo> urlMap = new HashMap<>(); |
| + private final List<DefaultSubscriptionInfo> adsSubscriptions = new ArrayList<>(); |
| + private final List<DefaultSubscriptionInfo> otherSubscriptions = new ArrayList<>(); |
| + |
| + private DefaultSubscriptions initialize() |
| + { |
| + this.listSubscriptions(this.linearSubscriptions); |
| + |
| + for (final DefaultSubscriptionInfo s : this.linearSubscriptions) |
| + { |
| + final String url = s.getUrl(); |
| + final String type = s.getType(); |
| + |
| + if (url.length() > 0) |
| + { |
| + this.urlMap.put(url, s); |
| + } |
| + |
| + if (type.length() == 0 || type.equals("ads")) |
| + { |
| + this.adsSubscriptions.add(s); |
| + } |
| + else if (type.equals("other")) |
| + { |
| + this.otherSubscriptions.add(s); |
| + } |
| + } |
| + |
| + return this; |
| + } |
| + |
| + public List<Subscription> createSubscriptions() throws IOException |
| + { |
| + final ArrayList<Subscription> subs = new ArrayList<>(); |
| + for (DefaultSubscriptionInfo info : this.linearSubscriptions) |
| + { |
| + if (!info.getUrl().isEmpty()) |
| + { |
| + final Subscription sub = Subscription.create(info.getUrl()); |
| + sub.putMeta(Subscription.KEY_TITLE, info.getTitle()); |
| + subs.add(sub); |
| + } |
| + } |
| + return subs; |
| + } |
| + |
| + public DefaultSubscriptionInfo getForUrl(final String url) |
| + { |
| + return this.urlMap.get(url); |
| + } |
| + |
| + public DefaultSubscriptionInfo getForUrl(final URL url) |
| + { |
| + return url != null ? this.getForUrl(url.toString()) : null; |
| + } |
| + |
| + public List<DefaultSubscriptionInfo> getAdsSubscriptions() |
| + { |
| + return adsSubscriptions; |
| + } |
| + |
| + private void listSubscriptions(final List<DefaultSubscriptionInfo> output) |
| + { |
| + for (final DefaultSubscriptionInfo s : this.subscriptions) |
| + { |
| + this.listSubscriptions(s, output); |
| + } |
| + } |
| + |
| + private void listSubscriptions(final DefaultSubscriptionInfo parent, |
| + final List<DefaultSubscriptionInfo> output) |
| + { |
| + output.add(parent); |
| + for (final DefaultSubscriptionInfo s : parent.variants) |
| + { |
| + this.listSubscriptions(s, output); |
| + } |
| + for (final DefaultSubscriptionInfo s : parent.supplements) |
| + { |
| + this.listSubscriptions(s, output); |
| + } |
| + } |
| + |
| + public static DefaultSubscriptions fromStream(final InputStream in) throws IOException |
| + { |
| + try |
| + { |
| + final SAXParserFactory factory = SAXParserFactory.newInstance(); |
| + factory.setValidating(false); |
| + final SAXParser parser = factory.newSAXParser(); |
| + final SubscriptionParser handler = new SubscriptionParser(); |
| + parser.parse(in, handler); |
| + return handler.subscriptions.initialize(); |
| + } |
| + catch (final ParserConfigurationException | SAXException e) |
| + { |
| + throw new IOException(e.getClass().getSimpleName() + ": " + e.getMessage()); |
| + } |
| + } |
| + |
| + private static class SubscriptionParser extends DefaultHandler |
| + { |
| + private boolean inSubscriptions = false; |
| + private boolean inVariants = false; |
| + |
| + private final static String KEY_SUPPLEMENTS = "supplements"; |
| + private final static String KEY_SUBSCRIPTIONS = "subscriptions"; |
| + private final static String KEY_SUBSCRIPTION = "subscription"; |
| + private final static String KEY_VARIANTS = "variants"; |
| + private final static String KEY_VARIANT = "variant"; |
| + |
| + private final DefaultSubscriptions subscriptions = new DefaultSubscriptions(); |
| + private final LinkedList<DefaultSubscriptionInfo> subscriptionStack = new LinkedList<>(); |
| + private DefaultSubscriptionInfo subscription = null; |
| + private DefaultSubscriptionInfo variant = null; |
| + |
| + @Override |
| + public void startElement(final String uri, final String localName, final String qName, |
| + final Attributes attributes) throws SAXException |
| + { |
| + super.startElement(uri, localName, qName, attributes); |
| + |
| + if (KEY_SUBSCRIPTIONS.equals(qName)) |
| + { |
| + this.inSubscriptions = true; |
| + } |
| + else if (KEY_SUBSCRIPTION.equals(qName)) |
| + { |
| + if (!this.inSubscriptions) |
| + { |
| + throw new SAXException("<subscription> outside <subscriptions>"); |
| + } |
| + if (this.subscription != null) |
| + { |
| + throw new SAXException("nested <subscription>"); |
| + } |
| + this.subscription = new DefaultSubscriptionInfo(); |
| + for (int i = 0; i < attributes.getLength(); i++) |
| + { |
| + this.subscription.attributes.put(attributes.getQName(i), attributes.getValue(i)); |
| + } |
| + } |
| + else if (KEY_VARIANTS.equals(qName)) |
| + { |
| + this.inVariants = true; |
| + } |
| + else if (KEY_VARIANT.equals(qName)) |
| + { |
| + if (!this.inVariants) |
| + { |
| + throw new SAXException("<variant> outside <variants>"); |
| + } |
| + if (!this.inSubscriptions) |
| + { |
| + throw new SAXException("<variant> outside <subscriptions>"); |
| + } |
| + if (this.subscription == null) |
| + { |
| + throw new SAXException("<variant> outside <subscription>"); |
| + } |
| + if (this.variant != null) |
| + { |
| + throw new SAXException("nested <variant>"); |
| + } |
| + this.variant = new DefaultSubscriptionInfo(); |
| + this.variant.attributes.putAll(this.subscription.attributes); |
| + for (int i = 0; i < attributes.getLength(); i++) |
| + { |
| + this.variant.attributes.put(attributes.getQName(i), attributes.getValue(i)); |
| + } |
| + } |
| + else if (KEY_SUPPLEMENTS.equals(qName)) |
| + { |
| + if (!this.inSubscriptions) |
| + { |
| + throw new SAXException("<supplements> outside <subscriptions>"); |
| + } |
| + if (this.subscription == null) |
| + { |
| + throw new SAXException("<supplements> outside <subscription>"); |
| + } |
| + this.subscriptionStack.addFirst(this.subscription); |
| + this.subscription = null; |
| + } |
| + } |
| + |
| + @Override |
| + public void endElement(final String uri, final String localName, final String qName) |
| + throws SAXException |
| + { |
| + super.endElement(uri, localName, qName); |
| + |
| + if (KEY_SUBSCRIPTIONS.equals(qName)) |
| + { |
| + if (!this.inSubscriptions) |
| + { |
| + throw new SAXException("</subscriptions> without <subscriptions>"); |
| + } |
| + this.inSubscriptions = false; |
| + } |
| + else if (KEY_SUBSCRIPTION.equals(qName)) |
| + { |
| + if (this.subscription == null) |
| + { |
| + throw new SAXException("</subscription> without <subscription>"); |
| + } |
| + if (!this.subscriptionStack.isEmpty()) |
| + { |
| + this.subscription.parent = this.subscriptionStack.getFirst(); |
| + this.subscription.parent.supplements.add(this.subscription); |
| + } |
| + else |
| + { |
| + this.subscriptions.subscriptions.add(this.subscription); |
| + } |
| + this.subscription = null; |
| + } |
| + else if (KEY_VARIANTS.equals(qName)) |
| + { |
| + if (!this.inVariants) |
| + { |
| + throw new SAXException("</variants> without </variants>"); |
| + } |
| + this.inVariants = false; |
| + } |
| + else if (KEY_VARIANT.equals(qName)) |
| + { |
| + if (this.variant == null) |
| + { |
| + throw new SAXException("</variant> without </variant>"); |
| + } |
| + |
| + this.variant.parent = this.subscription; |
| + this.subscription.variants.add(this.variant); |
| + |
| + this.variant = null; |
| + } |
| + else if (KEY_SUPPLEMENTS.equals(qName)) |
| + { |
| + if (this.subscriptionStack.isEmpty()) |
| + { |
| + throw new SAXException("</supplements> without </supplements>"); |
| + } |
| + this.subscription = this.subscriptionStack.removeFirst(); |
| + } |
| + } |
| + } |
| + |
| + @Override |
| + public String toString() |
| + { |
| + return this.linearSubscriptions.toString(); |
| + } |
| +} |