package js.http.encoder;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

import javax.servlet.http.HttpServletRequest;

import js.dom.Document;
import js.http.ContentType;
import js.io.StreamHandler;
import js.lang.BugError;
import js.lang.IllegalArgumentException;
import js.log.Log;
import js.log.LogFactory;

/**
 * Server encoders registry for invocation arguments readers and return value writers. This registry is a singleton. On its
 * construction loads readers and writers, both built-in and provided by service implementation of {@link HttpEncoderProvider}
 * interface.
 *

 * This registry has methods to retrieve reader and writer instances able to handle requested content and Java types, see
 * {@link #getArgumentsReader(HttpServletRequest, Type[])} and {@link #getValueWriter(ContentType)}. There is also a method to
 * determine content type appropriate to handle a requested object instance, see {@link #getContentTypeForValue(Object)}. This
 * method is a companion for return value writers.
 *
 * @author Iulian Rotaru
 * @version final
 */
public final class ServerEncoders implements ArgumentsReaderFactory, ValueWriterFactory {
	/** Class logger. */
	private static final Log log = LogFactory.getLog(ServerEncoders.class);

	/** Server encoders singleton. */
	private static final ServerEncoders instance = new ServerEncoders();

	/**
	 * Get server encoders instance.
	 *
	 * @return server encoders instance.
	 */
	public static ServerEncoders getInstance() {
		return instance;
	}

	/** Registered invocation arguments readers. */
	private final Map<EncoderKey, ArgumentsReader> readers = new HashMap<>();

	/** Registered return value writers. */
	private final Map<ContentType, ValueWriter> writers = new HashMap<>();

	/**
	 * Construct server encoders instance. Loads invocation arguments readers and return value writers, both built-in and
	 * provided by service implementation of {@link HttpEncoderProvider} interface.
	 */
	private ServerEncoders() {
		readers.put(null, new UrlQueryArgumentsReader());
		readers.put(new EncoderKey(ContentType.APPLICATION_JSON), new JsonArgumentsReader());
		readers.put(new EncoderKey(ContentType.APPLICATION_STREAM), new StreamArgumentsReader());
		readers.put(new EncoderKey(ContentType.MULTIPART_MIXED), new MultipartMixedArgumentsReader(this));
		readers.put(new EncoderKey(ContentType.TEXT_XML), new XmlArgumentsReader());
		readers.put(new EncoderKey(ContentType.MULTIPART_FORM), new MultipartFormArgumentsReader());
		readers.put(new EncoderKey(ContentType.URLENCODED_FORM), new FormUrlArgumentsReader());

		writers.put(ContentType.TEXT_XML, new XmlValueWriter());
		writers.put(ContentType.APPLICATION_STREAM, new StreamValueWriter());
		writers.put(ContentType.APPLICATION_JSON, new JsonValueWriter());

		for (HttpEncoderProvider encoderProvider : ServiceLoader.load(HttpEncoderProvider.class)) {
			for (Map.Entry<EncoderKey, ArgumentsReader> entry : encoderProvider.getArgumentsReaders().entrySet()) {
				log.debug("Register arguments reader |%s| for |%s|.", entry.getValue().getClass(), entry.getKey());
				readers.put(entry.getKey(), entry.getValue());
			}
			for (Map.Entry<ContentType, ValueWriter> entry : encoderProvider.getValueWriters().entrySet()) {
				log.debug("Register value writer |%s| for |%s|.", entry.getValue().getClass(), entry.getKey());
				writers.put(entry.getKey(), entry.getValue());
			}
		}
	}

	/**
	 * Get arguments reader able to process arguments from HTTP request accordingly expected argument types. If HTTP request has
	 * no content type returns {@link EncoderKey#APPLICATION_JSON} reader, that is, tiny container default content type is JSON.
	 * This departs from HTTP standard that mandates using bytes stream when no content type is provided on request.
	 *

	 * If formal parameters are empty returns {@link EmptyArgumentsReader}. If formal parameters are provided attempts to
	 * retrieve a reader for expected argument types. If none found try using content type solely.
	 *
	 * @param httpRequest HTTP request carrying arguments to read,
	 * @param formalParameters expected argument types, possible empty.
	 * @return arguments reader, never null.
	 * @throws IllegalArgumentException if no reader found for requested HTTP request and expected argument types.
	 */
	@Override
	public ArgumentsReader getArgumentsReader(HttpServletRequest httpRequest, Type[] formalParameters) {
		if (formalParameters.length == 0) {
			return EmptyArgumentsReader.getInstance();
		}
		if (httpRequest.getQueryString() != null) {
			return readers.get(null);
		}

		return getArgumentsReader(httpRequest.getContentType(), formalParameters[0]);
	}

	@Override
	public ArgumentPartReader getArgumentPartReader(String contentType, Type parameterType) {
		ArgumentsReader reader = getArgumentsReader(contentType, parameterType);
		if (!(reader instanceof ArgumentPartReader)) {
			throw new IllegalArgumentException("Invalid content type for mixed part. Content type |%s| not supported.", contentType);
		}
		return (ArgumentPartReader) reader;
	}

	/**
	 * Get arguments reader for requested content and parameter types. This factory method tries to retrieve a reader for
	 * expected parameter type. If none found it tries using content type solely.
	 *

	 * If content type is null this utility method returns {@link EncoderKey#APPLICATION_JSON} reader since tiny container
	 * default content type is JSON. This departs from HTTP standard that mandates using bytes stream when no content type is
	 * provided on request.
	 *
	 * @param contentType content type, possible null,
	 * @param parameterType parameter type.
	 * @return reader instance, never null.
	 * @throws IllegalArgumentException if no reader found for requested content and parameter types.
	 */
	private ArgumentsReader getArgumentsReader(String contentType, Type parameterType) {
		if (contentType == null) {
			return readers.get(EncoderKey.APPLICATION_JSON);
		}

		EncoderKey key = new EncoderKey(ContentType.valueOf(contentType), parameterType);
		ArgumentsReader reader = readers.get(key);
		if (reader != null) {
			return reader;
		}

		key = new EncoderKey(ContentType.valueOf(contentType));
		reader = readers.get(key);
		if (reader == null) {
			throw new IllegalArgumentException("Unsupported content type |%s|. There is no arguments reader registered for it.", key);
		}
		return reader;
	}

	/**
	 * Determine content type usable to retrieve a writer able to handle given object value. This method is used in tandem with
	 * {@link #getValueWriter(ContentType)}.
	 *

	 * There is a heuristic to determine content type based on object value class. If no suitable content type found returns
	 * {@link ContentType#APPLICATION_JSON}.
	 *
	 * @param value object value.
	 * @return content type for given object value.
	 */
	@Override
	public ContentType getContentTypeForValue(Object value) {
		if (value instanceof Document) {
			return ContentType.TEXT_XML;
		}
		if (value instanceof StreamHandler) {
			return ContentType.APPLICATION_STREAM;
		}
		return ContentType.APPLICATION_JSON;
	}

	/**
	 * Get return value writer able to handle requested content type. Content type argument should identify a registered value
	 * writer. If no writer found throws bug error.
	 *

	 * Recommend way to use this method is to provide content type instance returned by {@link #getContentTypeForValue(Object)}.
	 *
	 *

	 * Object value = ...
	 * ContentType contentType = encoders.getContentTypeForValue(value);
	 * ValueWriter writer = encoders.getValueWriter(contentType);
	 * ...
	 *

	 *
	 * @param contentType content type for return value.
	 * @return return value writer, never null.
	 * @throws BugError if no value writer registered for requested content type.
	 */
	@Override
	public ValueWriter getValueWriter(ContentType contentType) {
		ValueWriter writer = writers.get(contentType);
		if (writer == null) {
			throw new BugError("No return value writer for content type |%s|.", contentType);
		}
		return writer;
	}
}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies 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 FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.cologneintelligence.fitgoodies;

import de.cologneintelligence.fitgoodies.htmlparser.FitCell;

public class PrimitiveFixture extends Fixture {

	// format converters ////////////////////////

	public static long parseLong(FitCell cell) {
		return Long.parseLong(cell.getFitValue());
	}

	public static double parseDouble(FitCell cell) {
		return Double.parseDouble(cell.getFitValue());
	}

	public static boolean parseBoolean(FitCell cell) {
		return Boolean.valueOf(cell.getFitValue());
	}

	// answer comparisons ///////////////////////

	public void check(FitCell cell, String value) {
		if (cell.getFitValue().equals(value)) {
			cell.right();
		} else {
			cell.wrong(value);
		}
	}

	public void check(FitCell cell, long value) {
		if (parseLong(cell) == value) {
			cell.right();
		} else {
			cell.wrong(Long.toString(value));
		}
	}

	public void check(FitCell cell, double value) {
		if (parseDouble(cell) == value) {
			cell.right();
		} else {
			cell.wrong(Double.toString(value));
		}
	}

	public void check(FitCell cell, boolean value) {
		if (parseBoolean(cell) == value) {
			cell.right();
		} else {
			cell.wrong(Boolean.toString(value));
		}
	}

}


package net.typedrest;

import java.io.IOException;
import java.net.*;
import rx.*;
import rx.schedulers.Schedulers;
import rx.util.async.StoppableObservable;
import static rx.util.async.Async.runAsync;

/**
 * Base class for building REST endpoints that represents a stream of
 * TEntitys as TElementEndpoints.
 *
 * @param <TEntity> The type of entity the endpoint represents.
 * @param <TElementEndpoint> The specific type of {@link ElementEndpoint} to
 * provide for individual TEntitys.
 */
