Package com.nexirius.framework.htmlview

Source Code of com.nexirius.framework.htmlview.HTMLResolver$InObserver

//{HEADER
/**
* This class is part of jnex 'Nexirius Application Framework for Java'
* Copyright (C) Nexirius GmbH, CH-4450 Sissach, Switzerland (www.nexirius.ch)
*
* <p>This library is free software; you can redistribute it and/or<br>
* modify it under the terms of the GNU Lesser General Public<br>
* License as published by the Free Software Foundation; either<br>
* version 2.1 of the License, or (at your option) any later version.</p>
*
* <p>This library is distributed in the hope that it will be useful,<br>
* but WITHOUT ANY WARRANTY; without even the implied warranty of<br>
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU<br>
* Lesser General Public License for more details.</p>
*
* <p>You should have received a copy of the GNU Lesser General Public<br>
* License along with this library; if not, write to the Free Software<br>
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA</p>
* </blockquote>
*
* <p>
* Nexirius GmbH, hereby disclaims all copyright interest in<br>
* the library jnex' 'Nexirius Application Framework for Java' written<br>
* by Marcel Baumann.</p>
*/
//}HEADER
package com.nexirius.framework.htmlview;

import com.nexirius.framework.FWLog;
import com.nexirius.framework.datamodel.DataModel;
import com.nexirius.framework.htmlview.function.*;
import com.nexirius.util.Semaphore;
import com.nexirius.util.StringVector;
import com.nexirius.util.VariableResolver;
import com.nexirius.util.locale.LocaleManager;
import com.nexirius.util.locale.LocaleManagerImpl;
import com.nexirius.util.locale.LocaleEvent;

import java.io.*;
import java.util.Enumeration;
import java.util.Stack;
import java.util.Locale;

/**
* This class translates a HTML file which contains special functions $!function() and variables like $(VARIABLE) into a real HTML file
* by parsing it over and over again until all special tags and variables are replaced
*/
public class HTMLResolver implements Serializable {

    VariableStore variableStore = new VariableStore();
    HTMLStreamMap streamMapper = new HTMLStreamMap();
    HTMLTemplateMap templateMap = new HTMLTemplateMap();
    HTMLFunctionMap functionMap = new HTMLFunctionMap();
    HTMLTranslator translator = new NullHTMLTranslator();
    LocaleManager localeManager = new LocaleManagerImpl();
    Stack templateStack = new Stack();
    DataModel rootModel = null;
    OutputStream observerStream = null; // just for debug!

    public HTMLResolver() {
        init();
    }

    private void init() {
        registerHTMLFunction(new FieldHTMLFunction());
        registerHTMLFunction(new IncludeHTMLFunction());
        registerHTMLFunction(new IncludeBodyHTMLFunction());
        registerHTMLFunction(new ArrayHTMLFunction());
        registerHTMLFunction(new ButtonHTMLFunction());
        registerHTMLFunction(new BackButtonHTMLFunction());
        registerHTMLFunction(new PopupHTMLFunction());
        registerHTMLFunction(new EventHTMLFunction());
        registerHTMLFunction(new IconEventHTMLFunction());
        registerHTMLFunction(new DefineHTMLFunction());
        registerHTMLFunction(new FlagHTMLFunction());
        registerHTMLFunction(new TranslateHTMLFunction());
        registerHTMLFunction(new SelectHTMLFunction());
        registerHTMLFunction(new ColorHTMLFunction());
        registerHTMLFunction(new DebugHTMLFunction());
        registerHTMLFunction(new PasswordFieldHTMLFunction());
        registerHTMLFunction(new FormStartHTMLFunction());
        registerHTMLFunction(new FormEndHTMLFunction());
        registerHTMLFunction(new EncodeUrlHTMLFunction());
        registerHTMLFunction(new TooltipHTMLFunction());
        registerHTMLFunction(new GetChildTextHTMLFunction());
    }

    /**
     * use this method to attach an observer before the recursive stream reader
     *
     * @param out use e.g. System.out
     */
    public void setObserverStream(OutputStream out) {
        observerStream = out;
    }

