/*
  Copyright 2011-2014 Red Hat, Inc

  This file is part of PressGang CCMS.

  PressGang CCMS 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.

  PressGang CCMS 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 PressGang CCMS.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.jboss.pressgang.ccms.model;

import static javax.persistence.GenerationType.IDENTITY;

import javax.imageio.ImageIO;
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

import org.apache.commons.codec.binary.Base64;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.validator.constraints.NotBlank;
import org.imgscalr.Scalr;
import org.jboss.pressgang.ccms.model.base.AuditedEntity;
import org.jboss.pressgang.ccms.model.exceptions.CustomConstraintViolationException;
import org.jboss.pressgang.ccms.model.utils.SVGIcon;
import org.jboss.pressgang.ccms.utils.common.HashUtilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Entity
@Audited
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Table(name = "LanguageImage", uniqueConstraints = @UniqueConstraint(columnNames = {"ImageFileID", "LocaleID"}))
public class LanguageImage extends AuditedEntity implements java.io.Serializable {
    private static final Logger log = LoggerFactory.getLogger(LanguageImage.class);
    private static final long serialVersionUID = 1585978752264763594L;
    private static final String SVG_MIME_TYPE = "image/svg+xml";
    private static final String JPG_MIME_TYPE = "image/jpeg";
    private static final String GIF_MIME_TYPE = "image/gif";
    private static final String PNG_MIME_TYPE = "image/png";
    public static final String SELECT_ALL_QUERY = "SELECT languageImage FROM LanguageImage languageImage";

    /**
     * The dimensions of the generated thumbnail
     */
    private static final int THUMBNAIL_SIZE = 64;
    private Integer languageImageId;
    private ImageFile imageFile;
    private byte[] imageData;
    private byte[] thumbnail;
    private Locale locale;
    private String originalFileName;

    private byte[] uiImageData;
    private String uiOriginalFileName;
    private char[] imageContentHash;

    public LanguageImage() {
    }

    public LanguageImage(final String originalFileName, final byte[] imageData) {
        this.imageData = imageData;
        this.originalFileName = originalFileName;
    }

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "LanguageImageID", unique = true, nullable = false)
    public Integer getLanguageImageId() {
        return languageImageId;
    }

    public void setLanguageImageId(final Integer languageImageId) {
        this.languageImageId = languageImageId;
    }

    @Column(name = "OriginalFileName", length = 255)
    @Size(max = 255)
    public String getOriginalFileName() {
        return originalFileName;
    }

    public void setOriginalFileName(final String originalFileName) {
        this.originalFileName = originalFileName;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "ImageFileID", nullable = false)
    @NotNull
    public ImageFile getImageFile() {
        return imageFile;
    }

    public void setImageFile(ImageFile imageFile) {
        this.imageFile = imageFile;
    }

    @Column(name = "ImageData", columnDefinition = "mediumblob")
    public byte[] getImageData() {
        return imageData;
    }

    public void setImageData(final byte[] imageData) {
        this.imageData = imageData;
    }

    @Column(name = "ThumbnailData", columnDefinition = "mediumblob")
    public byte[] getThumbnailData() {
        return thumbnail;
    }

    public void setThumbnailData(final byte[] thumbnail) {
        this.thumbnail = thumbnail;
    }

    @Transient
    public String getThumbnailDataString() {
        return thumbnail == null ? "" : new String(thumbnail);
    }

    private byte[] createImage(final boolean resize) {
        if (imageData == null) return null;

        try {
            BufferedImage outImage = null;

            if (getMimeType().equals(SVG_MIME_TYPE)) {
                SVGIcon svgIcon = null;
                if (resize) {
                    svgIcon = new SVGIcon(new ByteArrayInputStream(imageData), THUMBNAIL_SIZE, THUMBNAIL_SIZE);
                } else {
                    svgIcon = new SVGIcon(new ByteArrayInputStream(imageData));
                }

                outImage = new BufferedImage(svgIcon.getIconWidth(), svgIcon.getIconHeight(), BufferedImage.TYPE_INT_RGB);
                final Graphics2D g2d = outImage.createGraphics();

                svgIcon.setBackgroundColour(Color.WHITE);
                svgIcon.paintIcon(null, g2d, 0, 0);
                g2d.dispose();
            } else {
                final BufferedImage img = ImageIO.read(new ByteArrayInputStream(imageData));
                outImage = Scalr.resize(img, Scalr.Method.AUTOMATIC, THUMBNAIL_SIZE, THUMBNAIL_SIZE, Scalr.OP_ANTIALIAS);
            }

            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(outImage, getFormatName(), baos);
            final byte[] bytesOut = baos.toByteArray();

            return Base64.encodeBase64(bytesOut);
        } catch (final Exception ex) {
            log.error("An error creating an image thumbnail", ex);
        }

        return null;
    }

    @Transient
    public String getImageDataString() {
        if (imageData == null) return "";
        return new String(imageData);
    }

    @Override
    @Transient
    public Integer getId() {
        return languageImageId;
    }

    /**
     * Create the thumbnails, and make sure the parent imagefile is valid.
     *
     * @throws CustomConstraintViolationException
     *
     */
    @PrePersist
    @PreUpdate
    private void updateImageData() throws CustomConstraintViolationException {
        thumbnail = createImage(true);
        imageFile.validate();
        if (imageData != null) {
            imageContentHash = HashUtilities.generateSHA256(imageData).toCharArray();
        }
    }

    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "LocaleID")
    @NotNull(message = "{languageimage.locale.notBlank}")
    @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
    public Locale getLocale() {
        return locale;
    }

    public void setLocale(Locale locale) {
        this.locale = locale;
    }

    @Transient
    public String getMimeType() {
        final String extension = getExtension();
        if (extension != null) {
            if (extension.equalsIgnoreCase("JPG")) return JPG_MIME_TYPE;
            if (extension.equalsIgnoreCase("GIF")) return GIF_MIME_TYPE;
            if (extension.equalsIgnoreCase("PNG")) return PNG_MIME_TYPE;
            if (extension.equalsIgnoreCase("SVG")) return SVG_MIME_TYPE;
        }

        return "application/octet-stream";
    }

    @Transient
    public String getExtension() {
        final int lastPeriodIndex = originalFileName.lastIndexOf(".");
        if (lastPeriodIndex != -1 && lastPeriodIndex < originalFileName.length() - 1) {
            return originalFileName.substring(lastPeriodIndex + 1);
        }

        return null;
    }

    @Transient
    private String getFormatName() {
        final String extension = getExtension();
        if (extension != null) {
            if (extension.equalsIgnoreCase("png")) {
                return "PNG";
            } else if (extension.equalsIgnoreCase("gif")) {
                return "GIF";
            }
        }

        return "JPG";
    }

    @Transient
    public byte[] getUiImageData() {
        return uiImageData;
    }

    /**
     * The UI will attempt to assign a null value if the file upload box does not have a file selected. This method is used to
     * ignore any null values, while passing through any legitimate file uploads. This means that the absence of a file in the
     * upload box does not indicate that no file should be assigned to the entity.
     *
     * @param uiImageData The image data uploaded through the UI
     */
    public void setUiImageData(byte[] uiImageData) {
        this.uiImageData = uiImageData;
        if (this.uiImageData != null) imageData = this.uiImageData;
    }

    @Transient
    public String getUiOriginalFileName() {
        return uiOriginalFileName;
    }

    /**
     * The UI will attempt to assign an empty value if the file upload box does not have a file selected. This method is used to
     * ignore any empty values, while passing through any legitimate file uploads. This means that the absence of a file in the
     * upload box does not indicate that no file should be assigned to the entity.
     *
     * @param uiOriginalFileName The image file name uploaded through the UI
     */
    public void setUiOriginalFileName(final String uiOriginalFileName) {
        this.uiOriginalFileName = uiOriginalFileName;
        if (this.uiOriginalFileName != null && !this.uiOriginalFileName.isEmpty()) originalFileName = this.uiOriginalFileName;
    }

    @Transient
    public byte[] getImageDataBase64() {
        return imageData == null ? null : Base64.encodeBase64(imageData);
    }

    @Column(name = "ImageContentHash", columnDefinition = "CHAR(64)")
    @Size(max = 64, min = 64)
    public char[] getImageContentHash() {
        return imageContentHash;
    }

    public void setImageContentHash(final char[] imageContentHash) {
        this.imageContentHash = imageContentHash;
    }
}


/*
 * Icegem, Extensions library for VMWare vFabric GemFire
 *
 * Copyright (c) 2010-2011, Grid Dynamics Consulting Services Inc. or third-party
 * contributors as indicated by the @author tags or express copyright attribution
 * statements applied by the authors.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License v3, as published by the Free Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the GNU Lesser General Public License v3
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package com.googlecode.icegem.query.bucketoriented;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

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

import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.execute.FunctionException;
import com.gemstone.gemfire.cache.execute.FunctionService;
import com.gemstone.gemfire.cache.query.QueryException;
import com.gemstone.gemfire.cache.query.SelectResults;
import com.gemstone.gemfire.cache.query.internal.ResultsCollectionWrapper;
import com.gemstone.gemfire.cache.query.types.ObjectType;

/**
 * Query service that allows to execute OQL queries on a specified set of buckets.
 * This service can be used both on client and server/peer sides.
 * <p>
 * Note: this service works only on partition regions.
 *
 * @author Andrey Stepanov aka standy
 */
public class BucketOrientedQueryService {
    /** Field logger. */
    private static Logger logger = LoggerFactory.getLogger(BucketOrientedQueryService.class);