public abstract class AbstractStreamEndpoint<TEntity, TElementEndpoint extends Endpoint>
        extends AbstractCollectionEndpoint<TEntity, TElementEndpoint> implements GenericStreamEndpoint<TEntity, TElementEndpoint> {

    /**
     * Creates a new element stream endpoint.
     *
     * @param referrer The endpoint used to navigate to this one.
     * @param relativeUri The URI of this endpoint relative to the
     * referrer's. Missing trailing slash will be appended
     * automatically.
     * @param entityType The type of entity the endpoint represents.
     */
    protected AbstractStreamEndpoint(Endpoint referrer, URI relativeUri, Class<TEntity> entityType) {
        super(referrer, relativeUri, entityType);
    }

    /**
     * Creates a new element stream endpoint.
     *
     * @param referrer The endpoint used to navigate to this one.
     * @param relativeUri The URI of this endpoint relative to the
     * referrer's. Missing trailing slash will be appended
     * automatically. Prefix ./ to append a trailing slash to the
     * parent URI if missing.
     * @param entityType The type of entity the endpoint represents.
     */
    protected AbstractStreamEndpoint(Endpoint referrer, String relativeUri, Class<TEntity> entityType) {
        super(referrer, relativeUri, entityType);
    }

    @Override
    public StoppableObservable<TEntity> getObservable(long startIndex) {
        return getObservable(startIndex, Schedulers.newThread());
    }

    /**
     * Provides an observable stream of elements.
     *
     * @param startIndex The index of the first element to return in the stream.
     * Use negative values to start counting from the end of the stream.
     * @param scheduler The scheduler used to run the background thread.
     * @return An observable stream of elements.
     */
    StoppableObservable<TEntity> getObservable(final long startIndex, Scheduler scheduler) {
        return runAsync(scheduler, (rx.Observer<? super TEntity> observer, Subscription subscription) -> {
            long currentStartIndex = startIndex;
            while (!subscription.isUnsubscribed()) {
                PartialResponse<TEntity> response;
                try {
                    response = (currentStartIndex >= 0)
                            ? readRange(currentStartIndex, null)
                            : readRange(null, -currentStartIndex);
                } catch (IllegalStateException ex) {
                    // No new data available yet, keep polling
                    continue;
                } catch (IOException | IllegalArgumentException | IllegalAccessException error) {
                    observer.onError(error);
                    return;
                }

                response.getElements().stream().forEach(observer::onNext);

                if (response.isEndReached()) {
                    observer.onCompleted();
                    return;
                }

                // Continue polling for more data
                currentStartIndex = response.getTo() + 1;
            }
        });
    }
}


package js.container;

import js.converter.Converter;

/**
 * Managed class scope controls managed instance life span. Instance scope is used as selector for {@link ScopeFactory}
 * strategy, that is, it selects the right scope factory to use for managed instance retrieval. Note that {@link #LOCAL} scope
 * means managed instance is always fresh created and that there is no scope factory for it.
 *

 * Instance scope is configured into managed class descriptor, scope attribute, with default value to
 * {@link #APPLICATION}. In sample descriptor, managed request context class has {@link #THREAD} scope; as a consequence
 * {@link ThreadScopeFactory} will be used for managed instance retrieval. It is developer responsibility to use a supported
 * scope value otherwise managed class initialization fails with application abort.
 *
 *

 * <request-context class="js.servlet.RequestContext" scope="THREAD" />
 *

 *
 *

 * There are predefined scope constants but user defined instance scopes are supported. Anyway, adding a new scope implies also
 * creating a related {@link ScopeFactory}.
 *

 *

 * Scope
 * Factory
 * Description
 *

 * APPLICATION
 * {@link ApplicationScopeFactory}
 * Application level singleton; one instance per application. Once created, instances with APPLICATION scope last for entire
 * application life span and are destroyed just before application exit.
 *

 * THREAD
 * {@link ThreadScopeFactory}
 * Thread local storage. When declaring thread is destroyed local storage is freed and THREAD managed instance is garbage
 * collected.
 *

 * SESSION
 * SessionScopeFactory
 * Stateful across a single HTTP session. Managed instance is stored on HTTP session attribute; when HTTP session expires,
 * attribute is removed and SESSION managed instance becomes candidate for GC.
 *

 * LOCAL
 * N/A
 * Local scope creates always a new instance that last till declaring code block exit.
 *

 *
 *

 * Warning: use a supported scope value into managed class descriptor or application fails to start.
 *
 * @author Iulian Rotaru
 * @version final
 */
public class InstanceScope implements Converter {
	/**
	 * Application level singleton, that is, one instance per application. Instances with APPLICATION scope are destroyed at
	 * application exit.
	 */
	public static final InstanceScope APPLICATION = new InstanceScope("APPLICATION");

	/**
	 * Thread local storage. When declaring thread is destroyed local storage is freed and THREAD managed instance is garbage
	 * collected.
	 */
	public static final InstanceScope THREAD = new InstanceScope("THREAD");

	/**
	 * Stateful across a single HTTP session. Managed instance is stored on HTTP session attribute; when HTTP session expires,
	 * attribute is removed and SESSION managed instance becomes candidate for GC.
	 */
	public static final InstanceScope SESSION = new InstanceScope("SESSION");

	/** Local scope creates always a new instance that last till declaring code block exit. */
	public static final InstanceScope LOCAL = new InstanceScope("LOCAL");

	// --------------------------------------------------------------------------------------------
	// INSTANCE

	/** Scope value. */
	private final String value;

	/** Default constructor. */
	public InstanceScope() {
		this.value = null;
	}

	/**
	 * Construct an immutable scope.
	 *
	 * @param value scope value.
	 */
	public InstanceScope(String value) {
		this.value = value;
	}

	/**
	 * Get scope value.
	 *
	 * @return scope value.
	 */
	public String getValue() {
		return value;
	}

	/** Scope instance string representation. */
	@Override
	public String toString() {
		return value;
	}

	/** Hash code based on scope value. */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((value == null) ? 0 : value.hashCode());
		return result;
	}

	/** Two scopes are equal if have the same value. */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		InstanceScope other = (InstanceScope) obj;
		if (value == null) {
			if (other.value != null)
				return false;
		} else if (!value.equals(other.value))
			return false;
		return true;
	}

	// --------------------------------------------------------------------------------------------
	// CONVERTER

	/** Create a scope instance from its string value. */
	@SuppressWarnings("unchecked")
	@Override
	public <T> T asObject(String string, Class<T> valueType) {
		return (T) new InstanceScope(string);
	}

	/** Return string value of given scope object. */
	@Override
	public String asString(Object object) {
		return ((InstanceScope) object).getValue();
	}
}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies 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 FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.cologneintelligence.fitgoodies.typehandler;

import de.cologneintelligence.fitgoodies.date.FitDateHelper;
import de.cologneintelligence.fitgoodies.util.DependencyManager;

import java.text.ParseException;
import java.util.Date;

/**
 * TypeAdapter which is able to convert a string into a {@code java.util.Date}.
 * This class uses the {@link FitDateHelper} to allow the user
 * to set individual formats.
 * <p>
 * If the cell is parameterized, the date format can be set individually.
 * The parameter must have the format "{@code locale, format}".
 * Example: "{@code en_US, MM/dd/yyyy}".
 */
public class DateTypeHandler extends TypeHandler<Date> {
	private final FitDateHelper dateFitDateHelper;

	/**
	 * Creates a new TypeAdapter which bases on {@code ta}.
	 *
	 * @param convertParameter a parameter in the format [locale, format] which
	 *                         represents the format to use
	 */
	public DateTypeHandler(final String convertParameter) {
		super(convertParameter);
		this.dateFitDateHelper = DependencyManager.getOrCreate(FitDateHelper.class);
	}

	/**
	 * Returns the destination class which is managed by this parser.
	 *
	 * @return java.util.Date.class
	 */
	@Override
	public final Class<Date> getType() {
		return Date.class;
	}

	/**
	 * Parses a string and converts it into a {@code java.util.Date} object.
	 *
	 * @param s {@code String} which will be converted
	 * @return {@code java.util.Date} object which is represented by {@code s}.
	 */
	@Override
	public final Date unsafeParse(final String s) throws ParseException {
		return dateFitDateHelper.parse(s, parameter);
	}

	@Override
	public String toString(Date s) {
		return dateFitDateHelper.toString(s);
	}
}


package js.servlet;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;

import js.container.ClassProcessor;
import js.container.InstanceProcessor;
import js.container.ManagedClassSPI;
import js.core.AppContext;
import js.lang.BugError;
import js.log.Log;
import js.log.LogFactory;

/**
 * Initialize fields depending on context parameters, both class and instance.
 *
 * @author Iulian Rotaru
 * @version final
 */
public class ContextParamProcessor implements ClassProcessor, InstanceProcessor {
	/** Class logger. */
	private static final Log log = LogFactory.getLog(ContextParamProcessor.class);

	/**
	 * Application context used to supply named context parameter, see {@link AppContext#getProperty(String)} and related
	 * {@link AppContext#getProperty(String, Class)}.
	 */
	private final AppContext context;

	/**
	 * Initialize processor instance.
	 *
	 * @param context application context.
	 */
	public ContextParamProcessor(AppContext context) {
		this.context = context;
	}

	@Override
	public void postProcessClass(ManagedClassSPI managedClass) {
		for (Map.Entry<String, Field> entry : managedClass.getContextParamFields().entrySet()) {
			final String parameterName = entry.getKey();
			final Field field = entry.getValue();
			log.debug("Initialize static field |%s| from context parameter |%s|.", field, parameterName);
			if (Modifier.isStatic(field.getModifiers())) {
				setField(field, parameterName, null);
			}
		}
	}

	@Override
	public void postProcessInstance(ManagedClassSPI managedClass, Object instance) {
		for (Map.Entry<String, Field> entry : managedClass.getContextParamFields().entrySet()) {
			final String parameterName = entry.getKey();
			final Field field = entry.getValue();
			log.debug("Initialize instance field |%s| from context parameter |%s|.", field, parameterName);
			if (!Modifier.isStatic(field.getModifiers())) {
				setField(field, parameterName, instance);
			}
		}
	}

