/*-
 * #%L
 * Nazgul Project: nazgul-tools-visualization-model
 * %%
 * 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.model.diagram;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import java.util.Objects;

/**
 * NodeID statement Renderer, complying to the specification in the
 * DOT language specification.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
@XmlType(namespace = AbstractIdentifiable.NAMESPACE, propOrder = {"port"})
@XmlAccessorType(XmlAccessType.FIELD)
public class NodeID extends AbstractIdentifiable implements Comparable<NodeID> {

    /**
     * An optional 'port' definition of this NodeID, which provides
     */
    @XmlElement
    private Port port;

    /**
     * JAXB-friendly constructor, reserved for framework use.
     */
    public NodeID() {
        // Do nothing.
    }

    /**
     * Convenience constructor creating a NodeID instance with a null {@link Port}.
     *
     * @param id a non-null and non-empty identifier, assumed to be unique within a Graph.
     */
    public NodeID(final String id) {
        this(id, null);
    }

    /**
     * Compound constructor creating a {@link NodeID} with the supplied ID and optional
     * (i.e. nullable) Port values. Corresponds to node_id : ID [ port ] in the
     * DOT language specification.
     *
     * @param id   a non-null and non-empty identifier, assumed to be unique within a Graph.
     * @param port An optional (i.e. nullable) Port for this {@link NodeID}.
     */
    public NodeID(final String id, final Port port) {
        super(id);
        this.port = port;
    }

    /**
     * Assigns a Port to this {@link NodeID}.
     *
     * @param port a non-null Port instance.
     */
    public void setPort(final Port port) {

        // Check sanity
        if (port == null) {
            throw new IllegalArgumentException("Cannot handle null 'port' argument.");
        }

        // Assign internal state
        this.port = port;
    }

    /**
     * @return The optional (i.e. nullable) Port of this NodeID.
     */
    public Port getPort() {
        return port;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return super.toString() + ", (" + port + ")";
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(final Object o) {

        // Fail fast
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        // Delegate to internal state
        final NodeID that = (NodeID) o;
        return super.equals(that)
                && Objects.equals(port, that.port);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), port);
    }

    /**
     * Compares the supplied NodeID to this one.
     *
     * @param that A NodeID to compare to this one.
     * @return THe difference in
     */
    @Override
    public int compareTo(final NodeID that) {

        // Fail fast
        if (that == this) {
            return 0;
        } else if (null == that) {
            return -1;
        }

        // Delegate to internal state
        return this.getId().compareTo(that.getId());
    }
}


/*
 * 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.monitor.controller;

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

import com.googlecode.icegem.cacheutils.monitor.controller.event.NodeEvent;
import com.googlecode.icegem.cacheutils.monitor.controller.event.NodeEventHandler;

/**
 * Contains the list of events since the last execution of the operation
 * getAndClearEventslist(),
 */
public class BufferedNodeEventHandler implements NodeEventHandler {

	private List<NodeEvent> eventslist = new ArrayList<NodeEvent>();

	/**
	 * Adds one more event to the buffer
	 */
	public void handle(NodeEvent event) {
		eventslist.add(event);
	}

	/**
	 * Gets the copy of the buffer and clears the buffer.
	 *
	 * @return - the copy of events since the last call of this method.
	 */
	public List<NodeEvent> getAndClearEventslist() {
		ArrayList<NodeEvent> result = new ArrayList<NodeEvent>(eventslist);
		eventslist.clear();
		return result;
	}

}


/*-
 * #%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.javadoc;

import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.ProgramElementDoc;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.SourcePosition;
import se.jguru.nazgul.tools.visualization.spi.doclet.VisualizationDocletTag;
import se.jguru.nazgul.tools.visualization.spi.doclet.converter.Comparators;

import java.util.Arrays;
import java.util.Comparator;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * {@link RootDoc} implementation which swallows messages related to the
 * {@link se.jguru.nazgul.tools.visualization.spi.doclet.VisualizationDoclet}.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public class VisualizationWrappedRootDoc extends AbstractRootDocWrapper {

    /**
     * Constructs an {@link VisualizationWrappedRootDoc} instance around the supplied {@link RootDoc}.
     *
     * @param wrappedRootDoc A non-null {@link RootDoc} instance to be wrapped by this {@link VisualizationWrappedRootDoc}.
     */
    public VisualizationWrappedRootDoc(final RootDoc wrappedRootDoc) {
        super(wrappedRootDoc);
    }

    /**
     * Delegates to the wrapped {@link RootDoc} only if the supplied Message does not contain an
     * {@link se.jguru.nazgul.tools.visualization.spi.doclet.VisualizationDocletTag} identifier.
     * Otherwise, the message is ignored.
     *
     * @param msg The warning message.
     */
    @Override
    public void printWarning(final String msg) {

        // Swallow VisualizationDoclet warnings.
        if (!containsVisualizationDocletTag(msg)) {
            wrappedRootDoc.printWarning(msg);
        }
    }

    /**
     * Delegates to the wrapped {@link RootDoc} only if the supplied Message does not contain an
     * VisualizationDocletTag identifier. Otherwise, the message is ignored.
     *
     * @param pos The position in the Source.
     * @param msg The warning message.
     */
    @Override
    public void printWarning(final SourcePosition pos, final String msg) {

        // Swallow VisualizationDoclet warnings.
        if (!containsVisualizationDocletTag(msg)) {
            wrappedRootDoc.printWarning(pos, msg);
        }
    }

    /**
     * Checks if ths supplied message contains an {@link VisualizationDocletTag} reference, on the form
     * {@code '@nazgul_vis.xxxx '}.
     *
     * @param message The message to check.
     * @return {@code true} if the supplied message contains a {@link VisualizationDocletTag} reference.
     */
    public static boolean containsVisualizationDocletTag(final String message) {

        if (message != null) {
            for (VisualizationDocletTag current : VisualizationDocletTag.values()) {
                if (message.contains(current.toString() + " ")) {

                    // The message contains an entry on the form '@nazgul_vis.xxxx '
                    // which implies that it has to do with a VisualizationDoclet javadoc Tag.
                    return true;
                }
            }
        }

        // Nopes.
        return false;
    }

    /**
     * Finds all PackageDoc objects from the classes in this JavaDoc execution.
     *
     * @return a non-null (but possibly empty) SortedSet containing all {@link PackageDoc}s from the current execution.
     */
    public SortedSet<PackageDoc> findPackagesFromClassesInJavaDocRun() {

        final SortedSet<PackageDoc> toReturn = new TreeSet<>(Comparators.PACKAGE_NAME_COMPARATOR);
        final ClassDoc[] currentExecutionClasses = classes();

        if (currentExecutionClasses != null) {

            Arrays.stream(currentExecutionClasses)
                    .map(ProgramElementDoc::containingPackage)
                    .forEach(toReturn::add);
        }

        // All Done.
        return toReturn;
    }

    /**
     * Sorts all ClassDocs retrieved within the current JavaDoc compilation run into their respective PackageDocs.
     *
     * @return A SortedMap relating all ClassDocs to their respective PackageDoc.
     */
    public SortedMap<PackageDoc, SortedSet<ClassDoc>> sortClassesPerPackage() {
        return Comparators.sortClassesPerPackage(classes());
    }
}