    /**
     * Executes a particular query on specified region using a set of keys that represents buckets. The set of buckets is
     * determined by keys of entries that are stored in such buckets: - real and fake keys can be used (such key should
     * have the same routing object as bucket's keys have); - it will be enough to specify one key for each bucket. Work
     * of this method is based on execution of function.
     *
     * @see BucketOrientedQueryFunction
     * @param queryString OQL query string.
     * @param region Partitioned region on which query will be executed.
     * @param keys Set of keys that specify buckets.
     * @return Query results as instance of {@link SelectResults}.
     * @throws QueryException When in case of query execution failure.
     */
    @SuppressWarnings({ "unchecked" })
    public static SelectResults<Object> executeOnBuckets(String queryString, Region region, Set<Object> keys)
	    throws QueryException {
	return executeOnBuckets(queryString, null, region, keys);
    }

    /**
     * Executes a particular query with parameters on specified region using a set of keys that represents buckets. The
     * set of buckets is determined by keys of entries that are stored in such buckets: - real and fake keys can be used
     * (such key should have the same routing object as bucket's keys have); - it will be enough to specify one key for
     * each bucket. Work of this method is based on execution of function.
     *
     * @see BucketOrientedQueryFunction
     * @param queryString OQL query string.
     * @param queryParameters Query parameters.
     * @param region Partitioned region on which query will be executed.
     * @param keys Set of keys that specify buckets.
     * @return Query results as instance of {@link SelectResults}.
     * @throws QueryException When in case of query execution failure.
     */
    @SuppressWarnings({ "unchecked" })
    public static SelectResults<Object> executeOnBuckets(String queryString, Object[] queryParameters, Region region,
	    Set<Object> keys) throws QueryException {
	if ((queryString == null) || (queryString.length() == 0)) {
	    throw new QueryException("You must specify query string for execution.");
	}

	int limit = extractLimit(queryString);

	BucketOrientedQueryFunctionArgument functionArgument = new BucketOrientedQueryFunctionArgument(queryString,
		queryParameters);

	BucketOrientedQueryFunction function = new BucketOrientedQueryFunction();

	FunctionService.registerFunction(function);

	List<List<Object>> queryResults;

	try {
	    queryResults = (List<List<Object>>) FunctionService.onRegion(region).withFilter(keys)
		    .withArgs(functionArgument).execute(function).getResult();
	} catch (FunctionException e) {
	    logger.error("Failed to execute bucket oriented query function: " + function, e);

	    throw new QueryException(e.getMessage());
	}

	return formatSelectResults(queryResults, limit);
    }

    /**
     * Extracts limit value from query string.
     *
     * @param queryString OQL query string.
     * @return Value of 'limit' clause.
     */
    private static int extractLimit(String queryString) {
	int limitIndex = queryString.lastIndexOf("limit");

	if (limitIndex == -1) {
	    limitIndex = queryString.lastIndexOf("LIMIT");
	}

	if (limitIndex == -1) {
	    return limitIndex;
	}

	String limitValue = queryString.substring(limitIndex + 5);

	return Integer.parseInt(limitValue.trim());
    }

    /**
     * Collects and formats query results into SelectResults. Limits query results based on limit value.
     *
     * @param queryResults Query results from several nodes.
     * @param limit Query results limit.
     * @return Aggregated query results represented by instance of {@link SelectResults}.
     */
    @SuppressWarnings({ "unchecked" })
    private static SelectResults<Object> formatSelectResults(List<List<Object>> queryResults, int limit) {
	List<Object> list = new ArrayList<Object>();

	ObjectType baseElementType = null;

	for (List<Object> queryResult : queryResults) {
	    ObjectType elementType = (ObjectType) queryResult.remove(queryResult.size() - 1);

	    if (baseElementType == null) {
		baseElementType = elementType;
	    } else if (!baseElementType.equals(elementType)) {
		throw new IllegalStateException("Collection types for query result are different.");
	    }

	    list.addAll(queryResult);

	    if (limit != -1 && list.size() >= limit) {
		break;
	    }
	}

	return limit == -1 ? new ResultsCollectionWrapper(baseElementType, list) : new ResultsCollectionWrapper(
		baseElementType, list, limit);
    }
}


/*
 * Icegem, Extensions library for VMWare vFabric GemFire
 *
 * Copyright (c) 2010-2011, Grid Dynamics Consulting Services Inc. or third-party
 * contributors as indicated by the @author tags or express copyright attribution
 * statements applied by the authors.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License v3, as published by the Free Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the GNU Lesser General Public License v3
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package com.googlecode.icegem.serialization.codegen;

import java.io.InvalidClassException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;

import static java.util.Arrays.asList;

/**
 * @author igolovach
 */
// TODO: Complete this class.
public class RTSupport {

    private static final Set<Class<?>> JDK_ALLOWED_CLASSES;

    static {
        //todo:
        //        JDK_ALLOWED_CLASSES.add(BigInteger.class);
        //        JDK_ALLOWED_CLASSES.add(BigDecimal.class);
        //        //
        //        JDK_ALLOWED_CLASSES.add(Calendar.class);
        //        JDK_ALLOWED_CLASSES.add(Locale.class);
        //        JDK_ALLOWED_CLASSES.add(TimeZone.class);
        //        JDK_ALLOWED_CLASSES.add(GregorianCalendar.class);
        //        //
        //        JDK_ALLOWED_CLASSES.add(Currency.class);
        //        JDK_ALLOWED_CLASSES.add(UUID.class);
        //        JDK_ALLOWED_CLASSES.add(URL.class);
        //        JDK_ALLOWED_CLASSES.add(URI.class);
        JDK_ALLOWED_CLASSES = new HashSet<Class<?>>();
        //
        JDK_ALLOWED_CLASSES.addAll(asList(byte.class, short.class, char.class, int.class, long.class, float.class, double.class));
        JDK_ALLOWED_CLASSES.addAll(asList(Byte.class, Short.class, Character.class, Integer.class, Long.class, Float.class, Double.class));
        //
        JDK_ALLOWED_CLASSES.addAll(asList(String.class, Date.class));
        //
        JDK_ALLOWED_CLASSES.addAll(asList(ArrayList.class, LinkedList.class, HashMap.class, TreeMap.class, IdentityHashMap.class, LinkedHashSet.class, HashSet.class, TreeSet.class));
        //
        JDK_ALLOWED_CLASSES.addAll(asList(Hashtable.class, Vector.class, Stack.class));
    }

    public static void checkAllowedInCompileTime(Class<?> clazz) throws InvalidClassException {
        while (clazz.isArray()) {
            clazz = clazz.getComponentType();
        }
        if (clazz.getName().startsWith("java.") || clazz.getName().startsWith("javax.")) {
            checkAllowedInCompileTimeJdkType(clazz);
        } else {
            checkAllowedInCompileTimeCustomType(clazz);
        }
    }

    private static void checkAllowedInCompileTimeJdkType(Class<?> clazz) throws InvalidClassException {
        if (!JDK_ALLOWED_CLASSES.contains(clazz)) {
            throw new InvalidClassException(clazz.getName() + " is disallowed, only " + JDK_ALLOWED_CLASSES);
        }
    }

    private static void checkAllowedInCompileTimeCustomType(Class<?> clazz) {
        //To change body of created methods use File | Settings | File Templates.
        // TODO: Write method body
    }

    public static void checkAllowedInRealTime(Object obj) throws InvalidClassException {
        try {
            checkAllowedInRealTime0(obj, 1); //todo: correct 1? or 0?
        } catch (RuntimeException e) {
            throw new RuntimeException(MethodFrameCounter.STACK_OVERFLOW_MSG + ". For object: " + obj.toString());
        }
    }

    /**
     * With stack counter
     *
     * @param obj
     * @param depth of
     * @throws InvalidClassException
     */
    private static void checkAllowedInRealTime0(Object obj, int depth) throws InvalidClassException {
        if (depth >= MethodFrameCounter.MAX_STACK_DEPTH) { //todo: correct >? or >=?
            throw new RuntimeException();
        }
        Class<?> clazz = obj.getClass();

        if (clazz.getName().startsWith("java.") || clazz.getName().startsWith("javax.")) {
            checkAllowedInCompileTimeJdkType(clazz);
        } else {
            checkAllowedInCompileTimeCustomType(clazz);
        }

        // array
        if (clazz.isArray()) {
            final int length = Array.getLength(obj);
            for (int k = 0; k < length; k++) {
                Object elem = Array.get(obj, k);
                checkAllowedInRealTime0(elem, depth + 1);
            }
        }
        // Collection
        if (Collection.class.isAssignableFrom(clazz)) {
            for (Object elem : ((Collection) obj)) {
                checkAllowedInRealTime0(elem, depth + 1);
            }
        }
        // Map
        if (Map.class.isAssignableFrom(clazz)) {
            for (Map.Entry<Object, Object> elem : ((Map<Object, Object>) obj).entrySet()) {
                checkAllowedInRealTime0(elem.getKey(), depth + 1);
                checkAllowedInRealTime0(elem.getValue(), depth + 1);
            }
        }
    }
}


/*
 * Icegem, Extensions library for VMWare vFabric GemFire
 *
 * Copyright (c) 2010-2011, Grid Dynamics Consulting Services Inc. or third-party
 * contributors as indicated by the @author tags or express copyright attribution
 * statements applied by the authors.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License v3, as published by the Free Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the GNU Lesser General Public License v3
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package com.googlecode.icegem.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeoutException;

/**
 * Platform independent java process launcher.
 *
 * @author Andrey Stepanov aka standy
 */