	/**
	 * Initialize field from named context parameter.
	 *
	 * @param field field to be initialized, both class and instance fields accepted,
	 * @param parameterName name for context parameter,
	 * @param instance optional instance, null for class fields.
	 */
	private void setField(Field field, String parameterName, Object instance) {
		final Object value = context.getProperty(parameterName, field.getType());
		try {
			field.set(instance, value);
		} catch (Exception e) {
			throw new BugError(e);
		}
	}
}


/*
 * Copyright 2010 akquinet
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.akquinet.chameria.webview;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.trolltech.qt.core.QUrl;
import com.trolltech.qt.core.Qt.ContextMenuPolicy;
import com.trolltech.qt.core.Qt.Orientation;
import com.trolltech.qt.network.QNetworkReply;
import com.trolltech.qt.webkit.QWebPage;
import com.trolltech.qt.webkit.QWebPage.WebWindowType;
import com.trolltech.qt.webkit.QWebView;

/**
 * This class represents web views.
 */
public class ChameriaWebView extends QWebView {

    /**
     * The logger.
     */
    private Logger m_logger = LoggerFactory.getLogger(this.getClass());

    /**
     * the logger used for javascript messages.
     */
    private Logger m_javascriptLogger = LoggerFactory.getLogger("chameria.javascript");

    /**
     * The WebView Factory.
     */
    private WebViewFactory m_factory;

    /**
     * Creates a {@link ChameriaWebView}.
     * @param factory the factory
     */
    public ChameriaWebView(WebViewFactory factory) {
        super();
        m_factory = factory;
        loadStarted.connect(this, "loadStarted()");
        loadProgress.connect(this, "loadProgress(int)");
        loadFinished.connect(this, "loadDone()");
        linkClicked.connect(this, "linkClicked(QUrl)");

        setPage(new QWebPage() {
            @Override
            protected void javaScriptConsoleMessage(String message,
                    int lineNumber, String sourceID) {
                m_javascriptLogger.info("Javascript> " + message + " (" + sourceID + ", " + lineNumber + ")");
            }
        });

        page().setForwardUnsupportedContent(true);
        page().unsupportedContent.connect(this, "save(QNetworkReply)");

        page().printRequested.connect(this, "print()");
        page().windowCloseRequested.connect(this, "windowCloseRequested()");

        page().mainFrame().setScrollBarPolicy(Orientation.Horizontal, m_factory.getHorizontalScrollBarQTPolicy());
        page().mainFrame().setScrollBarPolicy(Orientation.Vertical, m_factory.getVerticalScrollBarQTPolicy());

        if (! m_factory.isContextMenuSupported()) {
            setContextMenuPolicy(ContextMenuPolicy.NoContextMenu);
        }

    }

    /**
     * This function is called whenever WebKit wants to create a new window of the given type,
     * for example when a JavaScript program requests to open a document in a new window.
     * @see com.trolltech.qt.webkit.QWebView#createWindow(com.trolltech.qt.webkit.QWebPage.WebWindowType)
     */
    @Override
    protected QWebView createWindow(WebWindowType type) {
        m_logger.info("Open a new window");
        return m_factory.openWindow();
    }

    /**
     * A link was clicked.
     * @param url the url
     */
    public void linkClicked(QUrl url) {
        m_logger.info("Link " + url);
    }

    /**
     * A new url is is loading.
     */
    public void loadStarted() {
        m_logger.info("Starting to load " + url());
    }

    /**
     * Loading done.
     */
    public void loadDone() {
        m_logger.info("Loading done... " + url());
        repaint();
    }

    /**
     * The current window will be closed.
     */
    public void windowCloseRequested() {
        m_logger.info("Close the window");
        this.close();
    }

    /**
     * Print requested (window.print)
     */
    public void print() {
        m_logger.info("Print Requested");
        m_factory.print(this);
    }

    /**
     * The page is loading.
     * @param x the new progress
     */
    public void loadProgress(int x) {
        if (x >= 10  && x % 10 == 0) {
            m_logger.info("Loading: " + x + " %");
        }
    }

    /**
     * Save dialog requested (unsupported content).
     * @param reply the url
     */
    public void save(QNetworkReply reply) {
        m_logger.info("Unsupported Content : " + reply.url().toString()  + " - Download request");
        m_factory.save(reply);
    }
}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies 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 FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.cologneintelligence.fitgoodies.log4j;

import org.apache.log4j.Appender;
import org.apache.log4j.spi.AppenderAttachable;

/**
 * Sinlgeton class which helps to manage loggers.
 */
public final class LogHelper {
	/**
	 * Adds a new CaptureAppender to an existing logger.
	 *
	 * @param logger       logger to use
	 * @param appenderName name of an existing appender which will be captured
	 */
	public void addCaptureToLogger(final AppenderAttachable logger,
	                               final String appenderName) {
		Appender currentAppender = logger.getAppender(appenderName);
		Appender captureAppender = CaptureAppender.newAppenderFrom(currentAppender);
		logger.addAppender(captureAppender);
	}

	/**
	 * Returns the CaptureAppender which captures appenderName.
	 *
	 * @param logger       logger to use
	 * @param appenderName name of existing appender
	 * @return responsible CaptureAppender
	 */
	public Appender getCaptureAppender(final AppenderAttachable logger,
	                                   final String appenderName) {
		return logger.getAppender(CaptureAppender.getAppenderNameFor(appenderName));
	}

	/**
	 * Removes a CaptureAppender from a logger.
	 *
	 * @param logger       logger to use
	 * @param appenderName name of the appender which is captured
	 */
	public void remove(final AppenderAttachable logger, final String appenderName) {
		logger.removeAppender(CaptureAppender.getAppenderNameFor(appenderName));
	}

	/**
	 * Deletes all cached log entries.
	 *
	 * @param logger       logger to use
	 * @param appenderName name of the appender which will be cleared
	 */
	public void clear(final AppenderAttachable logger, final String appenderName) {
		((CaptureAppender) logger.getAppender(CaptureAppender.getAppenderNameFor(
				appenderName))).clear();
	}
}


package js.http.encoder;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;

import javax.servlet.http.HttpServletRequest;

import js.dom.Document;
import js.http.ContentType;
import js.json.Json;
import js.lang.IllegalArgumentException;
import js.util.Files;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

/**
 * Reader for arguments encoded with HTTP multipart mixed format. This arguments reader is invoked with a HTTP request with
 * {@link ContentType#MULTIPART_MIXED}.
 *

 * Basically mutipart mixed format is a collection of body entities in the same message. This reader is able to process a mix of
 * {@link ContentType#APPLICATION_JSON}, {@link ContentType#TEXT_XML} and {@link ContentType#APPLICATION_STREAM} body entities.
 * If bytes stream body entity is present it should be the last one. Formal parameters order should be compatible with mixed
 * body entities order. JSON body entity is deserialized to any Java type supported by {@link Json} implementation, XML to
 * {@link Document} instance and bytes stream to a type supported by {@link StreamArgumentsReader}.
 *

 * This arguments reader implementation is heavily based on {@link js.http.form} package.
 *
 * @author Iulian Rotaru
 * @version final
 */
final class MultipartMixedArgumentsReader implements ArgumentsReader {
	/**
	 * Store input stream on current thread till HTTP request is processed by application logic, in order to close it after. See
	 * {@link #clean()}.
	 */
	private final ThreadLocal<Closeable> threadLocal = new ThreadLocal<>();

	/** Arguments reader factory for part argument readers. */
	private final ArgumentsReaderFactory argumentsReaderFactory;

	/**
	 * Construct multipart mixed arguments reader.
	 *
	 * @param argumentsReaderFactory arguments reader factory.
	 */
	public MultipartMixedArgumentsReader(ArgumentsReaderFactory argumentsReaderFactory) {
		this.argumentsReaderFactory = argumentsReaderFactory;
	}

	/**
	 * Read mixed body entities from HTTP request, creating and initializing arguments accordingly given formal parameters. The
	 * number and order of arguments from multipart mixed message should respect formal parameter types. Also stream argument,
	 * if present, should be a single one and the last on arguments list.
	 *
	 * @param httpRequest HTTP request,
	 * @param formalParameters formal parameter types.
	 * @return arguments array.
	 * @throws IOException if reading from request input stream fails.
	 * @throws IllegalArgumentException if arguments from HTTP request cannot be parsed.
	 * @throws IllegalArgumentException if content type for a mixed entity body is not supported.
	 * @throws IllegalArgumentException if there is a stream argument but is not the last on arguments list.
	 */
	@Override
	public Object[] read(HttpServletRequest httpRequest, Type[] formalParameters) throws IOException, IllegalArgumentException {
		try {
			Object[] arguments = new Object[formalParameters.length];
			int argumentIndex = 0;

			ServletFileUpload multipart = new ServletFileUpload();
			FileItemIterator iterator = multipart.getItemIterator(httpRequest);

			FileItemStream fileItemStream = null;
			while (iterator.hasNext()) {
				fileItemStream = iterator.next();
				String contentType = fileItemStream.getContentType();
				Type parameterType = formalParameters[argumentIndex];

				ArgumentPartReader reader = argumentsReaderFactory.getArgumentPartReader(contentType, parameterType);
				boolean streamArgument = StreamFactory.isStream(parameterType);

				ArgumentPartReader argumentPartReader = (ArgumentPartReader) reader;
				InputStream inputStream = streamArgument ? new LazyFileItemStream(fileItemStream) : fileItemStream.openStream();

				arguments[argumentIndex] = argumentPartReader.read(inputStream, parameterType);
				++argumentIndex;

				// stream argument should be last on mixed arguments list
				// save it to local thread storage for clean-up after arguments processed by application
				if (streamArgument) {
					threadLocal.set(inputStream);
					break;
				}
				inputStream.close();
			}

			if (argumentIndex != formalParameters.length) {
				throw new IllegalArgumentException("Not all parameters processed due to stream argument that is not the last on arguments list.");
			}
			return arguments;
		} catch (FileUploadException e) {
			throw new IOException(e.getMessage());
		}
	}