/*
 * 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 static java.util.Arrays.asList;

import java.io.InvalidClassException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.googlecode.icegem.serialization.AutoSerializable;
import com.googlecode.icegem.serialization.SinceVersion;
import com.googlecode.icegem.serialization.Transient;

/**
 * Util
 *
 * @author igolovach
 */

public class Introspector {

    // -------------------------- PUBLIC

    public static void checkClassIsSerialized(final Class<?> clazz) throws InvalidClassException {
        // class
        checkClassIsAnnotationMarked(clazz);
        checkClassIsPublic(clazz);
        checkClassIsNotNested(clazz);
        // constructor
        Constructor<?> constructor = checkConstructorNoArg(clazz);
        checkConstructorPublic(constructor);
        checkConstructorWithoutExceptions(constructor);
        checkParentConstructor(clazz);
        // getters
        Map<String, Method> gettersMap = getPropertyGetters(clazz);
        for (Map.Entry<String, Method> getterEntry : gettersMap.entrySet()) {
            Method getter = getterEntry.getValue();
            checkGetterPublic(getter);
            checkGetterNoArg(getter);
            checkGetterWithoutExceptions(getter);
            Method setter = getSetterForGetter(clazz, getter);
            checkSetterPublic(setter);
            checkSetterOneArg(setter);
            checkSetterReturnVoid(setter);
            checkSetterWithoutExceptions(setter);
            checkGetterSetterTheSameType(getter, setter);
        }
    }

    public static List<XProperty> getProperties(final Class<?> clazz) {
        try {
            checkClassIsSerialized(clazz);
        } catch (InvalidClassException e) {
            //todo: or InvalidClassException?
            throw new RuntimeException("Method call getFields(...) for not serialized class " + clazz.getName() + " is incorrect", e);
        }
        Map<String, Method> gettersMap = getPropertyGetters(clazz);
        final ArrayList<XProperty> result = new ArrayList<XProperty>();
        for (Map.Entry<String, Method> entry : gettersMap.entrySet()) {
            final Method method = entry.getValue();

            int sinceVersion = getMethodSinceVersion(method);

            boolean isBoolean = false;
            if (method.getName().startsWith("is")) {
                isBoolean = true;
            }
            result.add(new XProperty(entry.getKey(), method.getReturnType(), method.getDeclaringClass(), sinceVersion, isBoolean));
        }
        return result;
    }

    private static int getMethodSinceVersion(Method method) {
        int sinceVersion = 1;
        SinceVersion annotation = method.getAnnotation(SinceVersion.class);
        if (annotation != null) {
        	sinceVersion = ((SinceVersion) annotation).value();
            if (sinceVersion < 1) {
                throw new IllegalArgumentException("@SinceVersion must be positive at " + method);
            }
        }
        return sinceVersion;
	}

    // -------------------------- PROTECTED

    /**
     * Checks if parent class (if exists) is annotated as AutoSerializable, or have parameterless public constructor .
     * @param clazz
     * @throws java.io.InvalidClassException
     */
    protected static void checkParentConstructor(Class<?> clazz) throws InvalidClassException {
        if (clazz != Object.class) {
        Class<?> parentClazz = clazz.getSuperclass();
            try {
                checkConstructorNoArg(parentClazz);
            } catch (InvalidClassException e) {
                checkClassIsAnnotationMarked(parentClazz);
            }
        }
    }

    protected static void checkGetterSetterTheSameType(Method getter, Method setter) throws InvalidClassException {
        Class<?> getterType = getter.getReturnType();
        Class<?> setterType = setter.getParameterTypes()[0];
        if (getterType != setterType) {
            throw new InvalidClassException("Setter " + setter.getName() + " in class " + setter.getDeclaringClass().getName() +
                    " return " + setter.getReturnType().getName() +
                    " but getter " + getter.getName() + " in class " + getter.getDeclaringClass().getName() +
                    " has as parameter type " + getterType);
        }
    }

    protected static void checkSetterReturnVoid(Method setter) throws InvalidClassException {
        if (setter.getReturnType() != void.class) {
            throw new InvalidClassException("Setter " + setter.getName() + " in class " + setter.getDeclaringClass().getName() + " return not void but " + setter.getReturnType().getName());
        }
    }

    private static Method getSetterForGetter(Class<?> clazz, Method getter) throws InvalidClassException {
    	String fieldName = "";
    	String name = getter.getName();
		if(name.startsWith("get")){
    		fieldName = name.substring(3, 4).toUpperCase() + name.substring(4);
    	} else if (name.startsWith("is")){
    		fieldName = name.substring(2, 3).toUpperCase() + name.substring(3);
    	}
        final String setterName = "set" + fieldName;
        Class<?> param = getter.getReturnType();
        try {
            return clazz.getMethod(setterName, param);
        } catch (NoSuchMethodException e) {
            throw new InvalidClassException("There is no public setter with param type " + param.getName() + " for getter " + name + " in class " + clazz.getName());
        }
    }

    protected static void checkSetterWithoutExceptions(Method setter) throws InvalidClassException {
        final Class<?>[] exceptions = setter.getExceptionTypes();
        if (exceptions.length != 0) {
            throw new InvalidClassException("Setter " + setter.getName() + " in class " + setter.getDeclaringClass().getName() + " throws exceptions " + asList(exceptions));
        }
    }

    protected static void checkGetterWithoutExceptions(Method getter) throws InvalidClassException {
        final Class<?>[] exceptions = getter.getExceptionTypes();
        if (exceptions.length != 0) {
            throw new InvalidClassException("Getter " + getter.getName() + " in class " + getter.getDeclaringClass().getName() + " throws exceptions " + asList(exceptions));
        }
    }