public class JavaProcessLauncher {
	/** Field PROCESS_STDOUT_STREAM_PREFIX */
	private static final String PROCESS_STDOUT_STREAM_PREFIX = " out>";

	/** Field PROCESS_ERROR_STREAM_PREFIX */
	private static final String PROCESS_ERROR_STREAM_PREFIX = " error>";

	/**
	 * Each process that starts with confirmation must write a startup completed
	 * string into it's standard output. Only after this command the process
	 * startup will be completed.
	 */
	public static final String PROCESS_STARTUP_COMPLETED = "JavaProcessLauncher: startup complete";

	/** Field DEFAULT_PROCESS_STARTUP_SHUTDOWN_TIME */
	public static final long DEFAULT_PROCESS_STARTUP_SHUTDOWN_TIME = 5000;

	/**
	 * Indicates that an input stream for a started process must be redirected
	 * to astandard out of a parent process
	 */
	private boolean redirectProcessInputStreamToParentProcessStdOut;

	/**
	 * Indicates that an error stream for a started process must be redirected
	 * to a standard out of a parent process
	 */
	/** Field redirectProcessErrorStreamToParentProcessStdOut */
	private boolean redirectProcessErrorStreamToParentProcessStdOut;

	/** */
	private boolean printType;

	/**
	 * Constructor JavaProcessLauncher creates a new JavaProcessLauncher
	 * instance.
	 */
	public JavaProcessLauncher() {
		this(false, true, true);
	}

	/**
	 * Constructor JavaProcessLauncher creates a new JavaProcessLauncher
	 * instance.
	 *
	 * @param redirectProcessInputStreamToParentProcessStdOut
	 *            of type boolean
	 * @param redirectProcessErrorStreamToParentProcessStdOut
	 *            of type boolean
	 */
	public JavaProcessLauncher(
			boolean redirectProcessInputStreamToParentProcessStdOut,
			boolean redirectProcessErrorStreamToParentProcessStdOut) {

		this(redirectProcessInputStreamToParentProcessStdOut,
				redirectProcessErrorStreamToParentProcessStdOut, true);

	}

	public JavaProcessLauncher(
			boolean redirectProcessInputStreamToParentProcessStdOut,
			boolean redirectProcessErrorStreamToParentProcessStdOut,
			boolean printType) {

		this.redirectProcessInputStreamToParentProcessStdOut = redirectProcessInputStreamToParentProcessStdOut;
		this.redirectProcessErrorStreamToParentProcessStdOut = redirectProcessErrorStreamToParentProcessStdOut;
		this.printType = printType;

	}

	/**
	 * Sets the redirectProcessErrorStreamToParentProcessStdOut of this
	 * JavaProcessLauncher object.
	 *
	 * @param redirectProcessErrorStreamToParentProcessStdOut
	 *            boolean flag.
	 *
	 */
	public void setRedirectProcessErrorStreamToParentProcessStdOut(
			boolean redirectProcessErrorStreamToParentProcessStdOut) {
		this.redirectProcessErrorStreamToParentProcessStdOut = redirectProcessErrorStreamToParentProcessStdOut;
	}

	/**
	 * Sets the redirectProcessInputStreamToParentProcessStdOut of this
	 * JavaProcessLauncher object.
	 *
	 * @param redirectProcessInputStreamToParentProcessStdOut
	 *            boolean flag.
	 *
	 */
	public void setRedirectProcessInputStreamToParentProcessStdOut(
			boolean redirectProcessInputStreamToParentProcessStdOut) {
		this.redirectProcessInputStreamToParentProcessStdOut = redirectProcessInputStreamToParentProcessStdOut;
	}

	/**
	 * Runs process with arguments based on a specified class in a separate VM.
	 * Waits while process is working and returns exit code after process
	 * finished.
	 * @param id TODO
	 * @param klass
	 *            of type Class
	 * @param javaArguments
	 *            arguments for java
	 * @param processArguments
	 *            arguments for process
	 *
	 * @return int
	 * @throws IOException
	 *             when
	 * @throws InterruptedException
	 *             when
	 */
	public int runAndWaitProcessExitCode(String id,
			Class<?> klass, String[] javaArguments, String[] processArguments)
			throws IOException, InterruptedException {
		Process process = startProcess(id, klass, javaArguments, processArguments,
				false);
		process.waitFor();
		return process.exitValue();
	}

	/**
	 * Runs a process and returns the Process object.
	 * @param id TODO
	 * @param clazz
	 *            - the class to run
	 * @param javaArguments
	 *            arguments for java
	 * @param processArguments
	 *            arguments for process
	 *
	 * @return - the Process object representing running process
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public Process runWithoutConfirmation(String id,
			Class<?> clazz, String[] javaArguments, String[] processArguments)
			throws IOException, InterruptedException {
		return startProcess(id, clazz, javaArguments, processArguments, false);
	}

	/**
	 * Runs process based on a specified class in a separate VM using array of
	 * arguments. To confirm that process completes startup it should write a
	 * startup completed string into it's standard output.
	 *
	 * @param id
	 *            the process identifier.
	 * @param klass
	 *            of type Class.
	 * @param javaArguments
	 *            arguments for java.
	 * @param processArguments
	 *            arguments for process.
	 *
	 * @return Process the started process.
	 * @throws IOException
	 *             when
	 * @throws InterruptedException
	 *             when
	 */
	public Process runWithConfirmation(String id, Class<?> klass,
			String[] javaArguments, String[] processArguments)
			throws IOException, InterruptedException {
		Process process = startProcess(id, klass, javaArguments, processArguments,
				true);

		waitConfirmation(klass.getSimpleName(), process);

		new StreamRedirector(process.getInputStream(), klass.getSimpleName()
				+ PROCESS_STDOUT_STREAM_PREFIX,
				redirectProcessInputStreamToParentProcessStdOut).start();

		return process;
	}

	/**
	 * Runs process with arguments based on a specified class in a separate VM.
	 * Waits DEFAULT_PROCESS_STARTUP_TIME before returns the created process to
	 * a caller.
	 *
	 * @param klass
	 *            of type Class
	 * @param javaArguments
	 *            arguments for java
	 * @param processArguments
	 *            arguments for process
	 * @return Process
	 * @throws IOException
	 *             when
	 * @throws InterruptedException
	 *             when
	 * @throws TimeoutException
	 *             when
	 */
	public Process runWithStartupDelay(Class<?> klass, String[] javaArguments,
			String[] processArguments) throws IOException,
			InterruptedException, TimeoutException {
		return runWithStartupDelay(klass, javaArguments, processArguments,
				DEFAULT_PROCESS_STARTUP_SHUTDOWN_TIME);
	}

	/**
	 * Runs process with arguments based on a specified class in a separate VM.
	 * Waits processStartupTime before returns the created process to a caller.
	 *
	 * @param klass
	 *            of type Class
	 * @param processStartupTime
	 *            time in milliseconds that launcher spend on waiting process
	 *            after it's start.
	 * @param javaArguments
	 *            arguments for java
	 * @param processArguments
	 *            arguments for process
	 * @return Process
	 * @throws IOException
	 *             when
	 * @throws InterruptedException
	 *             when
	 * @throws TimeoutException
	 *             if process startup is not completed in time.
	 */
	public Process runWithStartupDelay(Class<?> klass, String[] javaArguments,
			String[] processArguments, long processStartupTime)
			throws IOException, InterruptedException, TimeoutException {
		Process process = runWithConfirmation("", klass, javaArguments,
				processArguments);

		if (processStartupTime > 0) {
			Thread.sleep(processStartupTime);
		}

		return process;
	}

	/**
	 * Stops process by sending new line to it's output stream.
	 *
	 * The process can be stopped by calling destroy() method.
	 *
	 * @param process
	 *            of type Process
	 * @throws IOException
	 *             when
	 * @throws InterruptedException
	 */
	public void stopBySendingNewLineIntoProcess(Process process)
			throws IOException, InterruptedException {
		if (process != null) {
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
					process.getOutputStream()));

			writer.newLine();

			writer.flush();