	/** Close bytes stream from current thread storage then remove the storage. */
	@Override
	public void clean() {
		Files.close(threadLocal.get());
		threadLocal.remove();
	}

	// --------------------------------------------------------------------------------------------
	// INNER CLASS

	/**
	 * Thin wrapper for {@link FileItemStream} that opens input stream on the fly, on first read.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private static final class LazyFileItemStream extends InputStream {
		/** Wrapped file item stream. */
		private FileItemStream fileItemStream;
		/** File item input stream. */
		private InputStream inputStream;

		/**
		 * Construct lazy file item stream.
		 *
		 * @param fileItemStream source file item stream.
		 */
		public LazyFileItemStream(FileItemStream fileItemStream) {
			this.fileItemStream = fileItemStream;
		}

		/**
		 * Read next byte from file item input stream, creating it on the fly if necessary.
		 *
		 * @return next byte from input stream or -1 on stream end.
		 * @throws IOException if read operation fails.
		 */
		@Override
		public int read() throws IOException {
			if (inputStream == null) {
				inputStream = fileItemStream.openStream();
			}
			return inputStream.read();
		}

		/**
		 * Close file item input stream.
		 *
		 * @throws IOException if close operation fails.
		 */
		@Override
		public void close() throws IOException {
			if (inputStream != null) {
				inputStream.close();
			}
		}
	}
}


/**
 * Copyright (C) 2015 Michael Schnell. All rights reserved.
 * http://www.fuin.org/
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option) any
 * later version.
 *
 * This library 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 Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library. If not, see http://www.gnu.org/licenses/.
 */
package org.fuin.srcgen4j.core.emf;

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.apache.commons.io.FilenameUtils;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Diagnostic;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.fuin.srcgen4j.core.base.AbstractParser;
import org.fuin.utils4j.Utils4J;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Basic functionality for parsing EMF Ecore models.
 *
 * @param <CONFIG_TYPE>
 *            Type of the concrete configuration.
 */
public abstract class AbstractEMFParser<CONFIG_TYPE> extends AbstractParser<CONFIG_TYPE> {

    private static final Logger LOG = LoggerFactory.getLogger(AbstractEMFParser.class);

    private ResourceSet resourceSet = new ResourceSetImpl();

    private boolean error = false;

    private List<File> modelDirs;

    private List<String> fileExtensions;

    private List<URI> modelResources;

    /**
     * Basic constructor.
     *
     * @param concreteConfigClass
     *            Type of the configuration.
     */
    public AbstractEMFParser(final Class<CONFIG_TYPE> concreteConfigClass) {
        super(concreteConfigClass);
    }

    /**
     * Constructor with array.
     *
     * @param concreteConfigClass
     *            Type of the configuration.
     * @param modelDirs
     *            Directory with the Ecore model files.
     * @param fileExtensions
     *            List of extensions for files to find ("mymodel", "java", "class", ...)
     */
    public AbstractEMFParser(final Class<CONFIG_TYPE> concreteConfigClass, final List<File> modelDirs, final String... fileExtensions) {
        this(concreteConfigClass, modelDirs, Arrays.asList(fileExtensions));
    }

    /**
     * Constructor with list.
     *
     * @param concreteConfigClass
     *            Type of the configuration.
     * @param modelDirs
     *            Directory with the Ecore model files.
     * @param fileExtensions
     *            List of extensions for files to find ("mymodel", "java", "class", ...)
     */
    public AbstractEMFParser(final Class<CONFIG_TYPE> concreteConfigClass, final List<File> modelDirs, final List<String> fileExtensions) {
        super(concreteConfigClass);
        this.modelDirs = modelDirs;
        this.fileExtensions = fileExtensions;
    }

    /**
     * Parses all model files in the directories and all resources. y
     */
    protected final void parseModel() {

        if ((fileExtensions == null) || (fileExtensions.size() == 0)) {
            throw new IllegalStateException("No file extensions for EMF model files set!");
        }

        // Drop previous resource set
        resourceSet = new ResourceSetImpl();
        error = false;

        parseDirs();
        parseResources();

    }

    private void parseDirs() {
        if ((modelDirs == null) || (modelDirs.size() == 0)) {
            LOG.debug("No model directories to parse");
        } else {
            for (final File modelDir : modelDirs) {
                parseDir(modelDir);
            }
        }
    }

    private void parseResources() {
        if ((modelResources == null) || (modelResources.size() == 0)) {
            LOG.debug("No model resources to parse");
        } else {
            for (final URI modelResource : modelResources) {
                final Resource resource = resourceSet.getResource(modelResource, true);
                final EList<Diagnostic> diagnostics = resource.getErrors();
                if (diagnostics.size() == 0) {
                    LOG.debug("Parsed {}", modelResource);
                } else {
                    error = true;
                    LOG.error("Parsed {} with errors: {}", modelResource, diagnostics);
                }
            }
        }
    }

    /**
     * Tries to resolve all proxies.
     */
    protected final void resolveProxies() {
        final List<String> unresolved = new ArrayList<String>();
        if (!resolvedAllProxies(unresolved, 0)) {
            LOG.warn("Could not resolve the following proxies ({}):", unresolved.size());
            for (final String ref : unresolved) {
                LOG.warn("Not found: {}", ref);
            }
            final Iterator<Notifier> it = resourceSet.getAllContents();
            while (it.hasNext()) {
                final Notifier next = it.next();
                if (next instanceof EObject) {
                    final EObject obj = (EObject) next;
                    if (obj.eIsProxy()) {
                        try {
                            it.remove();
                        } catch (final UnsupportedOperationException ex) {
                            LOG.error("Could not remove proxy: " + obj, ex);
                        }
                    }
                }
            }
        }
    }

    /**
     * Returns files that end with java and all directories. Files and directories started with a "." are excluded.
     *
     * @param dir
     *            Directory to scan.
     *
     * @return List of files in the directory.
     */
    private File[] getFiles(final File dir) {
        final File[] files = dir.listFiles(new FileFilter() {
            @Override
            public boolean accept(final File file) {
                final boolean pointFile = file.getName().startsWith(".");
                final String extension = FilenameUtils.getExtension(file.getName());
                return (!pointFile && fileExtensions.contains(extension)) || file.isDirectory();
            }
        });
        return files;
    }

    /**
     * Parse the directory and it's sub directory.
     *
     * @param dir
     *            Directory to parse.
     */
    private void parseDir(final File dir) {
        LOG.debug("Parse: {}", dir);
        final File[] files = getFiles(dir);
        if ((files == null) || (files.length == 0)) {
            LOG.debug("No files found in directory: {}", dir);
        } else {
            for (final File file : files) {
                if (file.isFile()) {
                    final Resource resource = resourceSet.getResource(URI.createFileURI(Utils4J.getCanonicalPath(file)), true);
                    final EList<Diagnostic> diagnostics = resource.getErrors();
                    if (diagnostics.size() == 0) {
                        LOG.debug("Parsed {}", file);
                    } else {
                        error = true;
                        LOG.error("Parsed {} with errors: {}", file, diagnostics);
                    }
                } else {
                    parseDir(file);
                }
            }
        }
    }

    /**
     * Determines if all proxies in the model are resolved.
     *
     * @return {@code true} if there are no more proxies in the model.
     */
    public boolean isModelFullyResolved() {

        boolean resolved = true;
        final Set<EObject> eObjects = findAllEObjects(resourceSet);
        final Iterator<EObject> it = eObjects.iterator();
        while (it.hasNext()) {
            final EObject eObj = it.next();
            if (eObj instanceof InternalEObject) {
                final InternalEObject iObj = (InternalEObject) eObj;
                for (final EObject crossRef : iObj.eCrossReferences()) {
                    if (crossRef.eIsProxy()) {
                        LOG.error("Unresolved: {}", crossRef);
                        resolved = false;
                    }
                }
            }
        }
        return resolved;

    }

    private boolean resolvedAllProxies(final List<String> unresolved, final int resourcesProcessedBefore) {

        final Set<EObject> eObjects = findAllEObjects(resourceSet);

        // Check if we discovered any new resources
        final int totalResources = resourceSet.getResources().size();
        final boolean discoveredNewResources = (totalResources != resourcesProcessedBefore);
        if (!discoveredNewResources) {
            return true;
        }

        // Resolve references
        boolean failure = false;
        int resolved = 0;
        final int count = eObjects.size();
        final Iterator<EObject> it = eObjects.iterator();
        int i = 0;
        while (it.hasNext()) {
            final EObject eObj = it.next();
            if (eObj instanceof InternalEObject) {
                final InternalEObject iObj = (InternalEObject) eObj;
                for (final EObject crossRef : iObj.eCrossReferences()) {
                    final EObject resolvedRef = EcoreUtil.resolve(crossRef, resourceSet);
                    final String resolvedRefStr = getStrRef(resolvedRef);
                    if (resolvedRef.eIsProxy()) {
                        failure = true;
                        if (!unresolved.contains(resolvedRefStr)) {
                            unresolved.add(resolvedRefStr);
                        }
                    } else {
                        resolved++;
                        unresolved.remove(resolvedRefStr);
                    }
                }
                if ((i % 1000) == 0) {
                    LOG.debug("Finished {} of {} references", i, count);
                }
                i++;
            }
        }
        LOG.debug("Cross references - Resolved: {}, Unresolved: {}", resolved, unresolved.size());
        return !failure && resolvedAllProxies(unresolved, totalResources);
    }

    private static String getStrRef(final EObject resolvedRef) {
        if (resolvedRef instanceof InternalEObject) {
            final URI uri = ((InternalEObject) resolvedRef).eProxyURI();
            if (uri != null) {
                return uri.toString();
            }
            return resolvedRef.toString();
        }
        return resolvedRef.toString();
    }

