// Path: src/main/java/moulder/Moulder.java
// public interface Moulder {
//     /**
//      * This is where a moulder can process the input element to generate its
//      * result.
//      *
//      * @param element the element and its associated data to process
//      * @return the list of nodes and their associated data that were generated
//      *         after processing the input element.
//      */
//     List<Node> process(Element element);
// }
// 
// Path: src/main/java/moulder/MoulderChain.java
// public class MoulderChain {
//         private final List<Moulder> moulders;
// 
//         public MoulderChain(List<Moulder> moulders) {
//             this.moulders = moulders;
//         }
// 
//         public MoulderChain add(Moulder... moulders) {
//             return add(Arrays.asList(moulders));
//         }
// 
//         public MoulderChain add(List<Moulder> moulders) {
//             this.moulders.addAll(moulders);
//             return null;
//         }
// 
// }
// 
// Path: src/main/java/moulder/Registry.java
// public class Registry {
// 
//     public static final class TemplatorConfig {
//         public final String selector;
//         public final List<Moulder> templators;
// 
//         public TemplatorConfig(String selector, List<Moulder> templators) {
//             this.selector = selector;
//             this.templators = templators;
//         }
//     }
// 
//     private final List<TemplatorConfig> cfg = new ArrayList<TemplatorConfig>();
// 
//     /**
//      * Registers a number of moulders to be applied to its input element's
//      * children returned by the supplied selector
//      *
//      * @param selector
//      *            to selected the input element's children to be processed
//      * @param templators
//      *            the moulders to apply on the selected children
//      */
//     public void register(String selector, List<Moulder> templators) {
//         cfg.add(new TemplatorConfig(selector, templators));
//     }
// 
//     /**
//      * Return the registered selector/moulders pairs
//      *
//      * @return
//      */
//     public List<TemplatorConfig> getConfig() {
//         return cfg;
//     }
// }
// 
// Path: src/main/java/moulder/Registry.java
// public static final class TemplatorConfig {
//     public final String selector;
//     public final List<Moulder> templators;
// 
//     public TemplatorConfig(String selector, List<Moulder> templators) {
//         this.selector = selector;
//         this.templators = templators;
//     }
// }
// 
// Path: src/main/java/moulder/moulds/helpers/JsoupHelper.java
// public class JsoupHelper {
// 
//     public static Element copy(Element e) {
//         Element res = new Element(Tag.valueOf(e.tagName()), e.baseUri());
//         for (Attribute a: e.attributes()) {
//             res.attr(a.getKey(), a.getValue());
//         }
//         res.html(e.html());
//         return res;
//     }
// }
// 
// Path: src/main/java/moulder/moulds/helpers/MouldersApplier.java
// public class MouldersApplier {
//     public static Collection<Node> applyMoulder(Moulder moulder, Collection<Node> nodes) {
//         List<Node> res = new ArrayList<Node>();
//         for(Node node: nodes) {
//             if(node instanceof Element) {
//                 res.addAll(moulder.process((Element) node));
//             } else {
//                 res.add(node);
//             }
//         }
//         return res;
//     }
//     public static Collection<Node> applyMoulders(Collection<Moulder> moulders, Collection<Node> nodes) {
//         if(moulders.isEmpty()) {
//             return nodes;
//         } else {
//             Collection<Node> workingSet = new ArrayList<Node>(nodes);
//             for(Moulder moulder: moulders) {
//                 workingSet = applyMoulder(moulder, workingSet);
//             }
//             return workingSet;
//         }
//     }
// }


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import moulder.Moulder;
import moulder.MoulderChain;
import moulder.Registry;
import moulder.Registry.TemplatorConfig;
import moulder.moulds.helpers.JsoupHelper;
import moulder.moulds.helpers.MouldersApplier;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.select.Elements;
package moulder.moulds;




/**
 * A moulder that can be configured to select children of its input element an
 * apply other moulders on them
 *
 * @author jawher
 */
public class SubMoulder implements Moulder {

    private final Registry registry = new Registry();

    /**
     * Registers a number of moulders to be applied to its input element's
     * children returned by the supplied selector
     *
     * @param selector to selected the input element's children to be processed
     * @param moulders the moulders to apply on the selected children
     * @return its self, so that calls to register can be chained
     */
    public SubMoulder register(String selector, Moulder... moulders) {
        return register(selector, Arrays.asList(moulders));
    }

    /**
     * Registers a number of moulders to be applied to its input element's
     * children returned by the supplied selector
     *
     * @param selector   to selected the input element's children to be processed
     * @param templators the moulders to apply on the selected children
     * @return its self, so that calls to register can be chained
     */
    public SubMoulder register(String selector, List<Moulder> templators) {
        registry.register(selector, templators);
        return this;
    }

    public MoulderChain select(String selector) {
        List<Moulder> moulders = new ArrayList<Moulder>();
        register(selector, moulders);
        return new MoulderChain(moulders);
    }