    /**
     * 1) move hierarchy up
     * 2) look for method with name
     * 3) check @TransientGetter
     *
     * @param clazz
     * @return
     */
    protected static Map<String, Method> getPropertyGetters(Class<?> clazz) { //todo: clazz.getDeclaredMethods() + hierarchy up OR clazz.getMethods()?
        Map<String, Method> result = new HashMap<String, Method>();

        if (clazz != Object.class) {
            Method[] methodArr = clazz.getMethods();
            for (Method method : methodArr) {
                if (isNameSuitableForGetter(method)) {
                    if (method.getDeclaringClass() != Object.class && !method.isBridge()) { //remove Object.getClass()
                        final Annotation[] annArr = method.getDeclaredAnnotations();
                        boolean find = false;
                        for (Annotation ann : annArr) {
                            if (ann.annotationType() == Transient.class) {
                                find = true;
                                break;
                            }
                        }
                        if (!find) {
                            String fieldName;
                            if (method.getReturnType() == Boolean.TYPE && method.getName().startsWith("is"))
                                fieldName = method.getName().substring(2, 3).toLowerCase() + method.getName().substring(3);
                            else
                                fieldName = method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4);
                            result.put(fieldName, method);
                        }
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }

        return result;
    }

    private static boolean isNameSuitableForGetter(Method method) {
    	String propFirstChar = "";
        if(method.getName().startsWith("get")
                && method.getName().length() > 3){
            propFirstChar = method.getName().substring(3, 4);
        } else if(method.getReturnType() == Boolean.TYPE &&
        		method.getName().startsWith("is") &&
        		method.getName().length() > 2){
                propFirstChar = method.getName().substring(2, 3);
        }
        return propFirstChar.length()!=0 && propFirstChar.toUpperCase().equals(propFirstChar);
    }

    protected static void checkSetterPublic(Method setter) throws InvalidClassException {
        if (!Modifier.isPublic(setter.getModifiers())) {
            throw new InvalidClassException("Setter " + setter.getName() + "in class " + setter.getDeclaringClass() + " is not public but " + Modifier.toString(setter.getModifiers()));
        }
    }

    protected static void checkSetterOneArg(Method setter) throws InvalidClassException {
        Class<?>[] types = setter.getParameterTypes();
        if (types.length != 1) {
            throw new InvalidClassException("Setter " + setter.getName() + " in class " + setter.getDeclaringClass() + " do not have 1 arg but" + types.length + ": " + asList(types));
        }
    }

    protected static void checkGetterPublic(Method getter) throws InvalidClassException {
        if (!Modifier.isPublic(getter.getModifiers())) {
            throw new InvalidClassException("Getter " + getter.getName() + " in class " + getter.getDeclaringClass() + " is not public but " + Modifier.toString(getter.getModifiers()));
        }
    }

    protected static void checkGetterNoArg(Method getter) throws InvalidClassException {
        Class<?>[] types = getter.getParameterTypes();
        if (types.length != 0) {
            throw new InvalidClassException("Getter " + getter.getName() + " in class " + getter.getDeclaringClass() + " have arg " + asList(types));
        }
    }

    protected static void checkClassIsAnnotationMarked(final Class<?> clazz) throws InvalidClassException {
        {if (clazz.getAnnotation(AutoSerializable.class) == null)
            throw new InvalidClassException("Class " + clazz.getName() + " do not contains annotation @" + AutoSerializable.class.getSimpleName());
        }
    }

    protected static void checkClassIsPublic(final Class<?> clazz) throws InvalidClassException {
        if (!Modifier.isPublic(clazz.getModifiers())) {
            throw new InvalidClassException("Class " + clazz.getName() + " is not public but " + Modifier.toString(clazz.getModifiers()));
        }
    }

    protected static void checkClassIsNotNested(final Class<?> clazz) throws InvalidClassException {
        // There are five kinds of classes (or interfaces):
        // a) Top level classes
        // b) Nested classes (static member classes)
        // c) Inner classes (non-static member classes)
        // d) Local classes (named classes declared within a method)
        // e) Anonymous classes

        if (clazz.getEnclosingClass() != null) {
            throw new InvalidClassException("Class " + clazz.getName() + " is not top_level and has enclosing class " + clazz.getEnclosingClass().getName());
        }
    }

    protected static Constructor<?> checkConstructorNoArg(final Class<?> clazz) throws InvalidClassException {
        try {
            return clazz.getDeclaredConstructor(new Class[0]);
        } catch (NoSuchMethodException e) {
            throw new InvalidClassException("Class " + clazz.getName() + " haven't got public no-arg constructor");
        }
    }

    protected static void checkConstructorPublic(final Constructor<?> constructor) throws InvalidClassException {
        if (!Modifier.isPublic(constructor.getModifiers())) {
            throw new InvalidClassException("No-arg constructor of class " + constructor.getDeclaringClass().getName() + " is not public");
        }
    }

    protected static void checkConstructorWithoutExceptions(final Constructor<?> constructor) throws InvalidClassException {
        final Class<?>[] exceptions = constructor.getExceptionTypes();
        if (exceptions.length != 0) {
            throw new InvalidClassException("No-arg constructor of class " + constructor.getDeclaringClass().getName() + " is throws exceptions " + asList(exceptions));
        }
    }
}


/*
 * 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.IOException;
import java.io.InvalidClassException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javassist.CannotCompileException;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.LoaderClassPath;
import javassist.NotFoundException;

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

import com.googlecode.icegem.serialization.AutoSerializable;
import com.googlecode.icegem.serialization.BeanVersion;
import com.googlecode.icegem.serialization.Configuration;

/**
 * <pre> Class that generate for class XYZ.class special serializer: class XYZDataSerializer extends DataSerializer
 * {...} by Javassist lib. </pre>
 *
 * @author igolovach
 */

public class DataSerializerGenerator {

    private static Logger logger = LoggerFactory.getLogger(DataSerializerGenerator.class);

    /**
     * All DataSerializer-s will extend this class.
     */
    public static final String PARENT_CLASS = "com.gemstone.gemfire.DataSerializer";

    private static final Map<Integer, String> dataSerializerID2ClassNameMap = new HashMap<Integer, String>();
    private static CodeGenerationListener listener;

    private static ClassPool newClassPool(ClassLoader loader) {
        ClassPool result = new ClassPool(null); // arg - parent ClassPool
        result.appendClassPath(new ClassClassPath(java.lang.Object.class)); // its equivalent of appendSystemPath();
        result.appendClassPath(new LoaderClassPath(loader));

        return result;
    }

    public static synchronized List<Class<?>> generateDataSerializerClasses(ClassLoader classLoader,
            Class<?>... classArray) throws CannotCompileException, InvalidClassException {
        return generateDataSerializerClasses(classLoader, Arrays.asList(classArray), null);
    }

    public static synchronized List<Class<?>> generateDataSerializerClasses(ClassLoader classLoader,
            List<Class<?>> classList) throws CannotCompileException, InvalidClassException {
        return generateDataSerializerClasses(classLoader, classList, null);
    }

    /**
     * 2-stage compilation scheme of group of classes: <p> hack from
     * http://www.csg.is.titech.ac.jp/~chiba/javassist/tutorial/tutorial2.html#add <p> order of return of
     * serializer-classes in response corresponds to order of arg-classes
     */
    public static synchronized List<Class<?>> generateDataSerializerClasses(ClassLoader classLoader,
            List<Class<?>> classList, String outputDir) throws CannotCompileException, InvalidClassException {
        checkClassesValid(classList);

        List<CtClass> dataSerializerClassList = new ArrayList<CtClass>();

        // create new ClassPool for every method call
        // 1) low memory consumption - any caches with internal data structures of created classes
        // 2) any collision between methods called serially - all crated classes are forgotten
        // 3) any references on created classes from this lib
        ClassPool classPool = newClassPool(classLoader);

        // #1: create dataSerializers with stubs toData/fromData
        for (Class<?> clazz : classList) {
            String serializerClsName = createDataSerializerClassNameForClass(clazz);

            if (existsClass(serializerClsName, classLoader)) {
                logger.debug("Serializer for class {} exists. Skipping generation", clazz.getName());
                break;
            }

            // create class
            CtClass cc = createClass(classPool, clazz, serializerClsName);

            dataSerializerClassList.add(cc);

            // add static Register
            addStaticConstruct(clazz, cc, serializerClsName);
            // add methods
            addMethodGetId(clazz, cc);
            addMethodGetSupportedClasses(clazz, cc);
            // add stub-methods toData/fromData
            addMethodToDataStub(clazz, cc);
            addMethodFromDataStub(clazz, cc);
            // we need this for Javassist do some magic with CtClass
            try {
                cc.toBytecode();
            } catch (IOException e) {
                throw new CannotCompileException(
                        "Error during end of compilation phase #1 (call CtClass.toBytecode() for some Javassist-magic with CtClass) for "
                                + cc.getName(), e);
            } catch (CannotCompileException e) {
                throw new CannotCompileException(
                        "Error during end of compilation phase #1 (call CtClass.toBytecode() for some Javassist-magic with CtClass) for "
                                + cc.getName(), e);
            }
        }

        List<Class<?>> result = new ArrayList<Class<?>>();
        // #2: change stubs toData/fromData in dataSerializers -> real methods
        for (int k = 0; k < classList.size(); k++) {
            Class<?> clazz = classList.get(k);
            CtClass cc = dataSerializerClassList.get(k);
            // make changing methods real: defrost CtClass
            cc.defrost();
            // change stub toData/fromData -> real methods
            addMethodToData(clazz, cc);
            addMethodFromData(clazz, cc);
            // generate result
            final Class<?> resultClass;
            try {
                resultClass = cc.toClass(classLoader, null); // ProtectionDomain == null
                logger.info("compiled data serializer for class: {}; id: {}; version: {}",
                        new Object[] { clazz, clazz.getAnnotation(AutoSerializable.class).dataSerializerID()
                                , clazz.getAnnotation(BeanVersion.class).value() });
                if ((outputDir != null) && (outputDir.length() > 0)) {
                    try {
                        cc.writeFile(outputDir);
                    } catch (IOException e) {
                        throw new RuntimeException("couldn't save DataSerializer for class " + clazz.getName(), e);
                    }
                }
            } catch (CannotCompileException e) {
                throw new CannotCompileException(
                        "Error during end of compilation phase #2 (call CtClass.toClass()) for " + cc.getName()
                                + ". Probably you second time try generate and load DataSerializer class "
                                + cc.getName() + " for class " + clazz.getName(), e);
            }

            // dump code to listener
            if (listener != null) {
                listener.generated(clazz.getName(), cc.getName(), new ClassProcessor().process(new XClass(clazz), cc.getName()));
            }

            result.add(resultClass);
        }

        return result;
    }

    // todo: what with thread-safeting?

    /**
     * TODO.
     *
     * @param clsName
     * @param clsLoader
     * @return
     */
    private static boolean existsClass(String clsName, ClassLoader clsLoader) {
        String resource = clsName.replace('.', '/');
        return clsLoader.getResource(resource) != null;
    }

    public static synchronized void registerCodeGenerationListener(CodeGenerationListener l) {
        listener = l;
    }

    // ------------------------ PRIVATE

    private static void checkClassesValid(List<Class<?>> classList) throws InvalidClassException {
        // check classes is valid for generating DataSerializer, if not - throw NotSerializableException with detailed
        // reason
        for (Class<?> clazz : classList) {
            Introspector.checkClassIsSerialized(clazz);
        }

        // check classes do not contain duplicated @AutoSerializable.dataSerializerID
        checkDataSerializerIDIsUnique(classList);
    }

    private static void checkDataSerializerIDIsUnique(List<Class<?>> classList) throws InvalidClassException {
        for (Class<?> clazz : classList) {
            AutoSerializable annotation = clazz.getAnnotation(AutoSerializable.class);
            int dataSerializerID = annotation.dataSerializerID();
            if (dataSerializerID2ClassNameMap.containsKey(dataSerializerID)) {
                throw new InvalidClassException("Classes " + dataSerializerID2ClassNameMap.get(dataSerializerID)
                        + " and " + clazz.getName()
                        + " contain duplicated value of @AutoSerializable.dataSerializerID: " + dataSerializerID); // todo:
                                                                                // right
                                                                                // ex
                                                                                // type?
            }
            dataSerializerID2ClassNameMap.put(dataSerializerID, clazz.getName());
        }
    }

    private static String createDataSerializerClassNameForClass(Class<?> clazz) {
        String dataSerializerPackage = Configuration.get().getDataSerializerPackage();
        return dataSerializerPackage + "." + clazz.getName() + "DataSerializer";
    }

    private static CtClass createClass(ClassPool classPool, Class<?> baseClass, final String newSerializerClassName)
            throws CannotCompileException {
        final CtClass parentClass;
        try {
            parentClass = classPool.get(PARENT_CLASS);
        } catch (NotFoundException e) {
            throw new CannotCompileException("There is no " + DataSerializerGenerator.PARENT_CLASS
                    + " in classpath of context ClassLoader for " + baseClass.getName(), e);
        }

        try {
            return classPool.makeClass(newSerializerClassName, parentClass);
        } catch (RuntimeException e) { // javadoc:makeClass(): if the existing class is frozen.
            throw new CannotCompileException("There is some internal error in our code (probably class "
                    + newSerializerClassName + " exists and frozen) for " + baseClass.getName(), e);
        }
    }

    private static void addStaticConstruct(Class<?> baseClass, CtClass cc, String serializerClsName) throws CannotCompileException {
        final String src = new StaticConstructorGenerator().process(new XClass(baseClass), serializerClsName);
        CtConstructor staticConstructor;
        try {
        	CtField metaInfoField= CtField.make("public static final com.googlecode.icegem.serialization.codegen.VersionMap VERSION_METADATA;", cc);
        	cc.addField(metaInfoField);

        	staticConstructor = cc.makeClassInitializer();
            staticConstructor.insertBefore(src);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(formatMsg("Cann't add static block for class ", src, baseClass, cc), e);
        }
    }

    private static void addMethodGetId(Class<?> baseClass, CtClass cc) throws CannotCompileException {
        XClass xClass = new XClass(baseClass);
        final String src = new MethodGetIdProcessor().process(xClass);
        CtMethod methodGetId;
        try {
            methodGetId = CtNewMethod.make(src, cc, null, null);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(formatMsg("Can't compile method '.getId()'\n", src, baseClass, cc), e);
        }
        try {
            cc.addMethod(methodGetId);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(formatMsg("Can compile but can't add compiled method '.getId()'\n", src,
                    baseClass, cc), e);
        }
    }

    private static void addMethodGetSupportedClasses(Class<?> baseClass, CtClass cc) throws CannotCompileException {
        XClass xClass = new XClass(baseClass);
        final String src = new MethodGetSupportedClassesProcessor().process(xClass);
        CtMethod methodGetSupportedClasses;
        try {
            methodGetSupportedClasses = CtNewMethod.make(src, cc, null, null);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(formatMsg("Can't compile method '.getSupportedClasses()'\n", src,
                    baseClass, cc), e);
        }
        try {
            cc.addMethod(methodGetSupportedClasses);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(formatMsg(
                    "Can compile but can't add compiled method '.getSupportedClasses()'\n", src, baseClass, cc), e);
        }
    }

    private static void addMethodToDataStub(Class<?> baseClass, CtClass cc) throws CannotCompileException {
        XClass xClass = new XClass(baseClass);
        final String src = new MethodToDataStubProcessor().process(xClass);
        CtMethod methodToData;
        try {
            methodToData = CtNewMethod.make(src, cc, null, null);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(formatMsg(
                    "Can't compile stub method '.toData()' (compilation phase #1)\n", src, baseClass, cc), e);
        }
        try {
            cc.addMethod(methodToData);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(formatMsg(
                    "Can compile stub but can't add compiled method '.toData()' (compilation phase #1)\n", src,
                    baseClass, cc), e);
        }
    }

    private static void addMethodFromDataStub(Class<?> baseClass, CtClass cc) throws CannotCompileException {
        XClass xClass = new XClass(baseClass);
        final String src = new MethodFromDataStubProcessor().process(xClass);
        CtMethod methodFromData;
        try {
            methodFromData = CtNewMethod.make(src, cc, null, null);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(formatMsg(
                    "Can't compile stub method '.fromData(...)' (compilation phase #1)\n", src, baseClass, cc), e);
        }
        try {
            cc.addMethod(methodFromData);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(formatMsg(
                    "Can compile stub but can't add compiled method '.fromData(...)' (compilation phase #1)\n", src,
                    baseClass, cc), e);
        }
    }

    private static void addMethodToData(Class<?> baseClass, CtClass cc) throws CannotCompileException {
        XClass xClass = new XClass(baseClass);
        final String src = new MethodToDataProcessor().process(xClass);
        // compile version #2
        CtMethod methodToData;
        try {
            methodToData = CtNewMethod.make(src, cc, null, null);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(formatMsg("Can't compile method '.toData()' (compilation phase #2)\n",
                    src, baseClass, cc), e);
        }
        // find version #1
        CtMethod removedStubMethod;
        try {
            removedStubMethod = cc.getDeclaredMethod("toData");
        } catch (NotFoundException e) {
            throw new CannotCompileException(formatMsg(
                    "Can't find stub method '.toData()' (from compilation phase #1)\n", src, baseClass, cc), e);
        }
        // remove version #1
        try {
            cc.removeMethod(removedStubMethod);
        } catch (NotFoundException e) {
            throw new CannotCompileException(formatMsg(
                    "Can find but can't remove stub method '.toData()' (from compilation phase #1)\n", src, baseClass,
                    cc), e);
        }
        // add version #2
        try {
            cc.addMethod(methodToData);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(
                    formatMsg(
                            "Can compile new, find old, remove old but can't add new compiled method '.toData()' (compilation phase #2)\n",
                            src, baseClass, cc), e);
        }
    }

    private static void addMethodFromData(Class<?> baseClass, CtClass cc) throws CannotCompileException {
        XClass xClass = new XClass(baseClass);
        final String src = new MethodFromDataProcessor().process(xClass);
        // compile version #2
        CtMethod methodFromData;
        try {
            methodFromData = CtNewMethod.make(src, cc, null, null);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(formatMsg(
                    "Can't compile method '.fromData(...)' (compilation phase #2)\n", src, baseClass, cc), e);
        }
        // find version #1
        CtMethod removedStubMethod;
        try {
            removedStubMethod = cc.getDeclaredMethod("fromData");
        } catch (NotFoundException e) {
            throw new CannotCompileException(formatMsg(
                    "Can't find stub method '.fromData(...)' (from compilation phase #1)\n", src, baseClass, cc), e);
        }
        // remove version #1
        try {
            cc.removeMethod(removedStubMethod);
        } catch (NotFoundException e) {
            throw new CannotCompileException(formatMsg(
                    "Can find but can't remove stub method '.fromData(...)' (from compilation phase #1)\n", src,
                    baseClass, cc), e);
        }
        // add version #2
        try {
            cc.addMethod(methodFromData);
        } catch (CannotCompileException e) {
            throw new CannotCompileException(
                    formatMsg(
                            "Can compile new, find old, remove old but can't add new compiled method '.fromData(...)' (compilation phase #2)\n",
                            src, baseClass, cc), e);
        }
    }

    private static String formatMsg(String headerMsg, String methodSrc, Class<?> baseClass, CtClass cc) {
        return headerMsg + "\n"
                + "source:\n"
                + methodSrc + "\n"
                + "method generated for DataSerializer for class: " + baseClass + "\n"
                + "partially created class of DataSerializer: " + cc + "\n";
    }
}


/*
 * #%L
 * Nazgul Project: nazgul-tools-codestyle
 * %%
 * Copyright (C) 2010 - 2015 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/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.
 * #L%
 */

package se.jguru.nazgul.tools.codestyle.enforcer.rules;

import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
import org.apache.maven.project.MavenProject;

/**
 * Enforcer rule to validate ProjectType compliance, to harmonize the pom structure in terms
 * of groupId, artifactId, packaging and (rudimentary) content checks.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public class ValidateProjectType extends AbstractNonCacheableEnforcerRule {

    /**
     * Delegate method, implemented by concrete subclasses.
     *
     * @param project The active MavenProject.
     * @param helper  The EnforcerRuleHelper instance, from which the MavenProject has been retrieved.
     * @throws se.jguru.nazgul.tools.codestyle.enforcer.rules.RuleFailureException If the enforcer rule
     *                                                                             was not satisfied.
     */
    @Override
    @SuppressWarnings("PMD.PreserveStackTrace")
    protected void performValidation(final MavenProject project, final EnforcerRuleHelper helper)
            throws RuleFailureException {

        try {
            ProjectType.getProjectType(project);
        } catch (IllegalArgumentException e) {
            throw new RuleFailureException(e.getMessage());
        }
    }

    /**
     * @return A human-readable short description for this AbstractEnforcerRule.
     * (Example: "No -impl dependencies permitted in this project")
     */
    @Override
    protected String getShortRuleDescription() {
        return "POM groupId, artifactId and packaging must comply with defined standard";
    }
}


/*
 * 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.impl;

import com.googlecode.icegem.serialization.Configuration;
import com.googlecode.icegem.serialization.codegen.CodeGenUtils;
import com.googlecode.icegem.serialization.codegen.XProperty;

import static com.googlecode.icegem.serialization.codegen.CodeGenUtils.tab;

/**
 * Generate code that
 * 1) get 'custom class' field value from bean
 * 2) if null -> writeByte(0) + exit
 * 3) if !null writeByte(1) + write field value it to DataOutput
 * for method DataSerializer.fromData(...)
 *
 * @author igolovach
 */

class ToDataFieldResolveClassByGemFireProcessor {

//    public String process(XProperty field) {
//        String fieldName = field.getField().getName();
//        String shortClassName = field.getField().getType().getSimpleName();
//        String getter = "get" + CodeGenUtils.firstLetterToUpperCase(fieldName) + "()";
//
//        return "if (concrete." + getter + " == null) {\n" +
//                tab("out.writeByte(0);\n") +
//                "} else {\n" +
//                tab("out.writeByte(1);\n" + //todo: CodeGenUtils.DATA_SERIALIZER_PACKAGE + "." + shortClassName -> can be collision between: a.b.bean vs x.y.Bean
//                        "new " + CodeGenUtils.DATA_SERIALIZER_PACKAGE + "." + shortClassName + "DataSerializer()" + //todo: generate full name in one place, not here
//                        ".toData(" +
//                        "concrete." + getter + ", " +
//                        "out);\n") +
//                "}\n";
//    }