    /**
     * Returns a list of all objects.
     *
     * @return List.
     */
    private static Set<EObject> findAllEObjects(final ResourceSet resourceSet) {
        final Set<EObject> list = new HashSet<EObject>();
        for (final Iterator<Notifier> i = resourceSet.getAllContents(); i.hasNext();) {
            final Notifier next = i.next();
            if (next instanceof EObject) {
                list.add((EObject) next);
            }
        }
        return list;
    }

    /**
     * Returns the model directories.
     *
     * @return List of directories.
     */
    protected final List<File> getModelDirs() {
        return modelDirs;
    }

    /**
     * Returns the model resources.
     *
     * @return List of resources.
     */
    protected final List<URI> getModelResources() {
        return modelResources;
    }

    /**
     * Returns the list of file extensions.
     *
     * @return List of extensions for files to find ("mymodel", "java", "class", ...)
     */
    protected final List<String> getFileExtensions() {
        return fileExtensions;
    }

    /**
     * Returns the resource set.
     *
     * @return Resource set.
     */
    protected final ResourceSet getResourceSet() {
        return resourceSet;
    }

    /**
     * Sets the model directories to parse.
     *
     * @param modelDirs
     *            List of model directories or NULL.
     */
    protected final void setModelDirs(final List<File> modelDirs) {
        this.modelDirs = modelDirs;
    }

    /**
     * Sets the model directories to parse. If no list exists internally, it will be created if necessary.
     *
     * @param modelDirs
     *            Array of model directories or NULL.
     */
    protected final void setModelDirs(final File... modelDirs) {
        if (modelDirs == null) {
            this.modelDirs = null;
        } else {
            this.modelDirs = new ArrayList<File>();
            this.modelDirs.addAll(Arrays.asList(modelDirs));
        }
    }

    /**
     * Sets the list of file extensions.
     *
     * @param fileExtensions
     *            List of extensions for files to find ("mymodel", "java", "class", ...)
     */
    protected final void setFileExtensions(final List<String> fileExtensions) {
        this.fileExtensions = fileExtensions;
    }

    /**
     * Sets the list of file extensions. If no list exists internally, it will be created if necessary.
     *
     * @param fileExtensions
     *            Array of extensions for files to find ("mymodel", "java", "class", ...)
     */
    protected final void setFileExtensions(final String... fileExtensions) {
        if (fileExtensions == null) {
            this.fileExtensions = null;
        } else {
            this.fileExtensions = new ArrayList<String>();
            this.fileExtensions.addAll(Arrays.asList(fileExtensions));
        }
    }

    /**
     * Sets the model resources to parse.
     *
     * @param modelResources
     *            List of model resources or NULL.
     */
    protected final void setModelResources(final List<URI> modelResources) {
        this.modelResources = modelResources;
    }

    /**
     * Sets the model resources to parse. If no list exists internally, it will be created if necessary.
     *
     * @param modelResources
     *            Array of model resources or NULL.
     */
    protected final void setModelResources(final URI... modelResources) {
        if (modelResources == null) {
            this.modelResources = null;
        } else {
            this.modelResources = new ArrayList<URI>();
            this.modelResources.addAll(Arrays.asList(modelResources));
        }
    }

    /**
     * Returns the information if an error happened in the last parse process.
     *
     * @return {@code true} if there was an error.
     */
    public final boolean isError() {
        return error;
    }

}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies 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 FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
*/

package de.cologneintelligence.fitgoodies.file.readers;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Reader which is capable to process comma separated value files.
 */
public class CSVRecordReader implements FileRecordReader {
	private static class ParserState {
		private boolean masked = false;
		private boolean skip = false;

		public boolean masked() {
			return masked;
		}

		public void toggleMasked() {
			masked = !masked;
		}

		public boolean skipped() {
			return skip;
		}

		public void skip(final boolean doIt) {
			skip = doIt;
		}
	}

	private final BufferedReader reader;
	private final char delimiterChar;
	private final char maskChar;
	private String[] parts;
	private int partIndex;

	/**
	 * Creates a new reader object.
	 *
	 * @param bufferedReader underlying stream to process
	 * @param fieldDelimiter field delimiter character (usually a comma)
	 * @param fieldMask      masking character (usually a quotation mark)
	 * @throws IOException thrown if {@code bufferedReader} reports a problem
	 */
	public CSVRecordReader(
			final BufferedReader bufferedReader,
			final char fieldDelimiter,
			final char fieldMask) throws IOException {

		reader = bufferedReader;
		delimiterChar = fieldDelimiter;
		maskChar = fieldMask;

		nextRecord();
	}

	/**
	 * Closes the underlying stream.
	 *
	 * @throws java.io.IOException Exception when stream cannot be closed.
	 */
	@Override
	public void close() throws IOException {
		reader.close();
	}

	/**
	 * Returns the next field in the record set.
	 *
	 * @return next field's value, or {@code null}, if the last column has
	 * been reached.
	 */
	@Override
	public final String nextField() {
		if (parts == null || partIndex >= parts.length) {
			return null;
		}

		final String returnValue = parts[partIndex];
		++partIndex;
		return returnValue;
	}

	/**
	 * Reads the next row.
	 *
	 * @return {@code true} if a record set could been read,
	 * {@code false} on end of file.
	 * @throws IOException when the underlying Stream reports an error
	 */
	@Override
	public final boolean nextRecord() throws IOException {
		final String line = reader.readLine();
		if (line == null) {
			parts = null;
			return false;
		}

		final List<String> newParts = splitLine(line);
		parts = newParts.toArray(new String[newParts.size()]);
		partIndex = 0;
		return true;
	}

	private List<String> splitLine(final String line) throws IOException {
		final List<String> newParts = new ArrayList<>();
		final ParserState state = new ParserState();

		final StringBuilder todo = new StringBuilder(line);
		final StringBuilder builder = new StringBuilder();

		while (todo.length() > 0) {
			final char character = todo.charAt(0);
			todo.deleteCharAt(0);

			changeState(state, character);
			final String result = processChar(state, builder, character);
			if (result != null) {
				newParts.add(result);
			}

			readNextLineIfNeeded(state, todo);
		}

		if (builder.length() > 0) {
			newParts.add(builder.toString().trim());
		}

		return newParts;
	}

	private void readNextLineIfNeeded(final ParserState state, final StringBuilder todo)
			throws IOException {
		if (todo.length() == 0 && state.masked() && !state.skipped()) {
			final String newLine = reader.readLine();
			if (newLine != null) {
				todo.append("\n");
				todo.append(newLine);
			} else {
				throw new RuntimeException("malformed csv");
			}
		}
	}

	private String processChar(final ParserState state,
	                           final StringBuilder builder, final char character) {
		String result = null;
		if (character == delimiterChar && !state.masked()
				&& !state.skipped()) {
			result = builder.toString();
			builder.delete(0, builder.length());
		} else if (!state.skipped()) {
			builder.append(character);
		}
		return result;
	}

	private void changeState(final ParserState state, final char character) {
		final boolean isMaskedChar = character == maskChar;

		if (isMaskedChar && !state.skipped()) {
			state.skip(true);
		} else if (!isMaskedChar && state.skipped()) {
			state.toggleMasked();
			state.skip(false);
		} else {
			state.skip(false);
		}
	}

	/**
	 * Reports whether {@link #nextField()} can be called or whether no more
	 * record set is available.
	 *
	 * @return {@code true} if {@link #nextField()} will return values,
	 * {@code false} otherwise
	 */
	@Override
	public final boolean canRead() {
		return parts != null;
	}
}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies 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 FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.cologneintelligence.fitgoodies;

import de.cologneintelligence.fitgoodies.htmlparser.FitCell;
import de.cologneintelligence.fitgoodies.htmlparser.FitRow;
import de.cologneintelligence.fitgoodies.htmlparser.FitTable;
import de.cologneintelligence.fitgoodies.typehandler.TypeHandler;
import de.cologneintelligence.fitgoodies.util.FitUtils;
import de.cologneintelligence.fitgoodies.valuereceivers.ValueReceiver;
import de.cologneintelligence.fitgoodies.valuereceivers.ValueReceiverFactory;

import java.lang.reflect.Field;
import java.text.ParseException;
import java.util.*;
import java.util.regex.Matcher;

abstract public class RowFixture extends Fixture {

    protected List<FitRow> missing = new LinkedList<>();
    protected List<Object> surplus = new LinkedList<>();
    private String[] columnParameters;
    private String[] columnNames;
    private FitTable table;

    /**
     * get rows to be compared
     * @return Actual values to be compared with the HTML content.
     * @throws Exception when querying for object fails.
     */
    abstract protected Object[] query() throws Exception;

    /**
     * get expected type of row
     *
     * @return Class which is processed by this RowFixture.
     */
    abstract protected Class getTargetClass();

    @Override
    public void doTable(FitTable table) {
        this.table = table;
        super.doTable(table);
    }

    protected void doRows(List<FitRow> rows) throws Exception {
        FitRow header = rows.get(0);
        columnParameters = extractColumnParameters(header);
        columnNames = findColumnNames(header);

        List<FitRow> expected = new LinkedList<>(rows.subList(1, rows.size()));
        List<Object> computed = new LinkedList<>(Arrays.asList(query()));
        multiLevelMatch(expected, computed, 0);

        appendSurplusRows();
        markMissingRows();
    }

    protected String[] findColumnNames(FitRow heads) {
        String[] names = new String[heads.size()];
        int i = 0;

        for (FitCell fitCell : heads.cells()) {
            names[i++] = validator.preProcess(fitCell);
        }

        return names;
    }

    /**
     * Sorts and compare two lists.
     * It is assumed that all items in {@code expected} and {@code computed}
     * are equal on the {@code col - 1} columns.
     */
    private void multiLevelMatch(List<FitRow> expected, List<Object> computed, int col) {
        boolean cantGoDeeper = col >= columnNames.length;
        if (cantGoDeeper) {
            check(expected, computed);
        } else {
            boolean isComment = isComment(col);
            if (isComment) {
                multiLevelMatch(expected, computed, col + 1);
            } else {
                groupAndMatch(expected, computed, col);
            }
        }
    }