    /**
     * access all nested templates into a string
     *
     * @return
     */
    public String getTemplateStackAsString() {
        StringBuffer ret = new StringBuffer(" in ");
        Enumeration e = templateStack.elements();
        Stack reverse = new Stack();

        while (e.hasMoreElements()) {
            reverse.push(e.nextElement());
        }

        while (!reverse.empty()) {
            ret.append("'" + reverse.pop() + "'");

            if (!reverse.empty()) {
                ret.append(" accessed by ");
            }
        }

        return ret.toString();
    }

    /**
     * Return the model which belongs to the actual state
     * @return
     */
    public DataModel getRootModel() {
        return rootModel;
    }

    /**
     * Access all nested data models into a string. Walks up the parents of the
     * given model until the actual root model (or null) is reached.
     *
     * @param model the bottom child from which it starts
     * @return
     */
    public String getModelStackAsString(DataModel model) {
        if (rootModel == null) {
            return "";
        }
        return rootModel.getChildName(model);
    }

    /**
     * set the variable accessor (which translates variables into strings)
     */
    public void setVariableStore(VariableStore va) {
        variableStore = va;
    }

    public VariableStore getVariableStore() {
        return variableStore;
    }

    /**
     * get the HTMLTranslatorImpl
     */
    public HTMLTranslator getHTMLTranslator() {
        return translator;
    }

    /**
     * By default a HTMLTranslatorImpl instance is already registered with this class.
     *
     * @param translator
     */
    public void setTranslator(HTMLTranslator translator) {
        this.translator = translator;
        localeManager.addLocaleListener(translator);
    }

    public void setLocale(Locale locale) {
        localeManager.setLocale(locale);
    }

    public Locale getLocale() {
        return localeManager.getLocale();
    }

    /**
     * translates all variables $(variable) from an inputstream into an outputstream and returns the number of
     * translated variables
     *
     * @param in
     * @param out
     * @return
     */
    public int resolveVariables(InputStream in, OutputStream out)
            throws IOException {
        return VariableResolver.translate(variableStore, in, out);
    }

    /**
     * set the HTML stream mapper (which translates templates into input streams)
     */
    public void setStreamMapper(HTMLStreamMap sm) {
        streamMapper = sm;
    }

    /**
     * set the mapper which translates data model types into templates
     */
    public void setTemplateMap(HTMLTemplateMap tm) {
        templateMap = tm;
    }

    /**
     * access the registered HTMLTranslatorImpl to translate the given string
     *
     * @param s
     * @return the translation or s if no translation found
     */
    public String translate(String s) {
        if (translator == null) {

            return s;
        }
        return translator.translate(s);
    }

    /**
     * access the template which is associated to a specific data model
     *
     * @param model    the data model for which a template is searched
     * @param isEditor there can be two templates registered for each model (one for edit and one foe view)
     * @return
     */
    public String getTemplateFor(DataModel model, boolean isEditor) {
        return templateMap.getTemplateFor(model, isEditor);
    }

    /**
     * The root model is used to translate any child model into a field name.
     * This method is only used to complete child names into the variable $(FULLNAME)
     *
     * @param model
     */
    public void setRootModel(DataModel model) {
        rootModel = model;
    }

    /**
     * translates a model (and eventually a template) into a byte array
     *
     * @param sessionVariable
     * @param model    the model which is the actual parent model
     * @param template the template which is used to access an input stream. A null template is translated into a template by using HTMLTemplateMap
     * @param isEditor the general type of the resulting HTML dialog (view or edit)
     * @return the output stream packed into a byte array
     * @throws IOException
     * @throws Exception
     */
    public byte[] resolve(HTMLSessionVariable sessionVariable, DataModel model, String template, boolean isEditor)
            throws IOException, Exception {
        if (model != null) {
            FWLog.debug("resolve(" + model.getFieldName() + ", " + template + ", " + isEditor + ")");
        }

        if (template == null) {
            if (model == null) {
                return "No template found for null model".getBytes();
            }

            template = getTemplateFor(model, isEditor);

            FWLog.debug("Using template -> " + template);
        }

        if (template != null) {
            templateStack.push(template);
        }

        byte ret[] = resolve(sessionVariable, model, streamMapper.getInputStreamFor(model, template, isEditor), isEditor);

        if (template != null) {
            templateStack.pop();
        }

        return ret;
    }