			process.waitFor();
		}
	}

	/**
	 * Stops process by destroying process.
	 *
	 * @param process
	 *            of type Process
	 * @throws IOException
	 *             when
	 */
	public void stopByDestroyingProcess(Process process) throws IOException {
		process.destroy();
	}

	/**
	 * Starts process based on specified class using command line arguments.
	 * This process inherits a classpath from parent VM that starts it.
	 *
	 * @param klass
	 *            of type Class
	 * @param javaArguments
	 *            of type String[]
	 * @param processArguments
	 *            of type String[]
	 * @param withConfirmation
	 *            of type boolean
	 * @return Process
	 * @throws IOException
	 *             when
	 * @throws InterruptedException
	 *             when
	 */
	private Process startProcess(String id, Class<?> klass, String[] javaArguments,
			String[] processArguments, boolean withConfirmation)
			throws IOException, InterruptedException {
		List<String> arguments = createCommandLineForProcess(klass,
				javaArguments, processArguments);

		Process process = new ProcessBuilder(arguments).start();

		redirectProcessStreams(id, klass, process, !withConfirmation);

		return process;
	}

	/**
	 * Redirects process standard output and error streams into parent process
	 * standard output.
	 *
	 * @param klass
	 *            of type Class
	 * @param process
	 *            of type Process
	 * @param redirectProcessStdOut
	 *            of type boolean
	 */
	private void redirectProcessStreams(String id, Class<?> klass, Process process,
			boolean redirectProcessStdOut) {

		String errorStreamType = (printType ? klass.getSimpleName() + id
				+ PROCESS_ERROR_STREAM_PREFIX : "");

		new StreamRedirector(process.getErrorStream(), errorStreamType,
				redirectProcessErrorStreamToParentProcessStdOut, System.err)
				.start();

		if (redirectProcessStdOut) {
			String outputStreamType = (printType ? klass.getSimpleName() + id
					+ PROCESS_STDOUT_STREAM_PREFIX : "");

			new StreamRedirector(process.getInputStream(), outputStreamType,
					redirectProcessInputStreamToParentProcessStdOut, System.out)
					.start();
		}
	}

	/**
	 * Builds command line for starting java process based on specified
	 * arguments.
	 *
	 * @param klazz
	 * @param processArguments
	 *            of type String[]
	 * @return List<String>
	 */
	private List<String> createCommandLineForProcess(Class<?> klazz,
			String[] processArguments) {
		return createCommandLineForProcess(klazz, null, processArguments);
	}

	/**
	 * Builds command line for starting java process based on specified
	 * arguments.
	 *
	 * @param klazz
	 * @param javaArguments
	 *            of type String[]
	 * @param processArguments
	 *            of type String[]
	 * @return List<String>
	 */
	private List<String> createCommandLineForProcess(Class<?> klazz,
			String[] javaArguments, String[] processArguments) {
		String javaHome = System.getProperty("java.home");
		String javaBin = javaHome + File.separator + "bin" + File.separator
				+ "java";
		String classpath = System.getProperty("java.class.path");

		List<String> argumentsList = new ArrayList<String>();
		argumentsList.add(javaBin);
		argumentsList.add("-cp");
		argumentsList.add(classpath);

		if (javaArguments != null && javaArguments.length > 0) {
			argumentsList.addAll(Arrays.asList(javaArguments));
		}

		argumentsList.add(klazz.getCanonicalName());

		if (processArguments != null && processArguments.length > 0) {
			argumentsList.addAll(Arrays.asList(processArguments));
		}

		return argumentsList;
	}

	/**
	 * Waits startup complete confirmation from process.
	 *
	 * @param className
	 *            of type String
	 * @param process
	 *            of type Process
	 * @throws IOException
	 *             when
	 * @throws InterruptedException
	 *             when
	 */
	private void waitConfirmation(String className, Process process)
			throws IOException, InterruptedException {
		System.out
				.println("Waiting startup complete confirmation for a process ("
						+ className + ")...");

		BufferedReader bufferedReader = new BufferedReader(
				new InputStreamReader(process.getInputStream()));

		String line;

		while ((line = bufferedReader.readLine()) != null) {
			if (line.equals(PROCESS_STARTUP_COMPLETED)) {
				System.out.println("The process (" + className
						+ ") has been started successfully");

				return;
			} else if (redirectProcessInputStreamToParentProcessStdOut) {
				System.out.println(className + PROCESS_STDOUT_STREAM_PREFIX
						+ line);
			}
		}

		throw new InterruptedException(
				"Process ("
						+ className
						+ ") "
						+ "has been already finished without startup complete confirmation");
	}

	/**
	 * Redirects process stream into parent standard output.
	 *
	 * @author Andrey Stepanov aka standy
	 */
	private class StreamRedirector extends Thread {
		/** Field inputStream */
		private InputStream inputStream;
		/** Field type */
		private String type;
		/**
		 * Field redirectToParentProcessStdOut - if true than this stream will
		 * be redirected to parent process standard output.
		 */
		private boolean redirectToParentProcessStdOut;
		private final PrintStream printStream;

		/**
		 * Constructor StreamRedirector creates a new StreamRedirector instance.
		 *
		 * @param inputStream
		 *            of type InputStream
		 * @param type
		 *            of type String
		 */
		public StreamRedirector(InputStream inputStream, String type) {
			this(inputStream, type, false);
		}

		/**
		 * Constructor StreamRedirector creates a new StreamRedirector instance.
		 *
		 * @param inputStream
		 *            of type InputStream
		 * @param type
		 *            of type String
		 * @param redirectToParentProcessStdOut
		 */
		public StreamRedirector(InputStream inputStream, String type,
				boolean redirectToParentProcessStdOut) {
			this(inputStream, type, redirectToParentProcessStdOut, System.out);
		}

		public StreamRedirector(InputStream inputStream, String type,
				boolean redirectToParentProcessStdOut, PrintStream printStream) {
			this.inputStream = inputStream;
			this.type = type;
			this.redirectToParentProcessStdOut = redirectToParentProcessStdOut;
			this.printStream = printStream;
		}

		/**
		 * Method run.
		 */
		@Override
		public void run() {
			try {
				BufferedReader br = new BufferedReader(new InputStreamReader(
						inputStream));

				String line;

				while ((line = br.readLine()) != null) {
					if (redirectToParentProcessStdOut) {
						printStream.println(type + line);
					}
				}

				br.close();
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
		}
	}
}


/*-
 * #%L
 * Nazgul Project: nazgul-tools-visualization-spi-doclet
 * %%
 * Copyright (C) 2010 - 2018 jGuru Europe AB
 * %%
 * Licensed under the jGuru Europe AB license (the "License"), based
 * on Apache License, Version 2.0; you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *       http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt
 *
 * 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.
 * #L%
 */

package se.jguru.nazgul.tools.visualization.spi.doclet.converter;

import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.PackageDoc;
import se.jguru.nazgul.tools.visualization.api.factory.AbstractGraphConverter;
import se.jguru.nazgul.tools.visualization.model.diagram.Graph;
import se.jguru.nazgul.tools.visualization.model.diagram.statement.Subgraph;
import se.jguru.nazgul.tools.visualization.spi.doclet.javadoc.VisualizationWrappedRootDoc;

import java.util.SortedSet;