    private boolean isComment(int col) {
        return columnNames[col] == null || columnNames[col].isEmpty();
    }

    /**
     * Groups both lists by column {@code col}. For every resulting group check if
     * there is a 1:1 or a 1:0 match. Otherwise, group further.
     */
    private void groupAndMatch(List<FitRow> expected, List<Object> computed, int col) {
        Map<Object, List<FitRow>> expectedMap = groupExpectedByColumn(expected, col);
        Map<Object, List<Object>> computedMap = groupComputedByColumn(computed, col);
        Set keys = union(expectedMap.keySet(), computedMap.keySet());

        for (Object key : keys) {
            List<FitRow> expectedList = expectedMap.get(key);
            List<Object> computedList = computedMap.get(key);

            boolean isAmbiguous = hasMultipleEntries(expectedList) && hasMultipleEntries(computedList);

            if (isAmbiguous) {
                multiLevelMatch(expectedList, computedList, col + 1);
            } else {
                check(expectedList, computedList);
            }
        }
    }

    private boolean hasMultipleEntries(List<?> expectedList) {
        return expectedList != null && expectedList.size() > 1;
    }

    private Map<Object, List<FitRow>> groupExpectedByColumn(List<FitRow> list, int col) {
        Map<Object, List<FitRow>> result = new HashMap<>(list.size());

        for (FitRow row : list) {
            List<FitCell> cells = row.cells();

            FitCell keyCell = cells.get(col);
            try {
                Object key = parseCell(col, keyCell);
                addToMap(result, key, row);
            } catch (Exception e) {
                keyCell.exception(e);
                for (int i = col + 1; i < cells.size(); i++) {
                    cells.get(i).ignore();
                }
            }
        }
        return result;
    }

    private Object parseCell(int col, FitCell cell) throws NoSuchMethodException, NoSuchFieldException, ParseException {
        String preprocessedText = validator.preProcess(cell);
        String parameter = FitUtils.saveGet(col, columnParameters);
        Class<?> columnType = getColumnType(columnNames[col]);
        TypeHandler typeHandler = typeHandlerFactory.getHandler(columnType, parameter);
        return typeHandler.parse(preprocessedText);
    }

    private Map<Object, List<Object>> groupComputedByColumn(List<Object> list, int col) {
        Map<Object, List<Object>> result = new HashMap<>(list.size());

        for (Object row : list) {
            try {
                Object key = createReceiver(row, columnNames[col]).get();
                addToMap(result, key, row);
            } catch (Exception e) {
                // surplus anything with bad keys, including null
                surplus.add(row);
            }
        }
        return result;
    }

    private <T> void addToMap(Map<Object, List<T>> map, Object key, T row) {
        if (key.getClass().isArray()) {
            key = Arrays.asList((Object[]) key);
        }

        if (map.containsKey(key)) {
            map.get(key).add(row);
        } else {
            List<T> list = new LinkedList<>();
            list.add(row);
            map.put(key, list);
        }
    }

    private <T> Set<T> union(Set<T> a, Set<T> b) {
        Set<T> result = new HashSet<>(a);
        result.addAll(b);
        return result;
    }

    /**
     * Compares two lists.
     *

     *
if {@code expectedList} is empty, all {@code computedList} items are surplus

     *
if {@code computedList} is empty, all {@code expectedList} items are missing

     *
otherwise, match the first rows and compare the rest recursively

     *

     *
     * @param computedList Objects from {@link #query()}.
     * @param expectedList Objects from HTML.
     */
    protected void check(List<FitRow> expectedList, List<Object> computedList) {
        if (expectedList == null || expectedList.size() == 0) {
            surplus.addAll(computedList);
        } else if (computedList == null || computedList.size() == 0) {
            missing.addAll(expectedList);
        } else {
            Object computedRow = computedList.remove(0);
            List<FitCell> expectedRow = expectedList.remove(0).cells();
            compareRow(computedRow, expectedRow);

            check(expectedList, computedList);
        }
    }

    /**
     * Compares two rows item by item using {@link Fixture#check(FitCell, ValueReceiver, String)}.
     * Each cell will be marked as right, wrong, ignored or exception
     */
    private void compareRow(Object computedRow, List<FitCell> expectedCells) {
        for (int i = 0; i < columnNames.length && expectedCells != null; i++) {
            try {
                ValueReceiver valueReceiver;
                if (isComment(i)) {
                    valueReceiver = null;
                } else {
                    valueReceiver = createReceiver(computedRow, columnNames[i]);
                }

                String columnParameter = FitUtils.saveGet(i, columnParameters);
                check(expectedCells.get(i), valueReceiver, columnParameter);
            } catch (NoSuchMethodException | NoSuchFieldException e) {
                expectedCells.indexOf(e);
            }
        }
    }

    private Class<?> getColumnType(String name) throws NoSuchMethodException, NoSuchFieldException {
        Matcher matcher = ValueReceiverFactory.METHOD_PATTERN.matcher(name);

        if (matcher.find()) {
            final String methodName = FitUtils.camel(matcher.group(1));
            return getTargetClass().getMethod(methodName).getReturnType();
        } else {
            final Field field = getTargetClass().getField(FitUtils.camel(name));
            return field.getType();
        }
    }

    private void appendSurplusRows() {
        for (Object row : surplus) {
            FitRow fitRow = table.appendRow();
            buildRow(fitRow, row, "surplus");
        }
    }

    private void buildRow(FitRow fitRow, Object row, String info) {
        if (row == null) {
            fitRow.insert(0).blank("null", columnNames.length);
            fitRow.wrong(info);
        } else {
            fitRow.wrong(info);
            for (int i = 0; i < columnNames.length; i++) {
                FitCell cell = fitRow.insert(i);
                buildCell(cell, row, i);
            }
        }
    }

    private void buildCell(FitCell cell, Object row, int i) {
        if (columnNames[i] == null) {
            cell.ignore();
        } else {
            try {
                ValueReceiver receiver = createReceiver(row, columnNames[i]);
                String parameter = FitUtils.saveGet(i, columnParameters);
                TypeHandler typeHandler = createTypeHandler(receiver, parameter);
                cell.setDisplayValue(typeHandler.toString(receiver.get()));
                //cell.ignore();
            } catch (Exception e) {
                cell.exception(e);
            }
        }
    }

    private void markMissingRows() {
        markRowsAs(missing.iterator(), "missing");
    }

    private void markRowsAs(Iterator<FitRow> rows, String message) {
        while (rows.hasNext()) {
            FitRow row = rows.next();
            preprocessMissingCells(row);

            row.wrong(message);
        }
    }

    private void preprocessMissingCells(FitRow row) {
        for (int i = 0; i < row.size(); i++) {
            FitCell cell = row.cells().get(i);
            Object preprocessed = validator.preProcess(cell);

            if (isComment(i)) {
                cell.setDisplayValue(Objects.toString(preprocessed));
            } else {
                String parameter = FitUtils.saveGet(i, columnParameters);
                try {
                    Class<?> columnType = getColumnType(columnNames[i]);
                    TypeHandler handler = typeHandlerFactory.getHandler(columnType, parameter);
                    cell.setDisplayValue(handler.toString(preprocessed));
                } catch (NoSuchMethodException | NoSuchFieldException e) {
                    cell.setDisplayValue(Objects.toString(preprocessed));
                }
            }
        }
    }
}


/*
 * Copyright 2010 akquinet
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.akquinet.chameria.launcher;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class SubstVars {

    private static final String DELIM_START = "${";
    private static final String DELIM_STOP  = "}";

    /**
     * <p>
     * This method performs property variable substitution on the specified
     * value. If the specified value contains the syntax
     * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt> refers to
     * either a configuration property or a system property, then the
     * corresponding property value is substituted for the variable placeholder.
     * Multiple variable placeholders may exist in the specified value as well
     * as nested variable placeholders, which are substituted from inner most to
     * outer most. Configuration properties override system properties.
     * </p>
     *
     * @param val
     *            The string on which to perform property substitution.
     * @param currentKey
     *            The key of the property being evaluated used to
     *            detect cycles.
     * @param cycleMap
     *            Map of variable references used to detect nested cycles.
     * @param configProps
     *            Set of configuration properties.
     * @return The value of the specified string after system property
     *         substitution.
     * @throws IllegalArgumentException
     *             If there was a syntax error in the
     *             property placeholder syntax or a recursive variable
     *             reference.
     **/
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static String substVars(String val, String currentKey,
            Map cycleMap, Properties configProps)
            throws IllegalArgumentException {
        // If there is currently no cycle map, then create
        // one for detecting cycles for this invocation.
        if (cycleMap == null) {
            cycleMap = new HashMap();
        }

        // Put the current key in the cycle map.
        cycleMap.put(currentKey, currentKey);

        // Assume we have a value that is something like:
        // "leading ${foo.${bar}} middle ${baz} trailing"

        // Find the first ending '}' variable delimiter, which
        // will correspond to the first deepest nested variable
        // placeholder.
        int stopDelim = -1;
        int startDelim = -1;

        do {
            stopDelim = val.indexOf(DELIM_STOP, stopDelim + 1);
            // If there is no stopping delimiter, then just return
            // the value since there is no variable declared.
            if (stopDelim < 0) {
                return val;
            }
            // Try to find the matching start delimiter by
            // looping until we find a start delimiter that is
            // greater than the stop delimiter we have found.
            startDelim = val.indexOf(DELIM_START);
            // If there is no starting delimiter, then just return
            // the value since there is no variable declared.
            if (startDelim < 0) {
                return val;
            }
            while (stopDelim >= 0) {
                int idx =
                        val.indexOf(DELIM_START, startDelim
                                + DELIM_START.length());
                if ((idx < 0) || (idx > stopDelim)) {
                    break;
                }
                else if (idx < stopDelim) {
                    startDelim = idx;
                }
            }
        } while ((startDelim > stopDelim) && (stopDelim >= 0));

        // At this point, we have found a variable placeholder so
        // we must perform a variable substitution on it.
        // Using the start and stop delimiter indices, extract
        // the first, deepest nested variable placeholder.
        String variable =
                val.substring(startDelim + DELIM_START.length(), stopDelim);

        // Verify that this is not a recursive variable reference.
        if (cycleMap.get(variable) != null) {
            throw new IllegalArgumentException(
                    "recursive variable reference: " + variable);
        }

        // Get the value of the deepest nested variable placeholder.
        // Try to configuration properties first.
        String substValue = (configProps != null)
                ? configProps.getProperty(variable, null)
                : null;
        if (substValue == null) {
            // Ignore unknown property values.
            substValue = System.getProperty(variable, "");
        }

        // Remove the found variable from the cycle map, since
        // it may appear more than once in the value and we don't
        // want such situations to appear as a recursive reference.
        cycleMap.remove(variable);

        // Append the leading characters, the substituted value of
        // the variable, and the trailing characters to get the new
        // value.
        val = val.substring(0, startDelim)
                + substValue
                + val.substring(stopDelim + DELIM_STOP.length(), val.length());

        // Now perform substitution again, since there could still
        // be substitutions to make.
        val = substVars(val, currentKey, cycleMap, configProps);

        // Return the value.
        return val;
    }

}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies 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 FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
*/