    public String process(XProperty field) {
        String fieldName = field.getName();
        String getter = "get" + CodeGenUtils.firstLetterToUpperCase(fieldName) + "()";

        String allowJavaSerialization = Boolean.toString(Configuration.get().isJavaSerializationEnabled());

        return "if (concrete." + getter + " == null) {\n" +
                tab("out.writeByte(0);\n") +
                "} else {\n" +
                tab("out.writeByte(1);\n") +
                tab("com.gemstone.gemfire.DataSerializer.writeObject(concrete." + getter + ", out, " + allowJavaSerialization + ");\n") +
                "}\n";
    }
}


/*
 * 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.regioncomparator;

import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.execute.FunctionAdapter;
import com.gemstone.gemfire.cache.execute.FunctionContext;
import com.gemstone.gemfire.cache.partition.PartitionRegionHelper;
import com.gemstone.gemfire.internal.cache.ForceReattemptException;
import com.gemstone.gemfire.internal.cache.PartitionedRegion;
import com.gemstone.gemfire.internal.cache.PartitionedRegionHelper;
import com.gemstone.gemfire.internal.cache.partitioned.PRLocallyDestroyedException;

import java.util.*;

/**
 * return keys for a particular bucket or all keys for replicated region
 * User: Artem Kondratyev, e-mail: kondratevae@gmail.com
 */
public class GetKeysFunction extends FunctionAdapter {