/**
 * Default TypeConverter to convert a {@link VisualizationWrappedRootDoc} received from
 * a JavaDoc execution into a {@link Graph} used for rendering.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public class VisualizationWrappedRootDocConverter extends AbstractGraphConverter<VisualizationWrappedRootDoc> {

    /**
     * {@inheritDoc}
     */
    @Override
    protected Graph doConvert(final VisualizationWrappedRootDoc toConvert) {

        final Graph toReturn = new Graph();

        // Create subgraphs for each package and add them to the returned Graph.
        toConvert.sortClassesPerPackage().forEach((packageDoc, classDocs) ->
                toReturn.add(convertPackage(packageDoc, classDocs)));

        // All Done.
        return toReturn;
    }

    protected Subgraph convertPackage(final PackageDoc packageDoc,
                                      final SortedSet<ClassDoc> classDocs) {
        // final Subgraph toReturn = new Subgraph(packageDoc.name());

        // TODO: Continue here.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}


/*-
 * #%L
 * Nazgul Project: nazgul-tools-checkstyle-maven-plugin
 * %%
 * Copyright (C) 2010 - 2018 jGuru Europe AB
 * %%
 * Licensed under the jGuru Europe AB license (the "License"), based
 * on Apache License, Version 2.0; you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *       http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt
 *
 * 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.
 * #L%
 */

package se.jguru.nazgul.tools.plugin.checkstyle.rss;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugin.logging.SystemStreamLog;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.VelocityException;
import org.codehaus.plexus.velocity.VelocityComponent;

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

/**
 *

 * A component to work with VelocityTemplates from within plugins.
 *

 *
 *

 * You will need to reference the velocity component as a parameter
 * in your plugin.  Like this:
 *

 *

 * /**
 *  * Velocity Component
 *  * @component
 *  */
 *  private VelocityComponent velocity;
 *

 *
 * @author Joakim Erdfelt
 * @version $Id$
 */
public class VelocityTemplate
{
    private String templateDirectory;

    private Log log;

    private VelocityComponent velocity;

    public VelocityTemplate( VelocityComponent velocityComponent, String templateBaseDirectory )
    {
        this.velocity = velocityComponent;
        this.templateDirectory = templateBaseDirectory;
    }

    public String getTemplateDirectory()
    {
        return templateDirectory;
    }

    public VelocityComponent getVelocity()
    {
        return velocity;
    }

    /**
     * Using a specified Velocity Template and provided context, create the outputFilename.
     *
     * @param outputFilename the file to be generated.
     * @param template       the velocity template to use.
     * @param context        the velocity context map.
     * @throws VelocityException if the template was not found or any other Velocity exception.
     * @throws MojoExecutionException
     * @throws IOException
     */
    public void generate( String outputFilename, String template, Context context )
        throws VelocityException, MojoExecutionException, IOException
    {
        Writer writer = null;

        try
        {
            File f = new File( outputFilename );

            if ( !f.getParentFile().exists() )
            {
                f.getParentFile().mkdirs();
            }

            writer = new FileWriter( f );

            getVelocity().getEngine().mergeTemplate( templateDirectory + "/" + template, context, writer );
        }
        catch ( ResourceNotFoundException e )
        {
            throw new ResourceNotFoundException( "Template not found: " + templateDirectory + "/" + template, e );
        }
        catch ( VelocityException | IOException e )
        {
            throw e; // to get past generic catch for Exception.
        }
        catch ( Exception e )
        {
            throw new MojoExecutionException( e.getMessage(), e );
        }
        finally
        {
            if ( writer != null )
            {
                writer.flush();
                writer.close();

                getLog().debug( "File " + outputFilename + " created..." );
            }
        }
    }

    public void setTemplateDirectory( String templateDirectory )
    {
        this.templateDirectory = templateDirectory;
    }

    public void setVelocity( VelocityComponent velocity )
    {
        this.velocity = velocity;
    }

    public Log getLog()
    {
        if ( this.log == null )
        {
            this.log = new SystemStreamLog();
        }
        return log;
    }

    public void setLog( Log log )
    {
        this.log = log;
    }

}


/*-
 * #%L
 * Nazgul Project: nazgul-tools-checkstyle-maven-plugin
 * %%
 * Copyright (C) 2010 - 2018 jGuru Europe AB
 * %%
 * Licensed under the jGuru Europe AB license (the "License"), based
 * on Apache License, Version 2.0; you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *       http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt
 *
 * 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.
 * #L%
 */

package se.jguru.nazgul.tools.plugin.checkstyle.exec;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

import com.puppycrawl.tools.checkstyle.api.AuditEvent;
import com.puppycrawl.tools.checkstyle.api.AuditListener;
import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
import com.puppycrawl.tools.checkstyle.api.Configuration;
import com.puppycrawl.tools.checkstyle.api.SeverityLevel;

import org.codehaus.plexus.util.StringUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * Listener in charge of receiving events from the Checker.
 *
 * @author Emmanuel Venisse
 * @author Vincent Siveton
 * @version $Id$
 */
public class CheckstyleCheckerListener extends AutomaticBean implements AuditListener {

    private List<File> sourceDirectories;
    private CheckstyleResults results;
    private String currentFile;
    private List<AuditEvent> events;
    private SeverityLevel severityLevel;
    private Configuration checkstyleConfiguration;

    /**
     * @param sourceDirectory assume that is sourceDirectory is a not null directory and exists
     */
    public CheckstyleCheckerListener(final File sourceDirectory) {
        this.sourceDirectories = new ArrayList<>();
        this.sourceDirectories.add(sourceDirectory);
    }

    /**
     * @param sourceDirectory assume that is sourceDirectory is a not null directory and exists
     * @param configuration   Checkstyle configuration
     * @since 2.5
     */
    public CheckstyleCheckerListener(final File sourceDirectory, final Configuration configuration) {
        this.sourceDirectories = new ArrayList<>();
        this.sourceDirectories.add(sourceDirectory);
        this.checkstyleConfiguration = configuration;
    }

    /**
     * @param configuration Checkstyle configuration
     * @since 2.5
     */
    public CheckstyleCheckerListener(final Configuration configuration) {
        this.sourceDirectories = new ArrayList<>();
        this.checkstyleConfiguration = configuration;
    }

    /**
     * @param sourceDirectory assume that is sourceDirectory is a not null directory and exists
     */
    public void addSourceDirectory(final File sourceDirectory) {
        this.sourceDirectories.add(sourceDirectory);
    }

    /**
     * @param severityLevel
     */
    public void setSeverityLevelFilter(final SeverityLevel severityLevel) {
        this.severityLevel = severityLevel;
    }

    /**
     * @return
     */
    public SeverityLevel getSeverityLevelFilter() {
        return severityLevel;
    }

    /**
     * {@inheritDoc}
     */
    public void auditStarted(final AuditEvent event) {
        setResults(new CheckstyleResults());
    }

    /**
     * {@inheritDoc}
     */
    public void auditFinished(final AuditEvent event) {
        //do nothing
    }

    /**
     * {@inheritDoc}
     */
    public void fileStarted(AuditEvent event) {
        final String fileName = StringUtils.replace(event.getFileName(), "\\", "/");

        for (File sourceDirectory : sourceDirectories) {
            String sourceDirectoryPath = StringUtils.replace(sourceDirectory.getPath(), "\\", "/");

            if (fileName.startsWith(sourceDirectoryPath + "/")) {
                currentFile = StringUtils.substring(fileName, sourceDirectoryPath.length() + 1);

                events = getResults().getFileViolations(currentFile);

                break;
            }
        }

        if (events == null) {
            events = new ArrayList<>();
        }
    }

    /**
     * {@inheritDoc}
     */
    public void fileFinished(AuditEvent event) {
        getResults().setFileViolations(currentFile, events);
        currentFile = null;
    }

    /**
     * {@inheritDoc}
     */
    public void addError(AuditEvent event) {
        if (SeverityLevel.IGNORE.equals(event.getSeverityLevel())) {
            return;
        }

        if (severityLevel == null || severityLevel.equals(event.getSeverityLevel())) {
            events.add(event);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void addException(final AuditEvent event, final Throwable throwable) {
        //Do Nothing
    }

    /**
     * @return
     */
    public CheckstyleResults getResults() {
        results.setConfiguration(checkstyleConfiguration);
        return results;
    }

    /**
     * @param results
     */
    public void setResults(CheckstyleResults results) {
        this.results = results;
    }

    /**
     * @since 2.5
     */
    public Configuration getCheckstyleConfiguration() {
        return checkstyleConfiguration;
    }

    /**
     * @since 2.5
     */
    public void setCheckstyleConfiguration(Configuration checkstyleConfiguration) {
        this.checkstyleConfiguration = checkstyleConfiguration;
    }

}


/*
 * Icegem, Extensions library for VMWare vFabric GemFire
 *
 * Copyright (c) 2010-2011, Grid Dynamics Consulting Services Inc. or third-party
 * contributors as indicated by the @author tags or express copyright attribution
 * statements applied by the authors.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License v3, as published by the Free Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * You should have received a copy of the GNU Lesser General Public License v3
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package com.googlecode.icegem.cacheutils.replication;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import com.googlecode.icegem.utils.PropertiesHelper;
import com.googlecode.icegem.utils.JavaProcessLauncher;

/**
 * Coordinates, configures and runs the set of guest nodes
 */
public class ReplicationProcessor {

	/* Clusters properties */
	private Properties clustersProperties;

	/* Timeout, milliseconds */
	private long timeout;

	/* Name of the technical region */
	private final String regionName;

	/* Debug enabled flag */
	private final boolean debugEnabled;

	/* Quiet flag */
	private final boolean quiet;

	/* Field javaProcessLauncher */
	private JavaProcessLauncher javaProcessLauncher = new JavaProcessLauncher(
		true, true, false);

	/* The time at which the processing has started */
	private long processingStartedAt;

	/**
	 * Configures and creates the instance of replication processor
	 *
	 * @param clustersProperties
	 *            - the clusters' properties
	 * @param timeout
	 *            - the timeout, milliseconds
	 * @param regionName
	 *            - the name of technical region
	 * @param debugEnabled
	 *            - the debug enabled flag
	 * @param quiet
	 *            - the quiet flag
	 */
	public ReplicationProcessor(Properties clustersProperties, long timeout,
		String regionName, boolean debugEnabled, boolean quiet) {

		processingStartedAt = System.currentTimeMillis();

		this.clustersProperties = clustersProperties;
		this.timeout = timeout;
		this.regionName = regionName;
		this.debugEnabled = debugEnabled;
		this.quiet = quiet;
	}

	/**
	 * Creates, configures and runs the guest nodes and collects information
	 * form them
	 *
	 * @return - 0 in case of success, 1 otherwise
	 *
	 * @throws IOException
	 * @throws InterruptedException
	 */
	public int process() throws IOException, InterruptedException {
		debug("ReplicationProcessor#process(): Processing start");

		Properties gemfireProperties = PropertiesHelper.filterProperties(
			System.getProperties(), "gemfire.");
		String[] vmOptions = PropertiesHelper
			.propertiesToVMOptions(gemfireProperties);

		debug("ReplicationProcessor#process(): vmOptions = "
			+ Arrays.asList(vmOptions));

		List<Process> processesList = new ArrayList<Process>();
		for (Object keyObject : clustersProperties.keySet()) {
			String cluster = (String) keyObject;
			String clustersPropertiesString = PropertiesHelper
				.propertiesToString(clustersProperties);

			debug("ReplicationProcessor#process(): Starting GuestNode with parameters: cluster = "
				+ cluster
				+ ", clustersPropertiesString = "
				+ clustersPropertiesString
				+ ", timeout = "
				+ timeout
				+ ", regionName = " + regionName);

			Process process = javaProcessLauncher.runWithoutConfirmation(
				"",
				GuestNode.class,
				vmOptions, new String[] { cluster, clustersPropertiesString,
					String.valueOf(timeout), regionName,
					String.valueOf(debugEnabled), String.valueOf(quiet),
					String.valueOf(processingStartedAt) });

			debug("ReplicationProcessor#process(): Adding GuestNode to processList");

			processesList.add(process);
		}

		debug("ReplicationProcessor#process(): Waiting for processes finish");
		int mainExitCode = 0;
		int processNumber = 0;
		for (Process process : processesList) {
			debug("ReplicationProcessor#process(): Waiting for process #"
				+ processNumber);

			int exitCode = process.waitFor();

			if (exitCode != 0) {
				mainExitCode = 1;
			}
			debug("ReplicationProcessor#process(): Process #" + processNumber
				+ " finished with exitCode = " + exitCode);

			processNumber++;
		}

		debug("ReplicationProcessor#process(): Processing finished with mainExitCode = "
			+ mainExitCode);

		return mainExitCode;
	}

	/**
	 * Prints debug information if the debug is enabled
	 *
	 * @param message
	 *            - the debug message
	 */
	private void debug(String message) {
		if (debugEnabled) {
			long currentTime = System.currentTimeMillis();
			long timeSinceProcessingStart = currentTime - processingStartedAt;
			System.err.println(timeSinceProcessingStart
				+ " [ReplicationProcessor] " + message);
		}
	}

}


/*-
 * #%L
 * Nazgul Project: nazgul-tools-validation-aspect
 * %%
 * Copyright (C) 2010 - 2018 jGuru Europe AB
 * %%
 * Licensed under the jGuru Europe AB license (the "License"), based
 * on Apache License, Version 2.0; you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *       http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt
 *
 * 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.
 * #L%
 */

package se.jguru.nazgul.tools.validation.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.ConstructorSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import se.jguru.nazgul.tools.validation.api.Validatable;
import se.jguru.nazgul.tools.validation.api.exception.InternalStateValidationException;

import java.lang.reflect.Constructor;

/**
 *
The aspect enforcing validity on a class implementing {@link Validatable}s (typically Entities or other classes
 * containing state). This aspect should be fired immediately after a non-default constructor is invoked, to ensure
 * that frameworks such as JAXB and JPA can perform full reflective population of an object before
 * state validation using this Aspect is performed.

 *
Validation should be run only once, and only after the constructor of the ultimate
 * created instance is run (default AspectJ behaviour is to run the Aspect after any
 * constructor within the inheritance hierarchy is executed [i.e. after constructors
 * in superclasses are run, within the constructor of subtypes]).

 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
@Aspect
public class ValidationAspect {

    // Our log
    private static final Logger log = LoggerFactory.getLogger(ValidationAspect.class);

    /**
     * Defines a Pointcut for any constructor to a class implementing Validatable.
     * The Pointcut will return {@code true} for all non-default Constructors, i.e. when the signature
     * of the JoinPoint has more than 0 arguments.
     *
     * @param joinPoint The currently executing joinPoint.
     */
    @Pointcut(value = "initialization(se.jguru.nazgul.tools.validation.api.Validatable+.new(..)) && if()",
            argNames = "joinPoint")
    public static boolean anyNonDefaultConstructor(final JoinPoint joinPoint) {

        // Extract the JoinPoint signature.
        final Signature signature = joinPoint.getSignature();
        final boolean isConstructorSignature = signature instanceof ConstructorSignature;

        if (isConstructorSignature) {

            final Constructor constructor = ((ConstructorSignature) signature).getConstructor();
            final boolean isNonDefaultConstructor = constructor != null && constructor.getParameters().length > 0;

            log.trace("Non-Default Constructor signature [" + signature.toString() + "] for ["
                    + (constructor == null ? "<null>" : constructor.toString()) + "]: " + isNonDefaultConstructor);

            // All Done.
            return isNonDefaultConstructor;

        } else {
            log.debug("Found non-constructor signature: " + signature);
        }

        // Nopes.
        return false;
    }

    /**
     * Aspect invoking the {@link Validatable#validateInternalState()} method before returning from any non-default
     * constructor within a class implementing {@link Validatable}.
     *
     * @param joinPoint The currently executing joinPoint.
     * @throws InternalStateValidationException if the validation of the {@link Validatable} failed.
     */
    @AfterReturning(value = "anyNonDefaultConstructor(joinPoint) && target(aValidatable)",
            argNames = "joinPoint, aValidatable")
    public void performValidationAfterCompoundConstructor(final JoinPoint joinPoint, final Validatable aValidatable)
            throws InternalStateValidationException {

        if (log.isDebugEnabled()) {
            log.debug("Validating instance of type [" + aValidatable.getClass().getName() + "]");
        }

        if (joinPoint.getStaticPart() == null) {
            log.warn("Static part of join point was null for validatable of type: "
                    + aValidatable.getClass().getName(), new IllegalStateException());
            return;
        }

        // Ignore calling validateInternalState when we execute constructors in
        // any class but the concrete Validatable class.
        final ConstructorSignature sig = (ConstructorSignature) joinPoint.getSignature();
        final Class<?> constructorDefinitionClass = sig.getConstructor().getDeclaringClass();
        if (aValidatable.getClass() == constructorDefinitionClass) {

            // Now fire the validateInternalState method.
            aValidatable.validateInternalState();

        } else {

            log.debug("Ignored firing validatable for constructor defined in ["
                    + constructorDefinitionClass.getName() + "] and Validatable of type ["
                    + aValidatable.getClass().getName() + "]");
        }
    }
}


/*
 * Copyright (c) 2015. Rick Hightower, Geoff Chandler
 *
 * 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.
 *
 * QBit - The Microservice lib for Java : JSON, WebSocket, REST. Be The Web!
 */

package io.advantageous.qbit.example.inproc;

import io.advantageous.boon.core.Sys;
import io.advantageous.qbit.reactive.Callback;
import io.advantageous.qbit.service.ServiceQueue;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import static io.advantageous.qbit.queue.QueueBuilder.queueBuilder;
import static io.advantageous.qbit.service.ServiceBuilder.serviceBuilder;

/**
 * created by rhightower on 1/30/15.
 */
public class InProcExample {

    public static void main(String... args) throws Exception {

        example1(args);

        example2(args);

    }

    /**
     * You can use services in process
     */
    public static void example1(String... args)  {

        /* Synchronous service. */
        final TodoManager todoManagerImpl = new TodoManager();

        /*
        Create the service which manages async calls to todoManagerImpl.
         */
        final ServiceQueue serviceQueue = serviceBuilder()
                .setServiceObject(todoManagerImpl)
                .build().startServiceQueue();

        /* Create Asynchronous proxy over Synchronous service. */
        final TodoManagerClientInterface todoManager = serviceQueue.createProxy(TodoManagerClientInterface.class);

        serviceQueue.startCallBackHandler();

        System.out.println("This is an async call");
        /* Asynchronous method call. */
        todoManager.add(new Todo("Call Mom", "Give Mom a call"));

        AtomicInteger countTracker = new AtomicInteger(); //Hold count from async call to service

        System.out.println("This is an async call to count");

        todoManager.count(count -> {
            System.out.println("This lambda expression is the callbackWithTimeout " + count);

            countTracker.set(count);
        });

        todoManager.clientProxyFlush(); //Flush all methods. It batches calls.

        Sys.sleep(100);

        System.out.printf("This is the count back from the server %d\n", countTracker.get());

        System.out.printf("END EXAMPLE 1 %s \n", countTracker.get());
        System.out.printf("END EXAMPLE 1 %s \n", countTracker.get());
        System.out.printf("END EXAMPLE 1 %s \n", countTracker.get());
        System.out.printf("END EXAMPLE 1 %s \n", countTracker.get());

    }

    /**
     * You can use services in process
     */
    public static void example2(String... args)  {

        /* Synchronous service. */
        final TodoManager todoManagerImpl = new TodoManager();

        /*
        Create the service which manages async calls to todoManagerImpl.

        You can control the batch size of methods.
        After it hits so many, it sends those methods to the service.
        This allows threads to handle many method calls with on access of the queue.
        Here we set it to 1 so it will flush with every call.
        Setting invoke dynamic false turns off auto type conversion which is mainly for JSON REST calls
        and WebSocket calls.
        This means that you can execute the service more efficiently when it is in proc.
         */
        final ServiceQueue serviceQueue = serviceBuilder()
                .setRequestQueueBuilder(queueBuilder().setBatchSize(1))
                .setServiceObject(todoManagerImpl).setInvokeDynamic(false)
                .build().startServiceQueue();

        /* Create Asynchronous proxy over Synchronous service. */
        final TodoManagerClientInterface todoManager = serviceQueue.createProxy(TodoManagerClientInterface.class);

        serviceQueue.startCallBackHandler();

        System.out.println("This is an async call");
        /* Asynchronous method call. */
        todoManager.add(new Todo("Call Mom", "Give Mom a call"));

        AtomicInteger countTracker = new AtomicInteger(); //Hold count from async call to service

        System.out.println("This is an async call to count");

        todoManager.count(count -> {
            System.out.println("This lambda expression is the callbackWithTimeout " + count);

            countTracker.set(count);
        });

        /*
        We don't need this now.
         */
        //todoManager.clientProxyFlush(); //Flush all methods. It batches calls.

        Sys.sleep(100);

        System.out.printf("This is the count back from the service %d\n", countTracker.get());

        todoManager.list(todos -> todos.forEach(System.out::println));

    }

    interface TodoManagerClientInterface {

        void add(Todo todo);

        void list(Callback<List<Todo>> list);

        void count(Callback<Integer> count);

        void clientProxyFlush();

    }

    /**
     * Example service class
     */
    public static class TodoManager {

        private final List<Todo> list = new ArrayList<>();

        public void add(Todo todo) {
            System.out.println("Add Todo");
            list.add(todo);
        }

        public List<Todo> list() {

            System.out.println("List Todo");
            return new ArrayList<>(list);
        }

        public int count() {
            System.out.println("Count Todo");
            return list.size();
        }
    }

    public static class Todo {
        private final String name;
        private final String description;

        public Todo(String name, String description) {

            this.name = name;
            this.description = description;
        }

        public String getName() {
            return name;
        }

        public String getDescription() {
            return description;
        }

        @Override
        public String toString() {
            return "Todo{" +
                    "name='" + name + '\'' +
                    ", description='" + description + '\'' +
                    '}';
        }
    }
}


package org.jboss.resteasy.util;

import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;

/**
 * Type conversions and generic type manipulations
 *
 * @author Bill Burke
 * @version $Revision: 1 $
 */
public class Types {
    /**
     * Is the genericType of a certain class?
     */
    public static boolean isA(Class clazz, ParameterizedType pType) {
        return clazz.isAssignableFrom((Class) pType.getRawType());
    }

    /**
     * Gets the index-th type argument.
     */
    public static Class getArgumentType(ParameterizedType pType, int index) {
        return (Class) pType.getActualTypeArguments()[index];
    }

    public static Class getTemplateParameterOfInterface(Class base, Class desiredInterface) {
        Object rtn = searchForInterfaceTemplateParameter(base, desiredInterface);
        if (rtn != null && rtn instanceof Class)
            return (Class) rtn;
        return null;
    }

    private static Object searchForInterfaceTemplateParameter(Class base, Class desiredInterface) {
        for (int i = 0; i < base.getInterfaces().length; i++) {
            Class intf = base.getInterfaces()[i];
            if (intf.equals(desiredInterface)) {
                Type generic = base.getGenericInterfaces()[i];
                if (generic instanceof ParameterizedType) {
                    ParameterizedType p = (ParameterizedType) generic;
                    Type type = p.getActualTypeArguments()[0];
                    Class rtn = getRawTypeNoException(type);
                    if (rtn != null)
                        return rtn;
                    return type;
                } else {
                    return null;
                }
            }
        }
        if (base.getSuperclass() == null || base.getSuperclass().equals(Object.class))
            return null;
        Object rtn = searchForInterfaceTemplateParameter(base.getSuperclass(), desiredInterface);
        if (rtn == null || rtn instanceof Class)
            return rtn;
        if (!(rtn instanceof TypeVariable))
            return null;

        String name = ((TypeVariable) rtn).getName();
        int index = -1;
        TypeVariable[] variables = base.getSuperclass().getTypeParameters();
        if (variables == null || variables.length < 1)
            return null;

        for (int i = 0; i < variables.length; i++) {
            if (variables[i].getName().equals(name))
                index = i;
        }
        if (index == -1)
            return null;

        Type genericSuperclass = base.getGenericSuperclass();
        if (!(genericSuperclass instanceof ParameterizedType))
            return null;

        ParameterizedType pt = (ParameterizedType) genericSuperclass;
        Type type = pt.getActualTypeArguments()[index];

        Class clazz = getRawTypeNoException(type);
        if (clazz != null)
            return clazz;
        return type;
    }

    /**
     * See if the two methods are compatible, that is they have the same
     * relative signature
     *
     * @param method
     * @param intfMethod
     * @return
     */
    public static boolean isCompatible(Method method, Method intfMethod) {
        if (method == intfMethod)
            return true;

        if (!method.getName().equals(intfMethod.getName()))
            return false;
        if (method.getParameterTypes().length != intfMethod.getParameterTypes().length)
            return false;

        for (int i = 0; i < method.getParameterTypes().length; i++) {
            Class rootParam = method.getParameterTypes()[i];
            Class intfParam = intfMethod.getParameterTypes()[i];
            if (!intfParam.isAssignableFrom(rootParam))
                return false;
        }
        return true;
    }

    /**
     * Given a method and a root class, find the actual method declared in the
     * root that implements the method.
     *
     * @param clazz
     * @param intfMethod
     * @return
     */
    public static Method getImplementingMethod(Class clazz, Method intfMethod) {
        Class<?> declaringClass = intfMethod.getDeclaringClass();
        if (declaringClass.equals(clazz))
            return intfMethod;

        Class[] paramTypes = intfMethod.getParameterTypes();

        if (declaringClass.getTypeParameters().length > 0 && paramTypes.length > 0) {
            Type[] intfTypes = findParameterizedTypes(clazz, declaringClass);
            Map<String, Type> typeVarMap = new HashMap<String, Type>();
            TypeVariable<? extends Class<?>>[] vars = declaringClass.getTypeParameters();
            for (int i = 0; i < vars.length; i++) {
                if (intfTypes != null && i < intfTypes.length) {
                    typeVarMap.put(vars[i].getName(), intfTypes[i]);
                } else {
                    // Interface type parameters may not have been filled out
                    typeVarMap.put(vars[i].getName(), vars[i].getGenericDeclaration());
                }
            }
            Type[] paramGenericTypes = intfMethod.getGenericParameterTypes();
            paramTypes = new Class[paramTypes.length];

            for (int i = 0; i < paramTypes.length; i++) {
                if (paramGenericTypes[i] instanceof TypeVariable) {
                    TypeVariable tv = (TypeVariable) paramGenericTypes[i];
                    Type t = typeVarMap.get(tv.getName());
                    if (t == null) {
                        throw new RuntimeException("Unable to resolve type variable");
                    }
                    paramTypes[i] = getRawType(t);
                } else {
                    paramTypes[i] = getRawType(paramGenericTypes[i]);
                }
            }

        }

        try {
            return clazz.getMethod(intfMethod.getName(), paramTypes);
        } catch (NoSuchMethodException e) {
        }

        try {
            Method tmp = clazz.getMethod(intfMethod.getName(), intfMethod.getParameterTypes());
            return tmp;
        } catch (NoSuchMethodException e) {

        }
        return intfMethod;
    }

    public static Class<?> getRawType(Type type) {
        if (type instanceof Class<?>) {
            // type is a normal class.
            return (Class<?>) type;

        } else if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type rawType = parameterizedType.getRawType();
            return (Class<?>) rawType;
        } else if (type instanceof GenericArrayType) {
            final GenericArrayType genericArrayType = (GenericArrayType) type;
            final Class<?> componentRawType = getRawType(genericArrayType.getGenericComponentType());
            return Array.newInstance(componentRawType, 0).getClass();
        } else if (type instanceof TypeVariable) {
            final TypeVariable typeVar = (TypeVariable) type;
            if (typeVar.getBounds() != null && typeVar.getBounds().length > 0) {
                return getRawType(typeVar.getBounds()[0]);
            }
        }
        throw new RuntimeException("Unable to determine base class from Type");
    }

    public static Class<?> getRawTypeNoException(Type type) {
        if (type instanceof Class<?>) {
            // type is a normal class.
            return (Class<?>) type;

        } else if (type instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) type;
            Type rawType = parameterizedType.getRawType();
            return (Class<?>) rawType;
        } else if (type instanceof GenericArrayType) {
            final GenericArrayType genericArrayType = (GenericArrayType) type;
            final Class<?> componentRawType = getRawType(genericArrayType.getGenericComponentType());
            return Array.newInstance(componentRawType, 0).getClass();
        }
        return null;
    }

    /**
     * Returns the type argument from a parameterized type
     *
     * @param genericType
     * @return null if there is no type parameter
     */
    public static Class<?> getTypeArgument(Type genericType) {
        if (!(genericType instanceof ParameterizedType))
            return null;
        ParameterizedType parameterizedType = (ParameterizedType) genericType;
        Class<?> typeArg = (Class<?>) parameterizedType.getActualTypeArguments()[0];
        return typeArg;
    }

    public static Class getCollectionBaseType(Class type, Type genericType) {
        if (genericType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            Type componentGenericType = parameterizedType.getActualTypeArguments()[0];
            return getRawType(componentGenericType);
        } else if (genericType instanceof GenericArrayType) {
            final GenericArrayType genericArrayType = (GenericArrayType) genericType;
            Type componentGenericType = genericArrayType.getGenericComponentType();
            return getRawType(componentGenericType);
        } else if (type.isArray()) {
            return type.getComponentType();
        }
        return null;
    }

    public static Class getMapKeyType(Type genericType) {
        if (genericType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            Type componentGenericType = parameterizedType.getActualTypeArguments()[0];
            return getRawType(componentGenericType);
        }
        return null;
    }

    public static Class getMapValueType(Type genericType) {
        if (genericType instanceof ParameterizedType) {
            ParameterizedType parameterizedType = (ParameterizedType) genericType;
            Type componentGenericType = parameterizedType.getActualTypeArguments()[1];
            return getRawType(componentGenericType);
        }
        return null;
    }

    public static Type resolveTypeVariables(Class<?> root, Type type) {
        if (type instanceof TypeVariable) {
            Type newType = resolveTypeVariable(root, (TypeVariable) type);
            return (newType == null) ? type : newType;
        } else if (type instanceof ParameterizedType) {
            final ParameterizedType param = (ParameterizedType) type;
            final Type[] actuals = new Type[param.getActualTypeArguments().length];
            for (int i = 0; i < actuals.length; i++) {
                Type newType = resolveTypeVariables(root, param.getActualTypeArguments()[i]);
                actuals[i] = newType == null ? param.getActualTypeArguments()[i] : newType;
            }
            return new ParameterizedType() {
                @Override
                public Type[] getActualTypeArguments() {
                    return actuals;
                }

                @Override
                public Type getRawType() {
                    return param.getRawType();
                }

                @Override
                public Type getOwnerType() {
                    return param.getOwnerType();
                }
            };
        } else if (type instanceof GenericArrayType) {
            GenericArrayType arrayType = (GenericArrayType) type;
            final Type componentType = resolveTypeVariables(root, arrayType.getGenericComponentType());
            if (componentType == null)
                return type;
            return new GenericArrayType() {
                @Override
                public Type getGenericComponentType() {
                    return componentType;
                }
            };
        } else {
            return type;
        }
    }

    /**
     * Finds an actual value of a type variable. The method looks in a class
     * hierarchy for a class defining the variable and returns the value if
     * present.
     *
     * @param root
     * @param typeVariable
     * @return actual type of the type variable
     */
    public static Type resolveTypeVariable(Class<?> root, TypeVariable<?> typeVariable) {
        if (typeVariable.getGenericDeclaration() instanceof Class<?>) {
            Class<?> classDeclaringTypeVariable = (Class<?>) typeVariable.getGenericDeclaration();
            Type[] types = findParameterizedTypes(root, classDeclaringTypeVariable);
            if (types == null)
                return null;
            for (int i = 0; i < types.length; i++) {
                TypeVariable<?> tv = classDeclaringTypeVariable.getTypeParameters()[i];
                if (tv.equals(typeVariable)) {
                    return types[i];
                }
            }
        }
        return null;
    }

    /**
     * Given a class and an interfaces, go through the class hierarchy to find
     * the interface and return its type arguments.
     *
     * @param classToSearch
     * @param interfaceToFind
     * @return type arguments of the interface
     */
    public static Type[] getActualTypeArgumentsOfAnInterface(Class<?> classToSearch, Class<?> interfaceToFind) {
        Type[] types = findParameterizedTypes(classToSearch, interfaceToFind);
        if (types == null)
            throw new RuntimeException("Unable to find type arguments of " + interfaceToFind);
        return types;
    }

    private static final Type[] EMPTY_TYPE_ARRAY = {};

    /**
     * Search for the given interface or class within the root's class/interface
     * hierarchy. If the searched for class/interface is a generic return an
     * array of real types that fill it out.
     *
     * @param root
     * @param searchedFor
     * @return
     */
    public static Type[] findParameterizedTypes(Class<?> root, Class<?> searchedFor) {
        if (searchedFor.isInterface()) {
            return findInterfaceParameterizedTypes(root, null, searchedFor);
        }
        return findClassParameterizedTypes(root, null, searchedFor);
    }

    public static Type[] findClassParameterizedTypes(Class<?> root, ParameterizedType rootType, Class<?> searchedForClass) {
        if (Object.class.equals(root))
            return null;

        Map<String, Type> typeVarMap = populateParameterizedMap(root, rootType);

        Class<?> superclass = root.getSuperclass();
        Type genericSuper = root.getGenericSuperclass();

        if (superclass.equals(searchedForClass)) {
            return extractTypes(typeVarMap, genericSuper);
        }

        if (genericSuper instanceof ParameterizedType) {
            ParameterizedType intfParam = (ParameterizedType) genericSuper;
            Type[] types = findClassParameterizedTypes(superclass, intfParam, searchedForClass);
            if (types != null) {
                return extractTypeVariables(typeVarMap, types);
            }
        } else {
            Type[] types = findClassParameterizedTypes(superclass, null, searchedForClass);
            if (types != null) {
                return types;
            }
        }
        return null;
    }

    private static Map<String, Type> populateParameterizedMap(Class<?> root, ParameterizedType rootType) {
        Map<String, Type> typeVarMap = new HashMap<String, Type>();
        if (rootType != null) {
            TypeVariable<? extends Class<?>>[] vars = root.getTypeParameters();
            for (int i = 0; i < vars.length; i++) {
                typeVarMap.put(vars[i].getName(), rootType.getActualTypeArguments()[i]);
            }
        }
        return typeVarMap;
    }

    public static Type[] findInterfaceParameterizedTypes(Class<?> root, ParameterizedType rootType, Class<?> searchedForInterface) {
        Map<String, Type> typeVarMap = populateParameterizedMap(root, rootType);

        for (int i = 0; i < root.getInterfaces().length; i++) {
            Class<?> sub = root.getInterfaces()[i];
            Type genericSub = root.getGenericInterfaces()[i];
            if (sub.equals(searchedForInterface)) {
                return extractTypes(typeVarMap, genericSub);
            }
        }

        for (int i = 0; i < root.getInterfaces().length; i++) {
            Type genericSub = root.getGenericInterfaces()[i];
            Class<?> sub = root.getInterfaces()[i];

            Type[] types = recurseSuperclassForInterface(searchedForInterface, typeVarMap, genericSub, sub);
            if (types != null)
                return types;
        }
        if (root.isInterface())
            return null;

        Class<?> superclass = root.getSuperclass();
        Type genericSuper = root.getGenericSuperclass();

        return recurseSuperclassForInterface(searchedForInterface, typeVarMap, genericSuper, superclass);
    }

    private static Type[] recurseSuperclassForInterface(Class<?> searchedForInterface, Map<String, Type> typeVarMap, Type genericSub, Class<?> sub) {
        if (genericSub instanceof ParameterizedType) {
            ParameterizedType intfParam = (ParameterizedType) genericSub;
            Type[] types = findInterfaceParameterizedTypes(sub, intfParam, searchedForInterface);
            if (types != null) {
                return extractTypeVariables(typeVarMap, types);
            }
        } else {
            Type[] types = findInterfaceParameterizedTypes(sub, null, searchedForInterface);
            if (types != null) {
                return types;
            }
        }
        return null;
    }

    private static Type[] extractTypeVariables(Map<String, Type> typeVarMap, Type[] types) {
        for (int j = 0; j < types.length; j++) {
            if (types[j] instanceof TypeVariable) {
                TypeVariable tv = (TypeVariable) types[j];
                types[j] = typeVarMap.get(tv.getName());
            } else {
                types[j] = types[j];
            }
        }
        return types;
    }

    private static Type[] extractTypes(Map<String, Type> typeVarMap, Type genericSub) {
        if (genericSub instanceof ParameterizedType) {
            ParameterizedType param = (ParameterizedType) genericSub;
            Type[] types = param.getActualTypeArguments();
            Type[] returnTypes = new Type[types.length];
            System.arraycopy(types, 0, returnTypes, 0, types.length);
            extractTypeVariables(typeVarMap, returnTypes);
            return returnTypes;
        } else {
            return EMPTY_TYPE_ARRAY;
        }
    }
}


