<commit_before>/*
 * Copyright (c) 2015 Ziver
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package zutil.parser;

import zutil.struct.MutableInt;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Class for generating dynamic text/code from set data.
 * The syntax is similar to the javascript mustache library.
 *
 * <br /><br />
 * Supported tags:
 * <ul>
 *  <li><b> {{key}} </b><br>
 *      will be replaced with the string from the key</li>
 *  <li><b> {{#key}}...{{/key}} </b><br>
 *      will display content between the tags if key is defined</li>
 * </ul>
 *
 * @author <NAME>
 */
public class Templator {
    private HashMap<String,Object> data;
    private TemplateEntity tmplRoot;

    public Templator(String tmpl){
        this.data = new HashMap<String, Object>();
        parseTemplate(tmpl);
    }

    public void setData(String key, Object data){
        this.data.put(key, data);
    }

    /**
     * Will clear all data attributes
     */
    public void clear(){
        data.clear();
    }

    public String compile(){
        StringBuilder str = new StringBuilder();
        if(tmplRoot != null)
            tmplRoot.compile(str);
        return str.toString();
    }

    /**
     * Will pare or re-parse the source template.
     */
    private void parseTemplate(String tmpl){
        TemplateNode node = new TemplateNode();
        parseTemplate(node, tmpl, new MutableInt(), null);
        tmplRoot = node;
    }
    private void parseTemplate(TemplateNode root, String tmpl, MutableInt m, String parentTag){
        StringBuilder data = new StringBuilder();
        StringBuilder tags = new StringBuilder();
        boolean tagOpen = false;

        for(; m.i<tmpl.length(); ++m.i){
            char c = tmpl.charAt(m.i);
            String d = ""+ c + (m.i+1<tmpl.length() ? tmpl.charAt(m.i+1) : ' ');
            switch( d ){
                case "{{":
                    root.addEntity(new TmplStaticString(data.toString()));
                    data.delete(0, data.length());
                    tags.delete(0, data.length());
                    tags.append("{{");
                    tagOpen = true;
                    ++m.i;
                    break;
                case "}}":
                    if(!tagOpen){ // Tag not opened, incorrect enclosure
                        data.append(c);
                        continue;
                    }
                    tagOpen = false;
                    ++m.i;
                    String tagName = data.toString();
                    data.delete(0, data.length());
                    switch(tagName.charAt(0)) {
                        case '#':
                            TemplateCondition conTmpl = new TemplateCondition(tagName);
                            parseTemplate(conTmpl, tmpl, m, tagName.substring(1));
                            root.addEntity(conTmpl);
                            break;
                        case '/':
                            if(tagName.endsWith(parentTag))
                                return;
                        default:
                            root.addEntity(new TmplDataAttribute(tagName));
                    }
                    break;
                default:
                    data.append(c);
                    break;
            }
        }
        if(tagOpen)
            data.insert(0, tags);
        if(data.length() > 0)
            root.addEntity(new TmplStaticString(data.toString()));
    }


    /**************************** Template Helper Classes *************************************/

    protected interface TemplateEntity {
        public void compile(StringBuilder str);
    }

    protected class TemplateNode implements TemplateEntity {
        private List<TemplateEntity> entities;

        public TemplateNode(){
            this.entities = new ArrayList<TemplateEntity>();
        }

        public void addEntity(TemplateEntity s){
            entities.add(s);
        }

        public void compile(StringBuilder str) {
            for(TemplateEntity sec : entities)
                sec.compile(str);
        }
    }

    protected class TemplateCondition extends TemplateNode {
        private String key;

        public TemplateCondition(String key){
            this.key = key;
        }

        public void compile(StringBuilder str) {
            if(data.containsKey(key))
                super.compile(str);
        }
    }

    protected class TmplStaticString implements TemplateEntity {
        private String text;

        public TmplStaticString(String text){
            this.text = text;
        }