package de.cologneintelligence.fitgoodies.typehandler;

import java.lang.reflect.Array;
import java.text.ParseException;
import java.util.StringTokenizer;

/**
 * If the destination type is an array, this adapter handles it.
 * It splits the input at commas and converts each part into an array element.
 */
public class ArrayTypeHandler extends TypeHandler {
	/**
	 * {@code TypeAdapter} which handles the array items.
	 */
	private TypeHandler componentAdapter;

	/**
	 * Creates a new TypeAdapter which can read arrays.
	 * In contrast to the fit TypeAdapter, this one is able to process registered
	 * Types of array.
	 *
	 * @param convertParameter column/row parameter
	 * @param componentAdapter adapter to parse the array content
	 */
	public ArrayTypeHandler(final String convertParameter, TypeHandler componentAdapter) {
		super(convertParameter);
		this.componentAdapter = componentAdapter;
	}

	@Override
	public Class getType() {
		return null;
	}

	/**
	 * Converts a {@code String} into a {@code Array}.
	 *
	 * @param s the {@code String} to convert
	 * @return an {@code Array} which contains {@code s}
	 */
	@Override
	public Object unsafeParse(final String s) throws ParseException {
		final StringTokenizer t = new StringTokenizer(s, ",");
		final Object array = Array.newInstance(componentAdapter.getType(), t.countTokens());
		for (int i = 0; t.hasMoreTokens(); i++) {
			Array.set(array, i, componentAdapter.parse(t.nextToken().trim()));
		}
		return array;
	}

	/**
	 * Returns the String representation of the array. The result is a comma
	 * separated list of all items. The items are converted to Strings using
	 * an item-specific type adapter.
	 *
	 * @param o Array to convert
	 * @return {@code o} as a String
	 */
	@SuppressWarnings("unchecked")
	@Override
	public String toString(final Object o) {
		if (o == null) {
			return "";
		}

		final int length = Array.getLength(o);
		final StringBuilder b = new StringBuilder(5 * length);
		for (int i = 0; i < length; i++) {
			b.append(componentAdapter.toString(Array.get(o, i)));
			if (i < length - 1) {
				b.append(", ");
			}
		}
		return b.toString();
	}

	/**
	 * Checks whether two Arrays {@code a} and {@code b} are
	 * equal.
	 * <p>
	 * A type specific {@code TypeAdapter} is used to compare the items.
	 *
	 * @param a first array
	 * @param b second array
	 * @return {@code true} if all items are equal, {@code false} otherwise
	 */
	@SuppressWarnings("unchecked")
	@Override
	public boolean unsafeEquals(Object a, Object b) {
		final int length = Array.getLength(a);
		if (length != Array.getLength(b)) {
			return false;
		}
		for (int i = 0; i < length; i++) {
			if (!componentAdapter.unsafeEquals(Array.get(a, i), Array.get(b, i))) {
				return false;
			}
		}
		return true;
	}
}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies 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 FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.cologneintelligence.fitgoodies.log4j;

import de.cologneintelligence.fitgoodies.Validator;
import de.cologneintelligence.fitgoodies.htmlparser.FitCell;
import org.apache.log4j.spi.LoggingEvent;

/**
 * Factory interface to provide {@link LogEventAnalyzer}s.
 */
public class LogEventAnalyzerFactory {
    /**
     * Creates a LogEventAnalyzer which is capable to analyze the events {@code
     * events} using the condition defined in {@code conditionCell}.
     *
     * @param validator     the validator for the cell
     * @param conditionCell cell which contains the condition
     * @param events        list of events to process   @return instance of LogEventAnalyzer
     * @return A LogEventAnalyzer able to processing all parameters.
     */
    public LogEventAnalyzer getLogEventAnalyzerFor(Validator validator, FitCell conditionCell,
                                                   LoggingEvent[] events) {
        return new LogEventAnalyzer(validator, conditionCell, events);
    }

}


package js.mvc;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;

import js.http.ContentType;
import js.template.Template;
import js.template.TemplateEngine;
import js.util.Classes;

/**
 * X(HT)ML Server Page - server generated pages from X(HT)ML templates. This class implements a {@link View} based on (X)HTML
 * {@link Template} processed on server side. In essence this class serialize back to client a (X)HTML page template with model
 * injected.
 *

 * XSP view uses {@link TemplateEngine} to inject model and serialize view to HTTP response. It is developer and deployer
 * responsibility to ensure that view templates syntax is compatible with template engine implementation. Template engine is a
 * Java service and its implementation is loaded at run-time.
 *

 * XSP view should be declared into views section from application descriptor. In sample configuration it is also
 * enabled operators serialization. By default operators serialization is disabled.
 *
 *

 * <views>
 * 	<repository path='templates/path' files-pattern='*.htm'>
 * 		<property name='template.operator.serialization' value='true' />
 * 	</repository>
 * </views>
 *

 *
 * @author Iulian Rotaru
 * @version final
 */
final class XspView extends AbstractView {
	/** Key for operator serialization property. */
	private static final String OPERATOR_SERIALIZATION = "template.operator.serialization";

	@Override
	protected ContentType getContentType() {
		return ContentType.TEXT_HTML;
	}

	/**
	 * Uses template engine to inject model and serialize view on HTTP response. If meta-view contains
	 * {@link #OPERATOR_SERIALIZATION} property enable engine operators serialization.
	 *
	 * @param outputStream output stream of the HTTP response.
	 * @throws IOException if stream write operation fails.
	 */
	@Override
	protected void serialize(OutputStream outputStream) throws IOException {
		// refrain to use HttpResponse#getWriter()
		// this library always uses output stream since servlet response API does not allow mixing characters and bytes streams
		Writer writer = new BufferedWriter(new OutputStreamWriter(outputStream, "UTF-8"));

		TemplateEngine templateEngine = Classes.loadService(TemplateEngine.class);
		Template template = templateEngine.getTemplate(meta.getTemplateFile());
		boolean operatorSerialization = Boolean.parseBoolean(meta.getProperty(OPERATOR_SERIALIZATION));
		if (operatorSerialization) {
			template.setProperty("js.template.serialize.operator", true);
		}
		// if model is null template serializes itself skipping injection operation
		template.serialize(model, writer);

		// there is no need to flush response stream on error so no need for try/finally
		writer.flush();
	}
}


/*
 * Copyright 2010 akquinet
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.akquinet.chameria.services;

/**
 * Helper class to parse arguments.
 * This class supports:
 *

 *
arg value arguments

 *
arg=value arguments

 *

 * If the argument starts with '-', the given argument must contain it.
 */
public class ActivationUtils {

    /**
     * Checks if the arguments contains the argument arg.
     * @param args the arguments
     * @param arg the argument to find
     * @return true if the
     */
    public static boolean containsArgument(String[] args, String arg) {
         if (args == null || arg == null || arg.length() == 0 || args.length == 0) {
             return false;
         } else {
             for (String a : args) {
                 if (a.equalsIgnoreCase(arg)) {
                     return true;
                 } else if (a.startsWith(arg + "=")) {
                     return true;
                 }

             }
             return false;
         }
    }

    /**
     * Gets the argument value.
     *

     *
for 'arg=value' it returns 'value'

     *
for 'arg value' it returns 'value'

     * @param args the arguments
     * @param arg the argument to find
     * @return the argument value or null if not found.
     */
    public static String getArgumentValue(String args[], String arg) {
        if (args == null || arg == null || arg.length() == 0 || args.length == 0) {
            return null;
        } else {
            for (int i = 0; i < args.length; i++) {
                String a = args[i];
                if (a.equalsIgnoreCase(arg)) {
                    // Case 'arg value' : Look for arg + 1;
                    if (args.length > i + 1) {
                        return args[i+1];
                    }

                } else if (a.startsWith(arg + "=")) {
                    // Case 'arg=value' : Parse the value
                    int index = a.indexOf('=') + 1;
                    return a.substring(index);
                }
            }

            return null;
        }
    }

    /**
     * Gets the value of the '-open' argument.
     * @param args the arguments
     * @return the value of the '-open' argument or
     * null if not found.
     */
    public static String getOpenArgument(String args[]) {
        return getArgumentValue(args, "-open");
    }

}


package js.container;

import js.converter.Converter;
import js.converter.ConverterException;
import js.util.Params;