package io.advantageous.qbit.kvstore;

import io.advantageous.boon.core.Str;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.redis.RedisClient;
import io.vertx.redis.RedisOptions;

import java.net.URI;

public class RedisKeyValueStoreBuilder {

    private RedisOptions redisOptions;
    private Vertx vertx;
    private RedisClient redisClient;
    private VertxOptions vertxOptions;

    public static RedisKeyValueStoreBuilder redisKeyValueStoreBuilder() {
        return new RedisKeyValueStoreBuilder();
    }

    /**
     * RedisKeyValueStoreBuilder redis://redisdb:<password>@<host>:<port>/0
     *
     * @return this
     */
    public RedisKeyValueStoreBuilder setRedisUri(final URI uri) {

        getRedisOptions().setPort(uri.getPort());
        getRedisOptions().setHost(uri.getHost());

        final String userInfo = uri.getUserInfo();
        final String[] split = Str.split(userInfo);

        if (split.length == 2) {
            getRedisOptions().setAuth(split[1]);
        } else if (split.length == 1) {
            getRedisOptions().setAuth(split[0]);
        }

        return this;
    }

    public VertxOptions getVertxOptions() {
        if (vertxOptions == null) {
            vertxOptions = new VertxOptions();
        }
        return vertxOptions;
    }

