java/ch/wlkl/wsm/FormatFactory.java

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ch.wlkl.wsm;

import ch.wlkl.env.A2S;
import ch.wlkl.env.All2S;
import static ch.wlkl.env.Env.*;
import ch.wlkl.wsm.FormatFactory.Fmt.FctNst;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static ch.wlkl.wsm.Formatter.toCode;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author walter
 */
public class FormatFactory {

    public final HashMap<Object, Format> k2f = new HashMap();

    public Format fmt(String g, Format... e) {
        return new Fmt(null, g, e);
    }

    public Format fmt(Object b, String g, Format... e) {
        return b == null ? new Fmt(b, g, e) : add(b, new Fmt(b, g, e));
    }

    public Format fmt(Object k, Object f, String g, Format... e) {
        Fmt n = new Fmt(f, g, e);
        return k == null ? n : add(k, n);
    }

    public Format chc(String g, Format... e) {
        return new Fmt.Chc(g, e);
    }

    public Format chc(Object b, String g, Format... e) {
        return b == null ? new Fmt.Chc(g, e) : add(b, new Fmt.Chc(g, e));
    }

    public Format fct(Object k, Object f, String g) {
        return k == null ? new Fmt.Fct(this, f, g) : add(k, new Fmt.Fct(this, f, g));
    }

     public Format fct(Object f, String g) {
        return fct(f, f, g);
    }

     public Format fctNst(Object k, String g) {
        return k == null ? new FctNst(this, null, g) : add(k, new FctNst(this, null, g));
    }
   /**
     * return Format at key k, create new placeholder if missing
     * @param k
     * @return
     */
    public Format at(Object k) {
        Format r = k2f.get(k);
        if (r == null) {
            r = new Fmt();
            k2f.put(k, r);
        }
        return r;
    }

     /**
     * return Format at key k, fail if missing
     * @param k
     * @return
     */
   public Format atKey(Object k) {
        Format r = k2f.get(k);
        if (r == null) {
            dy("key not found " + k);
        }
        return r;
    }

    /**
     * return Format for Formattable f, fail if missing
     * @param k
     * @return
     */
    public Format atFor(Formattable f) {
        return atKey(f.getClass());
    }

    public Format add(Object k, Format f) {
        final Fmt o;
        assert k != null;
        if (!k2f.containsKey(k)) {
            k2f.put(k, f);
        } else if (!(k2f.get(k) instanceof Fmt) || (o = (Fmt) k2f.get(k)).code != null) {
            dy("key " + k + " already set");
        } else {
            k2f.put(k, o.ele[0] = (Fmt) f);
        }
        return f;
    }
    
    public Format add(Format f) {
        return add(((Fmt)f).chcKey, f);
    } 

    public void clear() {
        k2f.clear();
    }
    
    /**
     * replace all placeholders by its final format, fail on unresolved placeholders
     */
    public void finish() {
        Set<Format> done = new HashSet();
        for (Map.Entry<Object, Format> e : k2f.entrySet()) {
            assert e.getValue() instanceof Fmt && ((Fmt) e.getValue()).code != null : assFail("unresolved format", e);
            ((Fmt) e.getValue()).finish(done);
        }
    }

    static class Fmt implements Format, A2S {

        final Object chcKey;
        final String[][] code;
        final Fmt[] ele;

        public Fmt(Object ck, String g, Format... e) {
            chcKey = ck;
            code = toCode(g, e.length);
            if (e == null || e.length == 0) {
                ele = null;
            } else {
                ele = new Fmt[e.length + 1];
                System.arraycopy(e, 0, ele, 1, e.length);
            }
        }

        protected Fmt() {
            chcKey = null;
            code = null;
            ele = new Fmt[1];
        }

        @Override
        public String[][] code() {
            return code;
        }

        @Override
        /* format: if i=0 format e globally else format subElement with index i */
        public int format(Formatter f, int i, Formattable e) {
            if (i == 0) {
                e.format(f);
            } else if (i >= 1 && i < ele.length && ele[i] != null) {
                f.format(ele[i], e);
            } else {
                assert false: assFail("bad i " + i, this, ", ele=", e);
            }
            return -1;
        }

        public void mergeLex(String lex) {
            Formatter.mergeLex(lex, 0, code);
        }

        public void mergeSyn(String syn) {
            String [][] n = Formatter.toCode(syn, ele.length - 1);
            assert(n.length == code.length);
            System.arraycopy(n, 0, code, 0, n.length);
        }
 
        private void finish(Set<Format> done) {
            if (ele == null || done.contains(this)) {
                return;
            }
            done.add(this);
            for (int i = 0; i < ele.length; i++) {
                if (ele[i] instanceof Fmt) {
                    Fmt e = (Fmt) ele[i];
                    if (e.code == null) {
                        assert e.ele[0] != null : assFail("ele[" + i + "] not resolved", this);
                        ele[i] = e.ele[0];
                    }
                    e.finish(done);
                }
            }
        }

        @Override
        public void a2s(All2S a) {
            a.a2s("chcKey=", chcKey, ", code=", code, ", ele=", ele);
        }

        static class Chc extends Fmt {

            public Chc(String g, Format... e) {
                super(e.length == 1 ? ((Fmt) e[0]).chcKey : null, g, e);
            }

            @Override
            /* format e, if i=0 first applicable choice else chc index i
        *   return next choice or -1    
             */
            public int format(Formatter f, int i, Formattable e) {
                if (i == 0) {
                    for (i = 1; i < ele.length && ele[i] != null && ele[i].chcKey != null && ele[i].chcKey != e.getClass(); i++) {
                    }
                }
                assert i >= 1 && i < ele.length && ele[i] != null && (ele[i].chcKey == null || ele[i].chcKey == e.getClass()) : assFail("bad i " + i, "this=", this, ", ele=", ele);
                f.format(ele[i], e);
                int j;
                for (j = i + 1; j < ele.length && ele[j] != null && ele[j].chcKey != e.getClass(); j++) {
                }
                return j < ele.length ? j : -1;
            }
        }

        static class Fct extends Fmt {

            final FormatFactory fact;

            public Fct(FormatFactory f, Object ck, String g) {
                super(ck, g);
                fact = f;
            }
            
            @Override
            /* format e, if i=0 first applicable choice else chc index i
        *   return next choice or -1    
             */
            public int format(Formatter f, int i, Formattable e) {
                if (i == 0) {
                    e.format(f);
                } else if (i > 0) {
                    f.format(fact.atFor(e), e);
                } else {
                    dy("i=" + i);
                }
                return -1;
            }
        }
        static class FctNst extends Fmt {

            final FormatFactory fact;

            public FctNst(FormatFactory f, Object ck, String g) {
                super(ck, g);
                fact = f;
            }
            
            @Override
            /* format e, if i=0 first applicable choice else chc index i
        *   return next choice or -1    
             */
            public int format(Formatter f, int i, Formattable e) {
                assert i == 0;
                f.format(fact.atFor(e), e);
                return -1;
            }
        }
    }
}