/**
 * Managed instance types. Instance type is used as selector for {@link InstanceFactory} strategy, that is, it selects the right
 * instance factory to use for managed instance creation.
 *

 * Instance type is configured into managed class descriptor, type attribute, with default value to {@link #POJO}.
 * In sample descriptor, managed request context class has {@link #POJO} scope; as a consequence {@link LocalInstanceFactory}
 * will be used for managed instance creation. It is developer responsibility to use a supported type value
 * otherwise managed class initialization fails with application abort.
 *
 *

 * <request-context class="js.servlet.RequestContext" type="POJO" />
 *

 *
 *

 * There are predefined instance type constants but user defined instance scopes are supported. Anyway, adding a new scope
 * implies also creating a related {@link InstanceFactory}.
 *

 *

 * Type
 * Factory
 * Description
 *

 * POJO
 * {@link LocalInstanceFactory}
 * Plain Old Java Object. This type of managed objects are standard java instances; they support only life cycle management
 * and dependency injection but no other services like declarative transaction, provided by {@link #PROXY}. Factory simple
 * returns implementation instance and method invocation is resolved directly by virtual machine.
 *

 * PROXY
 * {@link LocalInstanceFactory}
 * Managed Proxy. Application factory replaces implementation class with a dynamically generated Java Proxy which in turn
 * invokes implementation methods using reflection. This, of course, comes with a small overhead; please consider {@link #POJO}
 * alternative if container services are not needed.
 *

 * REMOTE
 * {@link RemoteInstanceFactory}
 * Managed instance on foreign server. Managed class descriptor should include the URL of the context where remote class is
 * deployed. When create remote types, application factory creates a Java Proxy that relay method invocation via HTTP-RMI to
 * remote implementation.
 *

 * SERVICE
 * {@link ServiceInstanceFactory}
 * External service loaded by Java service loader. In this context external means outside application scope but in
 * the same virtual machine.
 *

 *
 *

 * Warning: use a supported type value into managed class descriptor or application fails to start.
 *
 * @author Iulian Rotaru
 * @version final
 */
public class InstanceType implements Converter {
	/**
	 * Plain Old Java Object. This type of managed objects are standard java instances; they support only life cycle management
	 * and dependency injection but no other services like declarative transaction, provided by {@link #PROXY}. Factory simple
	 * returns implementation instance and method invocation is resolved directly by virtual machine.
	 */
	public static final InstanceType POJO = new InstanceType("POJO");

	/**
	 * Managed Proxy. Application factory replaces implementation class with a dynamically generated Java Proxy which in turn
	 * invokes implementation methods using reflection. This, of course, comes with a small overhead; please consider
	 * {@link #POJO} alternative if container services are not needed.
	 */
	public static final InstanceType PROXY = new InstanceType("PROXY");

	/**
	 * Managed instance on foreign server. Managed class descriptor should include the URL of the context where remote class is
	 * deployed. When create remote types, application factory creates a Java Proxy that relay method invocation via HTTP-RMI to
	 * remote implementation.
	 */
	public static final InstanceType REMOTE = new InstanceType("REMOTE");

	/**
	 * External service loaded by Java service loader. In this context external means outside application scope but in
	 * the same virtual machine.
	 */
	public static final InstanceType SERVICE = new InstanceType("SERVICE");

	// --------------------------------------------------------------------------------------------
	// INSTANCE

	/** Instance type value. */
	private final String value;

	/** Default constructor for converter instance. */
	public InstanceType() {
		this.value = null;
	}

	/**
	 * Create immutable instance type.
	 *
	 * @param value instance type value.
	 * @throws IllegalArgumentException if instance type value is null.
	 */
	public InstanceType(String value) {
		Params.notNull(value, "Instance type value");
		this.value = value;
	}

	/**
	 * Get instance type value.
	 *
	 * @return instance type value.
	 */
	public String getValue() {
		return value;
	}

	/**
	 * Test if this instance type requires interface on managed class descriptor. If this predicate returns true managed class
	 * descriptor should have interface attribute. More, interface value should be strict Java
	 * interface.
	 *
	 * @return true if this instance type requires interface declaration.
	 */
	public boolean requiresInterface() {
		return PROXY.value.equals(value) || REMOTE.value.equals(value);
	}

	/**
	 * Test if this instance type requires class implementation. If this predicate returns true managed class descriptor should
	 * have class attribute. Implementation is used reflexively to create managed instances. This is in contrast
	 * with the other types that loads instances using external means.
	 *
	 * @return true if this instance type requires class implementation.
	 */
	public boolean requiresImplementation() {
		return POJO.value.equals(value) || PROXY.value.equals(value);
	}

	/**
	 * Test if this instance type is POJO.
	 *
	 * @return true if this instance type is POJO.
	 */
	public boolean isPOJO() {
		return POJO.value.equals(value);
	}

	/**
	 * Test if this instance type is PROXY.
	 *
	 * @return true if this instance type is PROXY.
	 */
	public boolean isPROXY() {
		return PROXY.value.equals(value);
	}

	/**
	 * Test if this instance type is REMOTE.
	 *
	 * @return true if this instance type is REMOTE.
	 */
	public boolean isREMOTE() {
		return REMOTE.value.equals(value);
	}

	/**
	 * Test if this instance type is SERVICE.
	 *
	 * @return true if this instance type is SERVICE.
	 */
	public boolean isSERVICE() {
		return SERVICE.value.equals(value);
	}

	/** Instance type string representation. */
	@Override
	public String toString() {
		return value;
	}

	/** Hash code based on instance type value. */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((value == null) ? 0 : value.hashCode());
		return result;
	}

	/** Two instance types are equal if have the same values. */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		InstanceType other = (InstanceType) obj;
		if (value == null) {
			if (other.value != null)
				return false;
		} else if (!value.equals(other.value))
			return false;
		return true;
	}

	// --------------------------------------------------------------------------------------------
	// CONVERTER

	/** Create an instance type from its string value. */
	@SuppressWarnings("unchecked")
	@Override
	public <T> T asObject(String string, Class<T> valueType) throws IllegalArgumentException, ConverterException {
		return (T) new InstanceType(string);
	}

	/** Return string value of given instance type. */
	@Override
	public String asString(Object object) throws ConverterException {
		return ((InstanceType) object).getValue();
	}
}


/**
 * Copyright (C) 2015 Michael Schnell. All rights reserved.
 * http://www.fuin.org/
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option) any
 * later version.
 *
 * This library 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 Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library. If not, see http://www.gnu.org/licenses/.
 */
package org.fuin.srcgen4j.core.velocity;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
import org.apache.velocity.runtime.resource.loader.FileResourceLoader;
import org.fuin.srcgen4j.commons.GenerateException;
import org.fuin.srcgen4j.core.base.AbstractGenerator;
import org.fuin.srcgen4j.core.base.GeneratedFile;
import org.fuin.utils4j.Utils4J;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Base generator that uses velocity templates for generation.
 *
 * @param <MODEL>
 *            Type of the model.
 */
public abstract class VelocityGenerator<MODEL> extends AbstractGenerator<MODEL, VelocityGeneratorConfig> {

    /** Key for the location of the template files. */
    public static final String TEMPLATE_DIR_KEY = "templateDir";

    private static final Logger LOG = LoggerFactory.getLogger(VelocityGenerator.class);

    private VelocityEngine ve;

    private File templateDir;

    /**
     * Returns an initialized velocity engine.
     *
     * @return Engine - Never NULL after {@link #generate(boolean)} was called.
     */
    protected final VelocityEngine getVE() {
        return ve;
    }

    /**
     * Returns the template directory.
     *
     * @return Source directory.
     */
    public final File getTemplateDir() {
        return templateDir;
    }

    private VelocityEngine createVelocityEngine(final File templateDir) {
        final VelocityEngine ve = new VelocityEngine();
        if (templateDir == null) {
            ve.addProperty("resource.loader", "class");
        } else {
            ve.addProperty("resource.loader", "file, class");
            ve.addProperty("file.resource.loader.class", FileResourceLoader.class.getName());
            ve.addProperty("file.resource.loader.path", templateDir.toString());
        }
        ve.addProperty("class.resource.loader.class", ClasspathResourceLoader.class.getName());
        ve.init();
        return ve;
    }

    /**
     * Merges the template and context into a file. If the directory of the file does not exists, the full directory path to it will be
     * created.
     *
     * @param context
     *            Context to use.
     * @param artifactName
     *            Unique name of the generated artifact.
     * @param templateName
     *            Name of the template to use.
     * @param filename
     *            Filename relative to the target directory.
     *
     * @throws GenerateException
     *             Error merging the template
     */
    protected final void merge(final VelocityContext context, final String artifactName, final String templateName, final String filename)
            throws GenerateException {

        final GeneratedFile genFile = getTargetFile(artifactName, filename, templateName);
        if (genFile.isSkip()) {
            LOG.debug("Omitted already existing file: {} [{}]", genFile, templateName);
        } else {
            LOG.debug("Start merging velocity template: {} [{}]", genFile, templateName);
            // Merge content
            try {
                final Writer writer = new FileWriter(genFile.getTmpFile());
                try {
                    final Template template = ve.getTemplate(templateName);
                    template.merge(context, writer);
                } finally {
                    writer.close();
                }
                genFile.persist();

            } catch (final IOException ex) {
                throw new GenerateException("Error merging template '" + templateName + "' to '" + filename + "'!", ex);
            }
        }

    }

    @Override
    public final Class<VelocityGeneratorConfig> getSpecificConfigClass() {
        return VelocityGeneratorConfig.class;
    }

    @Override
    public final void generate(final boolean incremental) throws GenerateException {
        this.templateDir = Utils4J.getCanonicalFile(getSpecificConfig().getTemplateDir());
        this.ve = createVelocityEngine(templateDir);

        LOG.debug("Template directory: {}", templateDir);

        generateIntern();
    }

    /**
     * Generates the files from velocity templates. The method {@link #getVE()} can be used to get a ready to use velocity engine that
     * points to the template directory.
     *
     * @throws GenerateException
     *             Error generating the files.
     */
    protected abstract void generateIntern() throws GenerateException;

}