    public RedisKeyValueStoreBuilder setVertxOptions(VertxOptions vertxOptions) {
        this.vertxOptions = vertxOptions;
        return this;
    }

    public RedisClient getRedisClient() {

        if (redisClient == null) {
            redisClient = RedisClient.create(getVertx(), getRedisOptions());

        }
        return redisClient;
    }

    public RedisKeyValueStoreBuilder setRedisClient(RedisClient redisClient) {
        this.redisClient = redisClient;
        return this;
    }

    public RedisOptions getRedisOptions() {
        if (redisOptions == null) {
            redisOptions = new RedisOptions();
        }
        return redisOptions;
    }

    public RedisKeyValueStoreBuilder setRedisOptions(RedisOptions redisOptions) {
        this.redisOptions = redisOptions;
        return this;
    }

    public Vertx getVertx() {
        if (vertx == null) {
            vertx = Vertx.vertx(getVertxOptions());
        }
        return vertx;
    }

    public RedisKeyValueStoreBuilder setVertx(Vertx vertx) {
        this.vertx = vertx;
        return this;
    }

    public RedisKeyValueStore build() {
        return new RedisKeyValueStore(getRedisClient());
    }
}


/*
 * $#
 * FOS Weka
 *
 * Copyright (C) 2013 Feedzai SA
 *
 * This program 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.
 *
 * This program 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 this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #$
 */