    private static final long serialVersionUID = -1467135292875589062L;
    private static int BATCH_SIZE = 3;                  //todo: how to customize this parameter. also it exists in regioninfofunction. ugly.

    @Override
    public void execute(FunctionContext functionContext) {
        Map<String, Object> args = (HashMap) functionContext.getArguments();
        String regionName = (String) args.get("regionName");
        Region region = CacheFactory.getAnyInstance().getRegion(regionName);
        if (region == null) {
            functionContext.getResultSender().lastResult(new HashSet());
            return;
        }

        int bucket = 0;
        if (PartitionRegionHelper.isPartitionedRegion(region)) {
            bucket = (Integer) args.get("bucket");                                              //todo: NPE if actual region is different from parameter (replicate vs partition)
            PartitionedRegion pr = (PartitionedRegion) region;
            Set keys = new HashSet();
            if (pr.getDataStore().getAllLocalPrimaryBucketIds().contains(bucket)) {
                try {
                    keys.addAll(pr.getDataStore().getKeysLocally(bucket));
                } catch (Exception e) {
                    throw new RuntimeException("error getting local keys for bucket " + bucket, e);
                }
            }
            functionContext.getResultSender().lastResult((HashSet) keys);
            return;
        }

        //todo: it's ugly. better: to ask a particular batch of keys (between oldBatch upper bound and nextBatch lower bound)
        Set keys = region.keySet();
        Iterator iterator = keys.iterator();
        Set keysBatch = new HashSet(BATCH_SIZE);
        while(iterator.hasNext()) {
            keysBatch.add(iterator.next());
            if ((keysBatch.size() + 1) % BATCH_SIZE  == 0) {
                functionContext.getResultSender().sendResult((HashSet) keysBatch);
                keysBatch = new HashSet(BATCH_SIZE);
            }
        }
        functionContext.getResultSender().lastResult((HashSet) keysBatch);
    }