    /**
     * This method is called after the template has been translated into an input stream.
     * It resolves the embedded variables $(variable) first and then the embedded functions !$function().
     * It continues recursively until all variables and functions are resolved (this can lead into
     * an infinite recursion which results in a stack overflow or out of memory exception)
     *
     * @param sessionVariable
     * @param model    the actual parent data model
     * @param in       the actual input stream
     * @param isEditor the general type of the resulting HTML dialog (view or edit)
     * @return the output stream packed into a byte array
     * @throws Exception
     */
    public byte[] resolve(HTMLSessionVariable sessionVariable, DataModel model, InputStream in, boolean isEditor)
            throws Exception {
        Semaphore semaphore = new Semaphore();
        PipedInputStream in1 = new PipedInputStream();
        PipedOutputStream out1 = new PipedOutputStream(in1);
        PipedInputStream in2 = new PipedInputStream();
        PipedOutputStream out2 = new PipedOutputStream(in2);
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        VariableStore oldValues = null;

        PushbackInputStream inParser = null;

        if (observerStream == null) {
            inParser = new PushbackInputStream(in2);
        } else {
            inParser = new PushbackInputStream(new InObserver(in2));
        }

        if (model != null) {
            oldValues = new VariableStore();

            variableStore.setVariable(VariableStore.FULLNAME, getModelStackAsString(model));
            DataModelVariableHandler.setVariablesFor(model, variableStore, oldValues, translator);
        }

// replace variables first
        new PrecompilerThread(in, out1);
        VariableResolverThread vr = new VariableResolverThread(new PushbackInputStream(in1), out2);
        HTMLParserThread parser = new HTMLParserThread(sessionVariable, model, isEditor, inParser, out, semaphore);

        semaphore.waitFor();

        if (vr.getException() != null) {

            throw vr.getException();
        }

        if (parser.getException() != null) {

            throw parser.getException();
        }

        if ((vr.getNumberOfTranslations() + parser.getNumberOfTranslations()) != 0) {
// recurse as long as translations are done
            return resolve(sessionVariable, model, new ByteArrayInputStream(out.toByteArray()), isEditor);
        }

        if (model != null) {
            variableStore.set(oldValues);
        }

        return out.toByteArray();
    }

    /**
     * this class is only used for debugging
     *
     * @author isc-mb
     */
    class InObserver extends InputStream {
        InputStream in;