package com.feedzai.fos.impl.weka.utils.pool;

import com.feedzai.fos.common.validation.Nullable;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;

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

/**
 * Extension of the @{GenericObjectPool} to auto populate based on the minIdle even if the monitor is not enabled.
 *
 * @param <T> the type of elements in the pool
 * @author Marco Jorge (marco.jorge@feedzai.com)
 */
public class AutoPopulateGenericObjectPool<T> extends GenericObjectPool<T> {
    private static final int DISABLED = -1;

    /**
     * Create a new pool with the given factory and configuration.
     *
     * @param factory the factory that produces the elements of the pool
     * @param config  the configuration of the pool
     * @throws Exception
     */
    public AutoPopulateGenericObjectPool(PoolableObjectFactory<T> factory, @Nullable Config config) throws Exception {
        super(factory, config);
        this.populate();
    }

    /**
     * Populate the pool up to minIdle instances. Is limited to maxIdle and maxActive.
     *
     * @throws Exception when could not borrow or return objects to the pool
     */
    private void populate() throws Exception {
        List<T> initializer = new ArrayList<T>(this.getMinIdle());

        for (int idx = 0; idx < this.getMinIdle() && (this.getMaxIdle() == DISABLED || idx < this.getMaxIdle()) && (this.getMaxActive() == DISABLED || idx < this.getMaxActive()); idx++) {
            initializer.add(this.borrowObject());
        }
        for (int idx = 0; idx < this.getMinIdle() && (this.getMaxIdle() == DISABLED || idx < this.getMaxIdle()) && (this.getMaxActive() == DISABLED || idx < this.getMaxActive()); idx++) {
            this.returnObject(initializer.get(idx));
        }
    }
}