    @Override
    public String getId() {
        return getClass().getName();
    }
}


/*-
 * #%L
 * Nazgul Project: nazgul-tools-visualization-api
 * %%
 * 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.api;

import se.jguru.nazgul.tools.visualization.model.diagram.statement.Statement;
import se.jguru.nazgul.tools.visualization.model.diagram.statement.Subgraph;

/**
 * Abstract implementation of a Renderer yielding String results, and sporting facilities for pretty
 * printing/indenting the String result.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public abstract class AbstractStringRenderer<T> implements Renderer<String> {

    // Internal state
    private Class<T> acceptedType;

    /**
     * Compound constructor creating an {@link AbstractStringRenderer} wrapping the supplied acceptedType.
     *
     * @param acceptedType A non-null Class defining the type of entity acceptable to this
     *                     {@link AbstractStringRenderer}.
     */
    protected AbstractStringRenderer(final Class<T> acceptedType) {

        // Check sanity
        if (acceptedType == null) {
            throw new IllegalArgumentException("Cannot handle null 'acceptedType' argument.");
        }

        // Assign internal state
        this.acceptedType = acceptedType;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final String render(final RenderConfiguration configuration, final Object entity) {

        // Check sanity
        if (configuration == null) {
            throw new IllegalArgumentException("Cannot handle null 'configuration' argument.");
        }
        if (entity == null) {
            throw new IllegalArgumentException("Cannot handle null 'entity' argument.");
        }
        if (!accept(entity)) {
            throw new IllegalArgumentException("Entity of type [" + entity.getClass().getName()
                    + "] was not accepted for rendering. Only [" + acceptedType.getName()
                    + "] types can be rendered by this " + getClass().getSimpleName() + " renderer.");
        }

        // Find out the combination of newline and ';' which should be emitted after rendering.
        String epilogue = "";
        if (entity instanceof Subgraph) {
            epilogue = configuration.getNewline();
        } else if (entity instanceof Statement) {
            epilogue = " ;" + configuration.getNewline();
        }

        // All Done.
        final T castEntity = acceptedType.cast(entity);
        return doRender(configuration, castEntity) + epilogue;
    }

    /**
     * Implement this method to provide the algorithms rendering the supplied (and typed) entity as a String.
     * Do not add a {@link RenderConfiguration#NEWLINE} afterwards, as it is done by the render method itself.
     *
     * @param config The non-null RenderConfiguration.
     * @param entity The non-null entity of type T.
     * @return The rendered value for the supplied entity.
     */
    protected abstract String doRender(final RenderConfiguration config, final T entity);

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean accept(final Object entity) {
        return entity != null && acceptedType.isAssignableFrom(entity.getClass());
    }

    /**
     * Quotes the supplied string (i.e. surrounds it with "'s).
     *
     * @param toQuote The string to quote.
     * @return the supplied string surrounded with double quotes.
     */
    protected final String quote(final String toQuote) {
        return "\"" + toQuote + "\"";
    }

    /**
     * @return A standard string representation of this {@link AbstractStringRenderer}.
     */
    @Override
    public String toString() {
        return "[" + getClass().getSimpleName() + "]";
    }
}


package nl.pwiddershoven.jfactory;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

@SuppressWarnings("unchecked")
class ReflectionUtils {