        public void compile(StringBuilder str) {
            str.append(text);<commit_msg>Conditional template working now<commit_after>/*
 * Copyright (c) 2015 Ziver
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package zutil.parser;

import zutil.log.LogUtil;
import zutil.struct.MutableInt;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Logger;

/**
 * Class for generating dynamic text/code from set data.
 * The syntax is similar to the javascript mustache library.
 *
 * <br /><br />
 * Supported tags:
 * <ul>
 *  <li><b> {{key}} </b><br>
 *      will be replaced with the string from the key</li>
 *  <li><b> {{#key}}...{{/key}} </b><br>
 *      will display content between the tags if key is defined</li>
 * </ul>
 *
 * @author <NAME>
 */
public class Templator {
    private static final Logger log = LogUtil.getLogger();
    private HashMap<String,Object> data;
    private TemplateEntity tmplRoot;

    public Templator(String tmpl){
        this.data = new HashMap<String, Object>();
        parseTemplate(tmpl);
    }

    public void setData(String key, Object data){
        this.data.put(key, data);
    }

    /**
     * Will clear all data attributes
     */
    public void clear(){
        data.clear();
    }

    public String compile(){
        StringBuilder str = new StringBuilder();
        if(tmplRoot != null)
            tmplRoot.compile(str);
        return str.toString();
    }

    /**
     * Will pare or re-parse the source template.
     */
    private void parseTemplate(String tmpl){
        tmplRoot = parseTemplate(new TemplateNode(), tmpl, new MutableInt(), null);
    }
    private TemplateNode parseTemplate(TemplateNode root, String tmpl, MutableInt m, String parentTag){
        StringBuilder data = new StringBuilder();
        StringBuilder tag = new StringBuilder();
        boolean tagOpen = false;

        for(; m.i<tmpl.length(); ++m.i){
            char c = tmpl.charAt(m.i);
            String d = ""+ c + (m.i+1<tmpl.length() ? tmpl.charAt(m.i+1) : ' ');
            switch( d ){
                case "{{":
                    root.add(new TmplStaticString(data.toString()));
                    data.delete(0, data.length());
                    tag.delete(0, data.length());
                    tag.append("{{");
                    tagOpen = true;
                    ++m.i;
                    break;
                case "}}":
                    if(!tagOpen){ // Tag not opened, incorrect enclosure
                        data.append(c);
                        continue;
                    }
                    tagOpen = false;
                    ++m.i;
                    String tagName = data.toString();
                    data.delete(0, data.length());
                    tag.append(tagName).append("}}");
                    switch(tagName.charAt(0)) {
                        case '#':
                            ++m.i;
                            root.add(parseTemplate(new TemplateCondition(tagName),
                                            tmpl, m, tagName));
                            break;
                        case '/':
                            if(parentTag != null && tagName.endsWith(parentTag.substring(1)))
                                return root;
                            log.severe("Closing non-opened tag: "+tag.toString());
                            root.add(new TmplStaticString(tag.toString()));
                            break;
                        default:
                            root.add(new TmplDataAttribute(tagName));
                    }
                    break;
                default:
                    if(Character.isWhitespace(c)) // Not a valid tag if it contains whitespace
                        tagOpen = false;
                    data.append(c);
                    break;
            }
        }
        if(tagOpen)
            data.insert(0, tag);
        if(data.length() > 0)
            root.add(new TmplStaticString(data.toString()));

        // If we get to this point means that this node is incorrectly close
        // or this is the end of the file, so we convert it to a normal node
        if(parentTag != null) {
            root = new TemplateNode(root);
            String tagName = "{{"+parentTag+"}}";
            log.severe("Missing closure of tag: "+tagName);
            root.addFirst(new TmplStaticString(tagName));
        }
        return root;
    }


    /**************************** Template Helper Classes *************************************/

    protected interface TemplateEntity {
        public void compile(StringBuilder str);
    }

    protected class TemplateNode implements TemplateEntity {
        private List<TemplateEntity> entities;

        public TemplateNode(){
            this.entities = new ArrayList<TemplateEntity>();
        }
        public TemplateNode(TemplateNode node){
            this.entities = node.entities;
        }

        public void addFirst(TemplateEntity s){
            entities.add(0, s);
        }
        public void add(TemplateEntity s){
            entities.add(s);
        }

        public void compile(StringBuilder str) {
            for(TemplateEntity sec : entities)
                sec.compile(str);
        }
    }

    protected class TemplateCondition extends TemplateNode {
        private String key;

        public TemplateCondition(String key){
            this.key = key;
        }

        public void compile(StringBuilder str) {
            if(data.containsKey(key))
                super.compile(str);
        }
    }

    protected class TmplStaticString implements TemplateEntity {
        private String text;

        public TmplStaticString(String text){
            this.text = text;
        }

        public void compile(StringBuilder str) {
            str.append(text);
{'fraction_non_alphanumeric': 0.10617852604828462, 'fraction_numerical': 0.0021442185514612454, 'mean_word_length': 1.8034283170080143, 'pattern_counts': {'":': 4, '<': 45, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3605607', 'n_tokens_mistral': 3219, 'n_tokens_neox': 3058, 'n_words': 1004}