    public List<Node> process(Element element) {
        final Document doc = new Document(element.baseUri());
        final Element copy = JsoupHelper.copy(element);
        doc.appendChild(copy);

        for (TemplatorConfig c : registry.getConfig()) {
            Elements elements = copy.select(c.selector);
            for (Element e : elements) {
                Collection<Node> oes = MouldersApplier.applyMoulders(c.templators, Arrays.<Node>asList(e));
                // replace e with oes
                for (Node oe : oes) {
                    e.before(oe.outerHtml());
                }

// Path: src/main/java/moulder/Moulder.java
// public interface Moulder {
//     /**
//      * This is where a moulder can process the input element to generate its
//      * result.
//      *
//      * @param element the element and its associated data to process
//      * @return the list of nodes and their associated data that were generated
//      *         after processing the input element.
//      */
//     List<Node> process(Element element);
// }
// 
// Path: src/main/java/moulder/MoulderChain.java
// public class MoulderChain {
//         private final List<Moulder> moulders;
// 
//         public MoulderChain(List<Moulder> moulders) {
//             this.moulders = moulders;
//         }
// 
//         public MoulderChain add(Moulder... moulders) {
//             return add(Arrays.asList(moulders));
//         }
// 
//         public MoulderChain add(List<Moulder> moulders) {
//             this.moulders.addAll(moulders);
//             return null;
//         }
// 
// }
// 
// Path: src/main/java/moulder/Registry.java
// public class Registry {
// 
//     public static final class TemplatorConfig {
//         public final String selector;
//         public final List<Moulder> templators;
// 
//         public TemplatorConfig(String selector, List<Moulder> templators) {
//             this.selector = selector;
//             this.templators = templators;
//         }
//     }
// 
//     private final List<TemplatorConfig> cfg = new ArrayList<TemplatorConfig>();
// 
//     /**
//      * Registers a number of moulders to be applied to its input element's
//      * children returned by the supplied selector
//      *
//      * @param selector
//      *            to selected the input element's children to be processed
//      * @param templators
//      *            the moulders to apply on the selected children
//      */
//     public void register(String selector, List<Moulder> templators) {
//         cfg.add(new TemplatorConfig(selector, templators));
//     }
// 
//     /**
//      * Return the registered selector/moulders pairs
//      *
//      * @return
//      */
//     public List<TemplatorConfig> getConfig() {
//         return cfg;
//     }
// }
// 
// Path: src/main/java/moulder/Registry.java
// public static final class TemplatorConfig {
//     public final String selector;
//     public final List<Moulder> templators;
// 
//     public TemplatorConfig(String selector, List<Moulder> templators) {
//         this.selector = selector;
//         this.templators = templators;
//     }
// }
// 
// Path: src/main/java/moulder/moulds/helpers/JsoupHelper.java
// public class JsoupHelper {
// 
//     public static Element copy(Element e) {
//         Element res = new Element(Tag.valueOf(e.tagName()), e.baseUri());
//         for (Attribute a: e.attributes()) {
//             res.attr(a.getKey(), a.getValue());
//         }
//         res.html(e.html());
//         return res;
//     }
// }
// 
// Path: src/main/java/moulder/moulds/helpers/MouldersApplier.java
// public class MouldersApplier {
//     public static Collection<Node> applyMoulder(Moulder moulder, Collection<Node> nodes) {
//         List<Node> res = new ArrayList<Node>();
//         for(Node node: nodes) {
//             if(node instanceof Element) {
//                 res.addAll(moulder.process((Element) node));
//             } else {
//                 res.add(node);
//             }
//         }
//         return res;
//     }
//     public static Collection<Node> applyMoulders(Collection<Moulder> moulders, Collection<Node> nodes) {
//         if(moulders.isEmpty()) {
//             return nodes;
//         } else {
//             Collection<Node> workingSet = new ArrayList<Node>(nodes);
//             for(Moulder moulder: moulders) {
//                 workingSet = applyMoulder(moulder, workingSet);
//             }
//             return workingSet;
//         }
//     }
// }


// Path: src/main/java/moulder/moulds/SubMoulder.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import moulder.Moulder;
import moulder.MoulderChain;
import moulder.Registry;
import moulder.Registry.TemplatorConfig;
import moulder.moulds.helpers.JsoupHelper;
import moulder.moulds.helpers.MouldersApplier;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.select.Elements;

package moulder.moulds;




/**
 * A moulder that can be configured to select children of its input element an
 * apply other moulders on them
 *
 * @author jawher
 */
public class SubMoulder implements Moulder {

    private final Registry registry = new Registry();

    /**
     * Registers a number of moulders to be applied to its input element's
     * children returned by the supplied selector
     *
     * @param selector to selected the input element's children to be processed
     * @param moulders the moulders to apply on the selected children
     * @return its self, so that calls to register can be chained
     */
    public SubMoulder register(String selector, Moulder... moulders) {
        return register(selector, Arrays.asList(moulders));
    }

    /**
     * Registers a number of moulders to be applied to its input element's
     * children returned by the supplied selector
     *
     * @param selector   to selected the input element's children to be processed
     * @param templators the moulders to apply on the selected children
     * @return its self, so that calls to register can be chained
     */
    public SubMoulder register(String selector, List<Moulder> templators) {
        registry.register(selector, templators);
        return this;
    }

    public MoulderChain select(String selector) {
        List<Moulder> moulders = new ArrayList<Moulder>();
        register(selector, moulders);
        return new MoulderChain(moulders);
    }

    public List<Node> process(Element element) {
        final Document doc = new Document(element.baseUri());
        final Element copy = JsoupHelper.copy(element);
        doc.appendChild(copy);

        for (TemplatorConfig c : registry.getConfig()) {
            Elements elements = copy.select(c.selector);
            for (Element e : elements) {
                Collection<Node> oes = MouldersApplier.applyMoulders(c.templators, Arrays.<Node>asList(e));
                // replace e with oes
                for (Node oe : oes) {
                    e.before(oe.outerHtml());
                }

                e.remove();