        public InObserver(InputStream in) {
            this.in = in;

            try {
                observerStream.write("\nInObserver\n".getBytes());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public int read() throws IOException {
            int c = in.read();

            if (c >= 0) {
                observerStream.write((byte) c);
                observerStream.flush();
            }
            return c;
        }
    }

    /**
     * translates a simple template into a byte array. calls: resolve(null, template, false)
     *
     * @param sessionVariable
     * @param template
     * @return
     * @throws IOException
     * @throws Exception
     */
    public byte[] resolve(HTMLSessionVariable sessionVariable, String template)
            throws IOException, Exception {
        return resolve(sessionVariable, null, template, false);
    }

    /**
     * this class is used to handle $/* and $* / comments
     */
    class PrecompilerThread extends Thread {
        int numberOfTranslations = 0;
        InputStream in;
        OutputStream out;
        Exception exception = null;

        PrecompilerThread(InputStream input, OutputStream output) {
            in = input;
            out = output;
            start();
        }

        public Exception getException() {
            return exception;
        }

        public void run() {
            try {
                int state = 0;
                while (true) {
                    int c = in.read();

                    if (c < 0) {
                        break;
                    }

                    switch (state) {
                        case 0:
                            if (c == '$') {
                                state = 1;
                            } else {
                                out.write(c);
                                state = 0;
                            }
                            break;

                        case 1: // after $
                            if (c == '/') {
                                state = 2;
                            } else {
                                out.write('$');
                                out.write(c);
                                state = 0;
                            }
                            break;

                        case 2: // after /
                            if (c == '*') {
                                state = 3;
                            } else {
                                out.write('$');
                                out.write('/');
                                out.write(c);
                                state = 0;
                            }
                            break;

                        case 3: // inside comment
                            if (c == '$') {
                                state = 4;
                            }
                            break;

                        case 4: // after $
                            if (c == '*') {
                                state = 5;
                            } else {
                                state = 3;
                            }
                            break;

                        case 5: // after *
                            if (c == '/') {
                                state = 0;
                            } else {
                                state = 3;
                            }
                            break;
                    }
                }
            } catch (IOException e) {
                exception = e;
            }
            try {
                in.close();
                out.close();
            } catch (IOException e) {
                exception = e;
            }
        }

        public int getNumberOfTranslations() {
            return numberOfTranslations;
        }
    }

    /**
     * this class is used to handle variable translations in a seperate thread
     */
    class VariableResolverThread extends Thread {
        int numberOfTranslations = 0;
        InputStream in;
        OutputStream out;
        Exception exception = null;

        VariableResolverThread(InputStream input, OutputStream output) {
            in = input;
            out = output;
            start();
        }

        public Exception getException() {
            return exception;
        }

        public void run() {
            try {
                numberOfTranslations = VariableResolver.translate(variableStore, in, out);
            } catch (IOException e) {
                exception = e;
            }
            try {
                in.close();
                out.close();
            } catch (IOException e) {
                exception = e;
            }
        }

        public int getNumberOfTranslations() {
            return numberOfTranslations;
        }
    }

    /**
     * this class is used to handle embedded function translation in a seperate thread
     */
    class HTMLParserThread extends Thread {
        HTMLSessionVariable sessionVariable;
        int numberOfTranslations = 0;
        DataModel model;
        boolean isEditor;
        PushbackInputStream in;
        OutputStream out;
        Semaphore semaphore;
        Exception exception = null;

        HTMLParserThread(HTMLSessionVariable sessionVariable, DataModel model, boolean isEditor, PushbackInputStream input, OutputStream output, Semaphore s) {
            this.sessionVariable = sessionVariable;
            semaphore = s;
            try {
                semaphore.reserve();
            } catch (Exception ex) {
// should never happen
                ex.printStackTrace();
            }

            this.model = model;
            this.isEditor = isEditor;
            in = input;
            out = output;
            start();
        }

        public Exception getException() {
            return exception;
        }

        public void run() {
            try {
                HTMLParser parser = new HTMLParser(model, isEditor, HTMLResolver.this, out);

                parser.parse(sessionVariable, in);

                numberOfTranslations = parser.getNumberOfTranslations();
            } catch (Exception e) {
                exception = e;
            }

            try {
                in.close();
                out.close();
            } catch (IOException e) {
                exception = e;
            }

            semaphore.release();
        }

        public int getNumberOfTranslations() {
            return numberOfTranslations;
        }
    }

    /**
     * Access the actual HTMLStreamMap
     */
    public HTMLStreamMap getStreamMapper() {
        return streamMapper;
    }

    /**
     * Access the actual HTMLTemplateMap
     */
    public HTMLTemplateMap getTemplateMap() {
        return templateMap;
    }

    public void registerHTMLFunction(HTMLFunction function) {
        functionMap.put(function);
    }

    public HTMLFunction getFunction(String functionName) {
        return functionMap.get(functionName);
    }

    class NullHTMLTranslator implements HTMLTranslator {
        public String translate(String s) {
            return s;
        }

        public void localeChanged(LocaleEvent event) {

        }
    }
}
TOP

Related Classes of com.nexirius.framework.htmlview.HTMLResolver$InObserver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.