    /**
     * Set the property identified by name to the provided value.
     * @param target
     * @param name
     * @param value
     * @return true on success, false if property wasn't found.
     */
    public static boolean setProperty(Object target, String name, Object value) {
        try {
            for (PropertyDescriptor pd : Introspector.getBeanInfo(target.getClass()).getPropertyDescriptors()) {
                if (pd.getWriteMethod() != null && pd.getName().equals(name)) {
                    pd.getWriteMethod().invoke(target, value);
                    return true;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return false;
    }

    /**
     * Set the field identified by name to the given value.
     * @param target
     * @param name
     * @param value
     * @return true on success, false if field wasn't found.
     */
    public static boolean setField(Object target, String name, Object value) {
        try {
            Field field = target.getClass().getDeclaredField(name);
            field.setAccessible(true);
            field.set(target, value);
            return true;
        } catch (NoSuchFieldException e) {
            return false;
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Get all methods on the given class that are annotated with the given annotation type.
     * @param targetClass
     * @param annotationType
     * @return
     */
    public static List<Method> getAnnotatedMethods(Class targetClass, Class<? extends Annotation> annotationType) {
        List<Method> annotatedMethods = new ArrayList<Method>();

        for(Method method : targetClass.getDeclaredMethods()) {
            if(method.isAnnotationPresent(annotationType)) {
                annotatedMethods.add(method);
            }
        }

        return annotatedMethods;
    }

    /**
     * Invoke the given method on the given target using the given arguments.
     * Allows to call private and protected methods.
     * @throws RuntimeException if method could not be invoked.
     * @param target
     * @param method
     * @param arguments
     */
    public static void invokeMethod(Object target, Method method, Object... arguments) {
        method.setAccessible(true); // so we can call private and protected methods too

        try {
            method.invoke(target, arguments);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Create an empty object of the given class.
     * @param clazz
     * @param constructorArgs arguments to pass to the matching constructor
     * @param <T>
     * @return
     */
    public static <T> T createObject(Class<T> clazz, Object... constructorArgs) {
        if(constructorArgs == null) return createObject(clazz);

        T object;

        Class[] parameterTypes = new Class[constructorArgs.length];

        for (int i = 0; i < constructorArgs.length; i++) {
            parameterTypes[i] = constructorArgs[i].getClass();
        }

        try {
            Constructor ctor = clazz.getConstructor(parameterTypes);
            object = (T)ctor.newInstance(constructorArgs);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return object;
    }

    /**
     * Create an empty object of the given class.
     * @throws RuntimeException if object could not be created.
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T createObject(Class<T> clazz) {
        T object;

        try {
            object = clazz.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        return object;
    }

    public static <T> Set<Class<? extends T>> getSubclassesOf(Class<T> targetClazz, List<String> packages) {
        Set<Class<? extends T>> classes = new HashSet<Class<? extends T>>();

        Set<Class<?>> classesInPackages;
        try {
            classesInPackages = getClasses(packages);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        for(Class<?> clazz : classesInPackages) {
            if(targetClazz.isAssignableFrom(clazz)) {
                classes.add((Class<?extends T>)clazz);
            }
        }

        return classes;
    }

    public static Set<Class<?>> getClasses(List<String> packages) throws ClassNotFoundException, IOException {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        for(String pkg : packages) {
            classes.addAll(getClasses(pkg));
        }

        return classes;
    }

    /**
     * @source http://code.google.com/p/morphia/source/browse/trunk/morphia/src/main/java/com/google/code/morphia/utils/ReflectionUtils.java?r=1751
     * @param packageName
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static Set<Class<?>> getClasses(final String packageName) throws IOException, ClassNotFoundException
    {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        return getClasses(loader, packageName);
    }

    /**
     * @source http://code.google.com/p/morphia/source/browse/trunk/morphia/src/main/java/com/google/code/morphia/utils/ReflectionUtils.java?r=1751
     * @param loader
     * @param packageName
     * @return
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public static Set<Class<?>> getClasses(final ClassLoader loader, final String packageName) throws IOException,
            ClassNotFoundException
    {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = loader.getResources(path);
        if (resources != null)
        {
            while (resources.hasMoreElements())
            {
                String filePath = resources.nextElement().getFile();
                // WINDOWS HACK
                if (filePath.indexOf("%20") > 0)
                    filePath = filePath.replaceAll("%20", " ");
                // # in the jar name
                if (filePath.indexOf("%23") > 0)
                    filePath = filePath.replaceAll("%23", "#");

                if (filePath != null)
                {
                    if ((filePath.indexOf("!") > 0) & (filePath.indexOf(".jar") > 0))
                    {
                        String jarPath = filePath.substring(0, filePath.indexOf("!")).substring(
                                filePath.indexOf(":") + 1);
                        // WINDOWS HACK
                        if (jarPath.indexOf(":") >= 0)
                        {
                            jarPath = jarPath.substring(1);
                        }
                        classes.addAll(getFromJARFile(jarPath, path));
                    }
                    else
                    {
                        classes.addAll(getFromDirectory(new File(filePath), packageName));
                    }
                }
            }
        }
        return classes;
    }

    /**
     * @source http://code.google.com/p/morphia/source/browse/trunk/morphia/src/main/java/com/google/code/morphia/utils/ReflectionUtils.java?r=1751
     * @param filename
     * @return
     */
    private static String stripFilenameExtension(final String filename)
    {
        if (filename.indexOf('.') != -1)
        {
            return filename.substring(0, filename.lastIndexOf('.'));
        }
        else
        {
            return filename;
        }
    }

    /**
     * @source http://code.google.com/p/morphia/source/browse/trunk/morphia/src/main/java/com/google/code/morphia/utils/ReflectionUtils.java?r=1751
     * @param directory
     * @param packageName
     * @return
     * @throws ClassNotFoundException
     */
    public static Set<Class<?>> getFromDirectory(final File directory, final String packageName)
            throws ClassNotFoundException
    {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        if (directory.exists())
        {
            for (String file : directory.list())
            {
                if (file.endsWith(".class"))
                {
                    String name = packageName + '.' + stripFilenameExtension(file);
                    Class<?> clazz = Class.forName(name);
                    classes.add(clazz);
                }
            }
        }
        return classes;
    }

    /**
     * @source http://code.google.com/p/morphia/source/browse/trunk/morphia/src/main/java/com/google/code/morphia/utils/ReflectionUtils.java?r=1751
     * @param jar
     * @param packageName
     * @return
     * @throws IOException
     * @throws FileNotFoundException
     * @throws ClassNotFoundException
     */
    public static Set<Class<?>> getFromJARFile(final String jar, final String packageName) throws IOException,
            ClassNotFoundException
    {
        Set<Class<?>> classes = new HashSet<Class<?>>();
        JarInputStream jarFile = new JarInputStream(new FileInputStream(jar));
        JarEntry jarEntry;
        do
        {
            jarEntry = jarFile.getNextJarEntry();
            if (jarEntry != null)
            {
                String className = jarEntry.getName();
                if (className.endsWith(".class"))
                {
                    className = stripFilenameExtension(className);
                    if (className.startsWith(packageName))
                    {
                        classes.add(Class.forName(className.replace('/', '.')));
                    }
                }
            }
        }
        while (jarEntry != null);
        return classes;
    }
}


/*
 * 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 com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.CacheFactory;
import com.googlecode.icegem.serialization.HierarchyRegistry;

/**
 * Server template for using in tests.
 *
 * Use
 * {@link JavaProcessLauncher#runWithConfirmation(String, Class, String[], String[])} to
 * launch this cache server from tests. All peer/server configurations should be
 * passed via properties file.
 *
 * @see JavaProcessLauncher
 *
 * @author Andrey Stepanov aka standy
 */
public class ServerTemplate {
	/** Cache. */
	private static Cache cache;

	/**
	 * Server entry point.
	 *
	 * @param args
	 *            of type String[]
	 */
	public static void main(String[] args) throws Exception {
		if (args.length > 0) {
			loadClasses(args[0]);
		}

		startCacheServer();

		ConsoleUtils
				.waitForEnter(JavaProcessLauncher.PROCESS_STARTUP_COMPLETED);

		stopCacheServer();
	}

	private static void loadClasses(String classes) throws Exception {
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

		String[] names = classes.split("\\,");
		Class<?>[] classArray = new Class<?>[names.length];

		for(int i = 0; i<names.length; i++) {
			classArray[i] = classLoader.loadClass(names[i]);
		}

		HierarchyRegistry.registerAll(classLoader, classArray);
	}

	/**
	 * Starts cache server.
	 */
	public static void startCacheServer() {
		cache = new CacheFactory().create();
	}

	/**
	 * Stops cache server.
	 */
	public static void stopCacheServer() {
		cache.close();
	}
}