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

import se.jguru.nazgul.tools.visualization.model.diagram.AbstractIdentifiable;
import se.jguru.nazgul.tools.visualization.model.diagram.Graph;
import se.jguru.nazgul.tools.visualization.model.diagram.NodeID;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;

/**
 *
Right-side Edge part statement implementation, corresponding to the following DOT grammar:

 *

 *     edgeRHS : edgeop (node_id | subgraph) [ edgeRHS ]
 *

 *
Typically renders into something like the following:

 *

 *     e -> b[label="0.7",weight="0.7"];
 *

 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
@XmlType(namespace = AbstractIdentifiable.NAMESPACE, propOrder = {"rightSideEdge"})
@XmlAccessorType(XmlAccessType.FIELD)
public class RightSideEdge extends AbstractEdge {

    /**
     * An optional RightSideEdge hooked up to this one.
     */
    @XmlElement
    private RightSideEdge rightSideEdge;

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

    /**
     * Creates a {@link RightSideEdge} wrapping the supplied data.
     *
     * @param nodeID A non-null {@link NodeID} instance.
     */
    public RightSideEdge(final NodeID nodeID) {
        super(nodeID);
    }

    /**
     * Creates a {@link RightSideEdge} wrapping the supplied data.
     *
     * @param subgraph A non-null {@link Subgraph} instance.
     */
    public RightSideEdge(final Subgraph subgraph) {
        super(subgraph);
    }

    /**
     *
Assigns the supplied {@link RightSideEdge} to this one, implying that the supplied {@link RightSideEdge}
     * should be rendered after this one, on the form:

     *

     *     edgeRHS : edgeop (node_id | subgraph) [ edgeRHS ]
     *

     *
     * @param rightSideEdge a non-null {@link RightSideEdge} instance.
     */
    public void setRightSideEdge(final RightSideEdge rightSideEdge) {

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

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

    /**
     * Retrieves the optional RightSideEdge to the right of this {@link RightSideEdge}.
     *
     * @return the optional (i.e. nullable) RightSideEdge to the right of this {@link RightSideEdge}.
     */
    public RightSideEdge getRightSideEdge() {
        return rightSideEdge;
    }

    /**
     * Retrieves the RightSideEdge separator as indicated within the DOT language specification.
     *
     * @param isDirectedGraph if {@code true}, the edge symbol of a directed graph (i.e. "->") and otherwise the
     *                        edge symbol of an undirected graph (i.e. "--") is returned.
     * @return The edge separator given the status of a Graph.
     */
    public static String getEdgeSeparator(final boolean isDirectedGraph) {
        return isDirectedGraph ? "->" : "--";
    }

    /**
     * Factory method creating a {@link RightSideEdge} which terminates in the Node or Subgraph with the supplied ID
     * and within the supplied Graph.
     *
     * @param id          A non-null identifier of a NodeID or Subgraph to which this {@link RightSideEdge}
     *                    should go.
     * @param withinGraph The non-null Graph which is the ultimate parent of the {@link RightSideEdge} to create.
     * @return A newly constructed {@link RightSideEdge} which is not yet added to any {@link Edge} or
     * {@link RightSideEdge} - or null if no Node or Subgraph with the supplied id was
     * found in the withinGraph.
     */
    public static RightSideEdge to(final String id, final Graph withinGraph) {

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

        // First, assume that the ID is a NodeID.
        // final Statements statements = immediateParent.getStatements();
        final Node node = withinGraph.getStatements().findNode(id, true);
        if (node != null) {
            return new RightSideEdge(node.getNodeID());
        }

        // Next, assume that the ID is a Subgraph.
        final Subgraph subgraph = withinGraph.getStatements().findSubgraph(id, true);
        if (subgraph != null) {
            return new RightSideEdge(subgraph);
        }

        // All Done.
        return null;
    }
}


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

import com.sun.javadoc.DocErrorReporter;
import com.sun.javadoc.LanguageVersion;
import com.sun.javadoc.RootDoc;

/**
 *
What is a delegating Doclet?

 *
Doclet implementation which delegates its actual implementation to a {@link SimpleDoclet} implementation.
 * The Doclet specification within the JDK requires a Doclet implementation to have only static methods, implying
 * that standard inheritance is not normally available for Doclet development. The simpler option is to delegate
 * doclet execution to another class - and this is what the DelegatingDoclet does.

 *
 *
Finding the SimpleDoclet class

 *
When loading the DelegatingDoclet class, it looks for a {@link SimpleDoclet} implementation to which it will
 * delegate all doclet-related work. The delegation is done in 3 steps:

 *

 *
Environment property. If {@code System.getenv(ENV_SIMPLEDOCLET_CLASS)} returns a
 *     non-null value, the DelegatingDoclet assumes the result is the fully qualified classname of a SimpleDoclet
 *     implementation which is loaded and used as delegate. The class must contain a no-argument constructor.

 *
System property. If {@code System.getProperty(PROPERTY_SIMPLEDOCLET_CLASS)} returns a
 *     non-null value, the DelegatingDoclet assumes the result is the fully qualified classname of a SimpleDoclet
 *     implementation which is loaded and used as delegate. The class must contain a no-argument constructor.

 *
Default. If Doclet classname is found, the DelegatingDoclet uses
 *     {@link VisualizationDoclet} as the SimpleDoclet to which doclet-related work should be delegated.

 *

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

    // Internal state
    private static SimpleDoclet delegate;

    /**
     * Environment property used to read a fully qualified class name of a SimpleDoclet implementation which should
     * sport a no-argument constructor.
     */
    public static final String ENV_SIMPLEDOCLET_CLASS = "SIMPLEDOCLET_CLASS";

    /**
     * Java System property used to read a fully qualified class name of a SimpleDoclet implementation which should
     * sport a no-argument constructor. This is used only if the environment property {@link #ENV_SIMPLEDOCLET_CLASS}
     * did not hold any value.
     */
    public static final String PROPERTY_SIMPLEDOCLET_CLASS = "simpledoclet.class";

    static {

        String simpleDocletClassName = System.getenv(ENV_SIMPLEDOCLET_CLASS);
        if (simpleDocletClassName == null) {
            simpleDocletClassName = System.getProperty(
                    PROPERTY_SIMPLEDOCLET_CLASS, VisualizationDoclet.class.getName());
        }

        // Assign the SimpleDoclet delegate.
        setDelegate(createSimpleDoclet(simpleDocletClassName));
    }

    /**
     * Creates a SimpleDoclet instance by calling a no-argument constructor within the Class of the supplied ClassName.
     * Falls back to {@link VisualizationDoclet}.
     *
     * @param className A fully qualified classname of a SimpleDoclet implementation.
     * @return An instance of the supplied ClassName - or an {@link VisualizationDoclet} as a fallback.
     */
    public static SimpleDoclet createSimpleDoclet(final String className) {

        final String effectiveClassName = className != null && !className.isEmpty()
                ? className
                : VisualizationDoclet.class.getName();

        final ClassLoader[] classLoaders = new ClassLoader[]{
                Thread.currentThread().getContextClassLoader(),
                DelegatingDoclet.class.getClassLoader()};

        SimpleDoclet toReturn = null;

        for (ClassLoader current : classLoaders) {
            try {
                final Class<?> loadedClass = current.loadClass(effectiveClassName);
                if (SimpleDoclet.class.isAssignableFrom(loadedClass)) {
                    toReturn = (SimpleDoclet) loadedClass.newInstance();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // All Done.
        return toReturn == null ? new VisualizationDoclet() : toReturn;
    }

    /**
     * Assigns the {@link SimpleDoclet} delegate used within this {@link DelegatingDoclet}.
     *
     * @param delegate A non-null {@link SimpleDoclet} implementation
     */
    public static void setDelegate(final SimpleDoclet delegate) {

        if (delegate == null) {
            throw new NullPointerException("Cannot handle null 'delegate' argument.");
        }

        // Assign internal state
        DelegatingDoclet.delegate = delegate;
    }

    /**
     * Doclet start of execution.
     *
     * @param root A non-null {@link RootDoc} that represents the root of the program structure information
     *             for one run of javadoc. Enables extracting all other program structure information data.
     * @return true if the start completed OK.
     */
    public static boolean start(final RootDoc root) {
        return delegate.start(root);
    }

    /**
     *
Checks that the supplied options are valid for this Doclet, and uses the supplied {@link DocErrorReporter}
     * instance to report any errors. According to the doclet specification:

     *
If the validOptions method is present, it is automatically invoked; you don't have to
     * explicitly call it.
     * It should return {@code true} if the option usage is valid, and false otherwise. You can also print
     * appropriate error messages from validOptions when improper usages of command-line options are found.

     *
     * @param options       The options to validate.
     * @param errorReporter The error reporter used to output error messages, in case the options were not valid.
     * @return {@code true} if the options were valid.
     */
    public static boolean validOptions(final String[][] options, final DocErrorReporter errorReporter) {
        return delegate.validateJavaDocOptions(options, errorReporter);
    }

    /**
     *
Any doclet that uses custom options must have a method called optionLength(String option) that returns an
     * int. For each custom option that you want your doclet to recognize, optionLength must return the number of
     * separate pieces or tokens in the option.

     *
For example, the custom option {@code -foo bar} has 2 pieces, the -tag option itself and its value.
     * Hence, this optionLength method must return 2 for the {@code -foo} option.

     *
     * @param option A javadoc option.
     * @return The length of the supplied option, including the option itself, or {@code 0} for unrecognized options.
     */
    public static int optionLength(final String option) {
        return delegate.optionLength(option);
    }

    /**
     * The JavaDoc language version for this Doclet.
     *
     * @return the JavaDoc language version for this Doclet.
     * @see LanguageVersion#JAVA_1_5
     */
    public static LanguageVersion languageVersion() {
        return delegate.languageVersion();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return "DelegatingDoclet using [" + (delegate == null ? "<no>" : delegate.getClass().getName())
                + "] SimpleDoclet delegate.";
    }
}


/*-
 * #%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.XmlElementWrapper;
import javax.xml.bind.annotation.XmlType;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Implementation of a Comment containing a List of string lines.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
@XmlType(namespace = AbstractIdentifiable.NAMESPACE, propOrder = {"commentLines"})
@XmlAccessorType(XmlAccessType.FIELD)
public class Comment implements Serializable {

    /**
     * OS-neutral newline String/char.
     */
    public static final String NEWLINE = System.getProperty("line.separator");

    /**
     * The list of strings/lines within this Comment. May be empty but not null.
     */
    @XmlElement(name = "commentLine")
    private List<String> commentLines;

    /**
     * JAXB-friendly constructor, reserved for framework use.
     */
    public Comment() {
        commentLines = new ArrayList<>();
    }

    /**
     * Creates a new Comment wrapping the supplied lines
     *
     * @param lines One or more comment lines.
     */
    public Comment(final String... lines) {

        // Delegate
        this();

        // Add all supplied lines.
        add(lines);
    }

    /**
     * If non-null, adds one or more lines to this comment.
     * Also, {@link #NEWLINE} strings are removed from each line.
     *
     * @param lines one or more lines to add to this comment.
     */
    public final void add(final String... lines) {

        if (lines != null) {
            for (String current : lines) {
                commentLines.add(current.replace(NEWLINE, ""));
            }
        }
    }

    /**
     * Retrieves an unmodifiable List containing the wrapped comment lines.
     *
     * @return an unmodifiable List containing the wrapped comment lines.
     */
    public List<String> getCommentLines() {
        return Collections.unmodifiableList(commentLines);
    }
}


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

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import se.jguru.nazgul.tools.visualization.spi.dot.grammars.DotLexer;
import se.jguru.nazgul.tools.visualization.spi.dot.grammars.DotParser;

import java.io.IOException;
import java.io.InputStream;

/**
 * Validation implementation for Graphviz/Dot files, complying to the
 * DOT language specification.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public final class DotDiagramValidator {

    /*
     * Hide constructor for utility classes.
     */
    private DotDiagramValidator() {
        // Do nothing
    }

    /**
     * Validates that the supplied InputStream contains well-formed Graph data, complying to the
     * DOT language specification. If the
     *
     * @param dotDiagramData A non-null InputStream containing a Graph or DiGraph definition.
     * @return A {@link se.jguru.nazgul.tools.visualization.spi.dot.grammars.DotParser.GraphContext} containing the
     * parsed Graph from the parsed DOT data.
     * @throws DotDiagramSyntaxException if the supplied InputStream contained syntax errors.
     */
    public static DotParser.GraphContext validate(final InputStream dotDiagramData) throws DotDiagramSyntaxException {

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

        // Create the parser
        final DotLexer lexer = createLexer(dotDiagramData);
        final DotParser parser = createParser(lexer);

        // Ensure we can harvest all syntax errors.
        final DotSyntaxErrorListener errorListener = new DotSyntaxErrorListener();
        parser.addErrorListener(errorListener);

        final DotParser.GraphContext toReturn = parser.graph();

        // Check sanity
        if (errorListener.isErrorState()) {
            throw new DotDiagramSyntaxException(errorListener.getErrorDataList());
        }

        // All Done.
        return toReturn;
    }

    /**
     * Factory method creating a DotLexer from the supplied InputStream, assumed to contain Graphviz/DOT diagram data.
     *
     * @param dotDiagramData The InputStream containing Graphviz/DOT data, complying
     *                       with DOT language specification.
     * @return A {@link DotLexer} using the supplied InputStream as source of tokens.
     */
    public static DotLexer createLexer(final InputStream dotDiagramData) {

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

        try {

            // All Done.
            return new DotLexer(new ANTLRInputStream(dotDiagramData));
        } catch (IOException e) {
            throw new IllegalArgumentException("Could not create DotLexer", e);
        }
    }

    /**
     * Creates a {@link DotParser} reading data from the supplied {@link DotLexer}.
     *
     * @param lexer A non-null {@link DotLexer} instance holding all Graphviz/DOT diagram data.
     * @return a {@link DotParser} hooked up with a {@link org.antlr.v4.runtime.TokenStream} to the
     * supplied {@link DotLexer}.
     */
    public static DotParser createParser(final DotLexer lexer) {

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

        // All Done.
        return new DotParser(new CommonTokenStream(lexer));
    }
}


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

/*
 * 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.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.MavenReportException;
import se.jguru.nazgul.tools.plugin.checkstyle.exec.CheckstyleExecutorRequest;

import java.util.List;

/**
 * A reporting task that performs Checkstyle analysis and generates an aggregate
 * HTML report on the violations that Checkstyle finds in a multi-module reactor
 * build.
 *
 * @version $Id$
 */
@Mojo(name = "checkstyle-aggregate", aggregator = true, requiresDependencyResolution = ResolutionScope.COMPILE,
        threadSafe = true)
public class CheckstyleAggregateReport
        extends AbstractCheckstyleReport {
    /**
     * The projects in the reactor for aggregation report.
     *
     * @since 2.8
     */
    @Parameter(property = "reactorProjects", readonly = true)
    private List<MavenProject> reactorProjects;

    /**
     * {@inheritDoc}
     */
    protected MavenProject getProject() {
        return project;
    }

    /**
     * {@inheritDoc}
     */
    protected CheckstyleExecutorRequest createRequest()
            throws MavenReportException {
        CheckstyleExecutorRequest request = new CheckstyleExecutorRequest();
        request.setAggregate(true)
                .setReactorProjects(reactorProjects)
                .setConsoleListener(getConsoleListener()).setConsoleOutput(consoleOutput)
                .setExcludes(excludes).setFailsOnError(failsOnError).setIncludes(includes)
                .setIncludeResources(includeResources)
                .setIncludeTestResources(includeTestResources)
                .setResourceIncludes(resourceIncludes)
                .setResourceExcludes(resourceExcludes)
                .setIncludeTestSourceDirectory(includeTestSourceDirectory).setListener(getListener())
                .setProject(project).setSourceDirectories(getSourceDirectories())
                .setResources(resources).setTestResources(testResources)
                .setStringOutputStream(stringOutputStream).setSuppressionsLocation(suppressionsLocation)
                .setTestSourceDirectories(getTestSourceDirectories()).setConfigLocation(configLocation)
                .setPropertyExpansion(propertyExpansion).setHeaderLocation(headerLocation)
                .setCacheFile(cacheFile).setSuppressionsFileExpression(suppressionsFileExpression)
                .setEncoding(encoding).setPropertiesLocation(propertiesLocation);
        return request;
    }

    /**
     * {@inheritDoc}
     */
    public String getOutputName() {
        return "checkstyle-aggregate";
    }

    /**
     * {@inheritDoc}
     */
    public boolean canGenerateReport() {
        // TODO: would be good to scan the files here
        return !skip && project.isExecutionRoot() && reactorProjects.size() > 1;
    }
}


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

import se.jguru.nazgul.tools.visualization.api.AbstractStringRenderer;
import se.jguru.nazgul.tools.visualization.api.RenderConfiguration;
import se.jguru.nazgul.tools.visualization.model.diagram.NodeID;
import se.jguru.nazgul.tools.visualization.model.diagram.Port;

/**
 * NodeID Renderer, complying to the specification in the
 * DOT language specification.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public class NodeIdRenderer extends AbstractStringRenderer<NodeID> {

    // Internal state
    private PortRenderer portRenderer;

    /**
     * Default constructor.
     */
    public NodeIdRenderer() {

        // Delegate
        super(NodeID.class);

        // Assign internal state
        portRenderer = new PortRenderer();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected String doRender(final RenderConfiguration config, final NodeID entity) {

        final Port port = entity.getPort();
        final String renderedPort = port == null ? "" : portRenderer.doRender(config, entity.getPort());

        return quote(entity.getId()) + renderedPort;
    }
}


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

/*
 * 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.model.Resource;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.reporting.MavenReportException;
import org.codehaus.plexus.util.StringUtils;
import se.jguru.nazgul.tools.plugin.checkstyle.exec.CheckstyleExecutorRequest;

import java.io.File;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

/**
 * A reporting task that performs Checkstyle analysis and generates an HTML
 * report on any violations that Checkstyle finds.
 *
 * @author Emmanuel Venisse
 * @author Vincent Siveton
 * @author Joakim Erdfelt
 * @version $Id$
 */
@Mojo(name = "checkstyle", requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true)
public class CheckstyleReport extends AbstractCheckstyleReport {
    /**
     * @deprecated Remove with format parameter.
     */
    private static final Map<String, String> FORMAT_TO_CONFIG_LOCATION;

    static {
        Map<String, String> fmt2Cfg = new HashMap<>();

        fmt2Cfg.put("sun", "sun_checks.xml");

        FORMAT_TO_CONFIG_LOCATION = Collections.unmodifiableMap(fmt2Cfg);
    }

    /**
     * Specifies what predefined check set to use. Available sets are "sun" (for
     * the Sun coding conventions), and "maven".
     *
     * @deprecated Use configLocation instead.
     */
    @Parameter(defaultValue = "sun")
    private String format;

    /**
     * Specifies the location of the Checkstyle properties file that will be used to
     * check the source.
     *
     * @deprecated Use propertiesLocation instead.
     */
    @Parameter
    private File propertiesFile;

    /**
     * Specifies the URL of the Checkstyle properties that will be used to check
     * the source.
     *
     * @deprecated Use propertiesLocation instead.
     */
    @Parameter
    private URL propertiesURL;

    /**
     * Specifies the location of the License file (a.k.a. the header file) that
     * is used by Checkstyle to verify that source code has the correct
     * license header.
     *
     * @deprecated Use headerLocation instead.
     */
    @Parameter(defaultValue = "${basedir}/LICENSE.txt")
    private File headerFile;

    /**
     * Specifies the location of the suppressions XML file to use. The plugin
     * defines a Checkstyle property named
     * checkstyle.suppressions.file with the value of this
     * property. This allows using the Checkstyle property in your own custom
     * Checkstyle configuration file when specifying a suppressions file.
     *
     * @deprecated Use suppressionsLocation instead.
     */
    @Parameter
    private String suppressionsFile;

    /**
     *

     * Specifies the location of the package names XML to be used to configure
     * the Checkstyle Packages.
     *

     *

     *

     * This parameter is resolved as resource, URL, then file. If resolved to a
     * resource, or a URL, the contents of the package names XML is copied into
     * the ${project.build.directory}/checkstyle-packagenames.xml
     * file before being passed to Checkstyle for loading.
     *

     *
     * @since 2.0-beta-2
     */
    @Parameter
    private String packageNamesLocation;

    /**
     * Specifies the location of the package names XML to be used to configure
     * Checkstyle.
     *
     * @deprecated Use packageNamesLocation instead.
     */
    @Parameter
    private String packageNamesFile;

    /**
     * {@inheritDoc}
     */
    protected MavenProject getProject() {
        return project;
    }

    /**
     * {@inheritDoc}
     */
    public void executeReport(Locale locale)
            throws MavenReportException {
        mergeDeprecatedInfo();
        super.executeReport(locale);
    }

    /**
     * {@inheritDoc}
     */
    protected CheckstyleExecutorRequest createRequest()
            throws MavenReportException {
        CheckstyleExecutorRequest request = new CheckstyleExecutorRequest();
        request.setConsoleListener(getConsoleListener()).setConsoleOutput(consoleOutput)
                .setExcludes(excludes).setFailsOnError(failsOnError).setIncludes(includes)
                .setResourceIncludes(resourceIncludes)
                .setResourceExcludes(resourceExcludes)
                .setIncludeResources(includeResources)
                .setIncludeTestResources(includeTestResources)
                .setIncludeTestSourceDirectory(includeTestSourceDirectory).setListener(getListener())
                .setProject(project).setSourceDirectories(getSourceDirectories())
                .setResources(resources)
                .setStringOutputStream(stringOutputStream).setSuppressionsLocation(suppressionsLocation)
                .setTestSourceDirectories(getTestSourceDirectories()).setConfigLocation(configLocation)
                .setPropertyExpansion(propertyExpansion).setHeaderLocation(headerLocation)
                .setCacheFile(cacheFile).setSuppressionsFileExpression(suppressionsFileExpression)
                .setEncoding(encoding).setPropertiesLocation(propertiesLocation);
        return request;
    }

    /**
     * {@inheritDoc}
     */
    public String getOutputName() {
        return "checkstyle";
    }

    /**
     * {@inheritDoc}
     */
    public boolean canGenerateReport() {
        if (skip) {
            return false;
        }

        // TODO: would be good to scan the files here
        for (File sourceDirectory : getSourceDirectories()) {
            if (sourceDirectory.exists()) {
                return true;
            }
        }

        if (includeTestSourceDirectory) {
            for (File testSourceDirectory : getTestSourceDirectories()) {
                if (testSourceDirectory.exists()) {
                    return true;
                }
            }
        }

        return ((includeResources && hasResources(resources))
                || (includeTestResources && hasResources(testResources))
        );
    }

    /**
     * Check if any of the resources exist.
     *
     * @param resources The resources to check
     * @return true if the resource directory exist
     */
    private boolean hasResources(List<Resource> resources) {
        for (Resource resource : resources) {
            if (new File(resource.getDirectory()).exists()) {
                return true;
            }
        }
        return false;
    }

    /**
     * Merge in the deprecated parameters to the new ones, unless the new
     * parameters have values.
     *
     * @throws MavenReportException
     * @deprecated Remove when deprecated params are removed.
     */
    private void mergeDeprecatedInfo()
            throws MavenReportException {
        if ("sun_checks.xml".equals(configLocation) && !"sun".equals(format)) {
            configLocation = FORMAT_TO_CONFIG_LOCATION.get(format);

            throw new MavenReportException("'format' parameter is deprecated: please replace with <configLocation>"
                    + configLocation + "</configLocation>.");
        }

        if (StringUtils.isEmpty(propertiesLocation)) {
            if (propertiesFile != null) {
                propertiesLocation = propertiesFile.getPath();

                throw new MavenReportException("'propertiesFile' parameter is deprecated: please replace with "
                        + "<propertiesLocation>" + propertiesLocation + "</propertiesLocation>.");
            } else if (propertiesURL != null) {
                propertiesLocation = propertiesURL.toExternalForm();

                throw new MavenReportException("'propertiesURL' parameter is deprecated: please replace with "
                        + "<propertiesLocation>" + propertiesLocation + "</propertiesLocation>.");
            }
        }

        if ("LICENSE.txt".equals(headerLocation)) {
            File defaultHeaderFile = new File(project.getBasedir(), "LICENSE.txt");
            if (!defaultHeaderFile.equals(headerFile)) {
                headerLocation = headerFile.getPath();
            }
        }

        if (StringUtils.isEmpty(suppressionsLocation)) {
            suppressionsLocation = suppressionsFile;

            if (StringUtils.isNotEmpty(suppressionsFile)) {
                throw new MavenReportException("'suppressionsFile' parameter is deprecated: please replace with "
                        + "<suppressionsLocation>" + suppressionsLocation + "</suppressionsLocation>.");
            }
        }

        if (StringUtils.isEmpty(packageNamesLocation)) {
            packageNamesLocation = packageNamesFile;

            if (StringUtils.isNotEmpty(packageNamesFile)) {
                throw new MavenReportException("'packageNamesFile' parameter is deprecated: please replace with "
                        + "<packageNamesFile>" + suppressionsLocation + "</packageNamesFile>.");
            }
        }
    }

}


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

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

import com.gemstone.gemfire.cache.CacheFactory;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.execute.Function;
import com.gemstone.gemfire.cache.execute.FunctionService;
import com.gemstone.gemfire.cache.execute.ResultCollector;
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
import com.googlecode.icegem.utils.function.ClearRegionFunction;
import com.googlecode.icegem.utils.function.RemoveAllFunction;

/**
 * Help class for common operations with regions.
 *
 * @author Andrey Stepanov aka standy
 */
public class CacheUtils {
	/** */
	private static Logger log = LoggerFactory.getLogger(CacheUtils.class);

	/** */
	private static long BACKOFF_BASE = 10L;

	/**
	 * Limits query results.
	 *
	 * @param queryString
	 *            Query string.
	 * @return Query string with injected "LIMIT" clause.
	 */
	public static String addQueryLimit(String queryString, int queryLimit) {
		int limitIndex = queryString.lastIndexOf("limit");

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

		if (limitIndex == -1) {
			return queryString + " LIMIT " + (queryLimit + 1);
		}

		int limitNumber = Integer.parseInt(queryString
				.substring(limitIndex + 5).trim());

		return (limitNumber > queryLimit) ? queryString
				.substring(0, limitIndex) + " LIMIT " + (queryLimit + 1)
				: queryString;
	}

	/**
	 * @see #clearRegion(Region, boolean, boolean).
	 *
	 * @param region
	 *            the region.
	 */
	public static void clearRegion(Region<?, ?> region) {
		clearRegion(region, false, true);
	}

	/**
	 * Clears all types of regions. This method can clean both types of regions
	 * (REPLICATED, PARTITIONED). It can be used both on client and server side.
	 *
	 * @param region
	 *            the region.
	 * @param wanCompatible
	 *            if true, WAN replication compatible method will be used which
	 *            is considerably slower on REPLICATE regions.
	 * @param cleanLocal
	 *            if true, local cached replicate shall be cleared.
	 */
	public static void clearRegion(Region<?, ?> region, boolean wanCompatible,
			boolean cleanLocal) {
		ClearRegionFunction cleaner = new ClearRegionFunction();

		FunctionService.registerFunction(cleaner);

		FunctionService.onRegion(region).withArgs(wanCompatible)
				.execute(cleaner).getResult();

		if (cleanLocal && isGemFireClient()) {
			region.localClear();
		}
	}

	/**
	 * Returns first locator host and port from locators string.
	 *
	 * @param locatorsString
	 *            of type String
	 * @return String[0] - locator host String[1] - locator port
	 */
	public static String[] getFirstLocatorFromLocatorsString(
			String locatorsString) {
		if (locatorsString == null || locatorsString.length() == 0) {
			return new String[2];
		}

		String[] firstLocator = new String[2];

		firstLocator[0] = locatorsString.substring(0,
				locatorsString.indexOf('[')).trim();

		locatorsString = locatorsString
				.substring(locatorsString.indexOf('[') + 1);

		firstLocator[1] = locatorsString.substring(0,
				locatorsString.indexOf(']'));

		return firstLocator;
	}

	/**
	 * Returns approximate number of entries in the region. As the function
	 * counts number of entries on different nodes in parallel, per node values
	 * may be captured on different moments. So the value may never be valid.
	 *
	 * @param region
	 *            the region.
	 * @returns approximate number of objects in the given region.
	 */
	public static int getRegionSize(Region<?, ?> region) {
		Function function = new RegionSizeFunction();

		FunctionService.registerFunction(function);

		ResultCollector<?, ?> rc = FunctionService.onRegion(region)
				.withCollector(new RegionSizeResultCollector())
				.execute(function);

		return (Integer) rc.getResult();
	}

	/**
	 * Checks if the current GemFire is a client instance.
	 *
	 * @return true if the cache instance is client.
	 */
	public static boolean isGemFireClient() {
		GemFireCacheImpl impl = (GemFireCacheImpl) CacheFactory
				.getAnyInstance();
		return impl != null && impl.isClient();
	}

	/**
	 * Removes several entries from region in a single hop. On partitioned
	 * region execution is done simultaneously on all partitions.
	 *
	 * @param <K>
	 *            key type.
	 * @param region
	 *            the region to remove entries.
	 * @param keys
	 *            the keys of entries to remove.
	 */
	public static <K> void removeAll(Region<K, ?> region, Set<K> keys) {
		if (keys == null) {
			throw new NullPointerException();
		}
		if (keys.isEmpty()) {
			// Nothing to do
			return;
		}
		Function function = new RemoveAllFunction();

		FunctionService.registerFunction(function);

		ResultCollector<?, ?> rc = FunctionService.onRegion(region)
				.withFilter(keys).withArgs(region.getName()).execute(function);

		// Obtain result from the collector to return
		// only after everything is done.
		rc.getResult();
	}

	/**
	 * Retries passed operation with random exponential back off delay.
	 *
	 * @param <T>
	 *            Type of returned value.
	 * @param runnable
	 *            the operation.
	 * @param maxRetries
	 *            the maximum number of retries.
	 * @return the value returned by operation
	 * @throws OperationRetryFailedException
	 * @throws InterruptedException
	 */
	public static <T> T retryWithExponentialBackoff(Retryable<T> runnable,
			int maxRetries) throws InterruptedException,
			OperationRetryFailedException {
		int retry = 0;

		while (retry < maxRetries) {
			retry++;

			try {
				return runnable.execute();
			} catch (OperationRequireRetryException e) {
				// No-op.
			} catch (InterruptedException e) {
				throw e;
			}

			if (retry > 1) {
				long delay = (long) ((BACKOFF_BASE << retry) * Math.random());

				log.debug("Operation requested retry. Sleep for {} millis",
						delay);

				Thread.sleep(delay);
			}
		}

		throw new OperationRetryFailedException(
				"Maximum number of operation retries reached");
	}
}


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

import javax.swing.*;
import java.awt.*;
import java.awt.geom.Dimension2D;
import java.awt.image.BufferedImage;
import java.io.InputStream;

import org.apache.batik.bridge.UserAgentAdapter;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
import org.apache.batik.transcoder.image.ImageTranscoder;
import org.w3c.dom.Document;

/**
 * A Swing Icon that draws an SVG image.
 *
 * @author Cameron McCormack
 */
public class SVGIcon extends UserAgentAdapter implements Icon {
    /**
     * The BufferedImage generated from the SVG document.
     */
    private BufferedImage bufferedImage;

    /**
     * The width of the rendered image.
     */
    private int width;

    /**
     * The height of the rendered image.
     */
    private int height;

    private Color backgroundColour;

    /**
     * Create a new SVGIcon object.
     *
     * @param uri The URI to read the SVG document from.
     */
    public SVGIcon(String uri) throws TranscoderException {
        this(uri, 0, 0);
    }

    /**
     * Create a new SVGIcon object.
     *
     * @param uri The URI to read the SVG document from.
     * @param w   The width of the icon.
     * @param h   The height of the icon.
     */
    public SVGIcon(String uri, int w, int h) throws TranscoderException {
        generateBufferedImage(new TranscoderInput(uri), w, h);
    }

    /**
     * Create a new SVGIcon object.
     *
     * @param doc The SVG document.
     */
    public SVGIcon(Document doc) throws TranscoderException {
        this(doc, 0, 0);
    }

    public SVGIcon(final InputStream steam) throws TranscoderException {
        generateBufferedImage(new TranscoderInput(steam), 0, 0);
    }

    /**
     * Create a new SVGIcon object.
     *
     * @param doc The SVG document.
     * @param w   The width of the icon.
     * @param h   The height of the icon.
     */
    public SVGIcon(Document doc, int w, int h) throws TranscoderException {
        generateBufferedImage(new TranscoderInput(doc), w, h);
    }

    /**
     * Create a new SVGIcon object.
     *
     * @param doc The SVG document.
     * @param w   The width of the icon.
     * @param h   The height of the icon.
     */
    public SVGIcon(final InputStream steam, int w, int h) throws TranscoderException {
        generateBufferedImage(new TranscoderInput(steam), w, h);
    }

    /**
     * Generate the BufferedImage.
     */
    protected void generateBufferedImage(TranscoderInput in, int w, int h) throws TranscoderException {
        BufferedImageTranscoder t = new BufferedImageTranscoder();
        if (w != 0 && h != 0) {
            t.setDimensions(w, h);
        }
        t.transcode(in, null);
        bufferedImage = t.getBufferedImage();
        width = bufferedImage.getWidth();
        height = bufferedImage.getHeight();
    }

    /**
     * A transcoder that generates a BufferedImage.
     */
    protected class BufferedImageTranscoder extends ImageTranscoder {

        /**
         * The BufferedImage generated from the SVG document.
         */
        protected BufferedImage bufferedImage;

        /**
         * Creates a new ARGB image with the specified dimension.
         *
         * @param width  the image width in pixels
         * @param height the image height in pixels
         */
        @Override
        public BufferedImage createImage(int width, int height) {
            return new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        }

        /**
         * Writes the specified image to the specified output.
         *
         * @param img                 the image to write
         * @param output              the output where to store the image
         * @param TranscoderException if an error occured while storing the image
         */
        @Override
        public void writeImage(BufferedImage img, TranscoderOutput output) throws TranscoderException {
            bufferedImage = img;
        }

        /**
         * Returns the BufferedImage generated from the SVG document.
         */
        public BufferedImage getBufferedImage() {
            return bufferedImage;
        }

        /**
         * Set the dimensions to be used for the image.
         */
        public void setDimensions(int w, int h) {
            hints.put(KEY_WIDTH, new Float(w));
            hints.put(KEY_HEIGHT, new Float(h));
        }
    }

    // Icon //////////////////////////////////////////////////////////////////

    /**
     * Returns the icon's width.
     */
    @Override
    public int getIconWidth() {
        return width;
    }

    /**
     * Returns the icon's height.
     */
    @Override
    public int getIconHeight() {
        return height;
    }

    /**
     * Draw the icon at the specified location.
     */
    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
        if (backgroundColour == null) g.drawImage(bufferedImage, x, y, null);
        else g.drawImage(bufferedImage, x, y, backgroundColour, null);
    }

    // UserAgent /////////////////////////////////////////////////////////////

    /**
     * Returns the default size of this user agent.
     */
    @Override
    public Dimension2D getViewportSize() {
        return new Dimension(width, height);
    }

    public Color getBackgroundColour() {
        return backgroundColour;
    }

    public void setBackgroundColour(Color backgroundColour) {
        this.backgroundColour = backgroundColour;
    }
}


/*
 * #%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.artifact.Artifact;
import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
import org.apache.maven.project.MavenProject;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;

/**
 * Enforcer rule to restrict importing Impl projects with the exception of actual
 * JEE deployment projects.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public class RestrictImplDependencies extends AbstractNonCacheableEnforcerRule {

    /**
     * Pattern defining groupID for artifacts that should be evaluated by this EnforcerRule instance.
     * This default value will be used unless overridden by [explicit] configuration.
     */
    public static final String EVALUATE_GROUPID = "^se\\.jguru\\.nazgul\\..*";

    /**
     * Pattern defining patterns for groupIDs that should be ignored by this EnforcerRule instance.
     * This default value will be used unless overridden by [explicit] configuration.
     */
    public static final String IGNORE_GROUPID = "^se\\.jguru\\.nazgul\\..*\\.generated\\..*,"
            + "^se\\.jguru\\.nazgul\\.tools\\.codestyle\\..*";

    /**
     * ProjectTypes for which this rule should be ignored.
     */
    public static final List<ProjectType> IGNORED_PROJECT_TYPES = Arrays.asList(
            ProjectType.JEE_APPLICATION,
            ProjectType.PARENT,
            ProjectType.ASSEMBLY,
            ProjectType.REACTOR,
            ProjectType.PROOF_OF_CONCEPT,
            ProjectType.EXAMPLE,
            ProjectType.TEST,
            ProjectType.JAVA_AGENT,
            ProjectType.STANDALONE_APPLICATION);

    // Internal state
    private List<Pattern> evaluateGroupIds = new ArrayList<Pattern>();
    private List<Pattern> dontEvaluateGroupIds = new ArrayList<Pattern>();

    /**
     * Default constructor.
     */
    public RestrictImplDependencies() {

        // Add default values.
        setIncludedGroupIdPatterns(EVALUATE_GROUPID);
        setExcludedGroupIdPatterns(IGNORE_GROUPID);
    }

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

        // Acquire the ProjectType
        final ProjectType projectType;
        try {
            projectType = ProjectType.getProjectType(project);
        } catch (IllegalArgumentException e) {
            throw new RuleFailureException("Incorrect project definition for " + project, e);
        }

        // Don't evaluate for ignored project types.
        if (IGNORED_PROJECT_TYPES.contains(projectType)) {
            return;
        }

        // Don't evaluate if told not to.
        if (matches(project.getGroupId(), dontEvaluateGroupIds)) {

            // Log somewhat
            helper.getLog().debug("Ignored [" + project.getGroupId() + ":" + project.getArtifactId()
                    + "] since its groupId was excluded from enforcement.");
            return;

        }

        // Don't evaluate if not told to.
        if (!matches(project.getGroupId(), evaluateGroupIds)) {

            // Log somewhat
            helper.getLog().debug("Ignored [" + project.getGroupId() + ":" + project.getArtifactId()
                    + "] since its groupId was not included in enforcement.");
            return;
        }

        // Acquire all project dependencies.
        for (final Artifact current : project.getDependencyArtifacts()) {

            // Don't evaluate for test-scope dependencies.
            if (Artifact.SCOPE_TEST.equalsIgnoreCase(current.getScope())) {
                continue;
            }

            // Should this Artifact be evaluated?
            final boolean isIncludedInEvaluation = matches(current.getGroupId(), evaluateGroupIds);
            final boolean isNotExplicitlyExcludedFromEvaluation = !matches(current.getGroupId(), dontEvaluateGroupIds);
            if (isIncludedInEvaluation && isNotExplicitlyExcludedFromEvaluation) {

                final ProjectType artifactProjectType = ProjectType.getProjectType(current);
                final String prefix = "Don't use " + artifactProjectType + " dependencies ";

                if (artifactProjectType == ProjectType.IMPLEMENTATION) {
                    throw new RuleFailureException(prefix + "outside of application projects.", current);
                }

                if (artifactProjectType == ProjectType.TEST) {
                    throw new RuleFailureException(prefix + "in compile scope for non-test artifacts.", current);
                }

                if (artifactProjectType == ProjectType.JEE_APPLICATION
                        || artifactProjectType == ProjectType.PROOF_OF_CONCEPT) {
                    throw new RuleFailureException(prefix + "in bundles.", current);
                }
            }
        }
    }

    /**
     * @return A human-readable short description for this AbstractEnforcerRule.
     * (Example: "No -impl dependencies permitted in this project")
     */
    @Override
    protected final String getShortRuleDescription() {
        return "Impl projects should only be injected in applications";
    }

    /**
     *
Assigns a set of patterns defining included GroupIds. Any projects whose GroupID
     * match any of the provided includedGroupIdPatterns will be validated by this rule.

     *
A typical configuration of this property within a pom is similar to below:

     *
     * [includedGroupIdPatterns]^se\\.jguru\\.nazgul\\..*[/includedGroupIdPatterns];
     *
     *
     * @param includedGroupIdPatterns Comma-separated list of regexp patterns that groupID
     *                                should match to be included in enforcement.
     */
    public final void setIncludedGroupIdPatterns(final String includedGroupIdPatterns) {
        this.evaluateGroupIds = splice2Pattern(includedGroupIdPatterns);
    }

    /**
     * Assigns a (comma separated) list of regexp patterns defining excluded GroupIds.
     * Any projects whose GroupID match any of the provided excludedGroupIdPatterns
     * will not be validated by this rule.
     *
A typical configuration of this property within a pom is similar to below (but using
     * normal 'less-than' and 'greater-than' tag demarcation):

     *
     * <excludedGroupIdPatterns>^se\\.jguru\\.nazgul\\..*\\.generated\\..*,
     * ^se\\.jguru\\.nazgul\\.tools\\.codestyle\\..*</excludedGroupIdPatterns>
     *
     *
     * @param excludedGroupIdPatterns Comma-separated list of groupID prefixes that
     *                                should be excluded from enforcement.
     */
    public final void setExcludedGroupIdPatterns(final String excludedGroupIdPatterns) {
        this.dontEvaluateGroupIds = splice2Pattern(excludedGroupIdPatterns);
    }
}


/*
 * 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.IOException;
import java.io.InputStreamReader;

/**
 * Console utils.
 *
 * @author Andrey Stepanov aka standy
 */
public class ConsoleUtils {
    /** Field bufferedReader  */
    private static BufferedReader bufferedReader;

    /**
     * Method getReader returns the reader of this ConsoleUtils object.
     *
     * @return the reader (type BufferedReader) of this ConsoleUtils object.
     */
    private static BufferedReader getReader() {
        if (bufferedReader == null) {
            bufferedReader = new BufferedReader(new InputStreamReader(System.in));
        }

        return bufferedReader;
    }

    /**
     * Waits for enter.
     */
    public static void waitForEnter() {
        waitForEnter("Press 'Enter' to continue");
    }

    /**
     * Waits for enter with specified message.
     *
     * @param message of type String
     */
    public static void waitForEnter(String message) {
        System.out.println(message);

        try {
            getReader().readLine();
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }
}


/*
 * 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.util.Arrays;
import java.util.Properties;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;

import com.googlecode.icegem.cacheutils.Tool;
import com.googlecode.icegem.cacheutils.common.Utils;

/**
 * The main class of the replication measurement tool. It parses command-line
 * options, configures and creates the guest nodes, and collects their
 * responses.
 *
 * Responses with exit code 0 in case of all the relations established, 1
 * otherwise.
 */
public class CheckReplicationTool extends Tool {

	/* Name of timeout option */
	private static final String TIMEOUT_OPTION = "timeout";

	/* Name of cluster option */
	private static final String CLUSTER_OPTION = "cluster";

	/* Name of region option */
	private static final String REGION_OPTION = "region";

	/* Name of help option */
	private static final String HELP_OPTION = "help";

	/* Default timeout is 1 minute */
	private static final long DEFAULT_TIMEOUT = 60 * 1000;

	/* Additional timeout */
	private static final long DELTA_TIMEOUT = 10 * 1000;

	/* Default region name is "proxy" */
	private static final String DEFAULT_REGION_NAME = "proxy";

	/* Waiting timeout */
	private static long timeout = DEFAULT_TIMEOUT;

	/* Technical region name */
	private static String regionName = DEFAULT_REGION_NAME;

	/* Clusters properties */
	private Properties clustersProperties;

	/* Debug enabled flag */
	private boolean debugEnabled;

	/**
	 * Contains processing operation
	 */
	private class ProcessorTask implements Runnable {

		private int exitCode;
		private Properties clustersProperties;
		private long timeout;
		private String regionName;
		private boolean debugEnabled;
		private boolean quiet;

		public ProcessorTask(Properties clustersProperties, long timeout,
			String regionName, boolean debugEnabled, boolean quiet) {
			this.clustersProperties = clustersProperties;
			this.timeout = timeout;
			this.regionName = regionName;
			this.debugEnabled = debugEnabled;
			this.quiet = quiet;
		}

		public void run() {
			ReplicationProcessor processor = new ReplicationProcessor(
				clustersProperties, timeout, regionName, debugEnabled, quiet);

			exitCode = 1;
			try {
				exitCode = processor.process();
			} catch (Throwable t) {
				debug(
					"CheckReplicationTool.ProcessorTask#run(): Throwable caught with message = "
						+ t.getMessage(), t);
			}

		}

		public int getExitCode() {
			return exitCode;
		}
	}

	/**
	 * Runs the tool. All the tools run in this way.
	 */
	public void execute(String[] args, boolean debugEnabled, boolean quiet) {
		try {
			this.debugEnabled = debugEnabled;

			debug("CheckReplicationTool#execute(String[]): args = "
				+ Arrays.asList(args));

			parseCommandLineArguments(args);

			System.out.println("Connecting...");

			debug("CheckReplicationTool#execute(String[]): Creating CheckReplicationTool.ProcessorTask with parameters: clustersProperties = "
				+ clustersProperties
				+ ", timeout = "
				+ timeout
				+ ", regionName = " + regionName);

			ProcessorTask task = new ProcessorTask(clustersProperties, timeout,
				regionName, debugEnabled, quiet);

			debug("CheckReplicationTool#execute(String[]): Starting CheckReplicationTool.ProcessorTask");

			Utils.execute(task, timeout + DELTA_TIMEOUT);

			int exitCode = task.getExitCode();

			debug("CheckReplicationTool#execute(String[]): CheckReplicationTool.ProcessorTask finished with exitCode = "
				+ exitCode);

			if (exitCode == 0) {
				Utils.exitWithSuccess();
			}

			Utils.exitWithFailure();
		} catch (Throwable t) {
			debug(
				"CheckReplicationTool#execute(String[]): Throwable caught with message = "
					+ t.getMessage(), t);

			Utils.exitWithFailure("Unexpected throwable", t);
		}
	}

	/**
	 * Parses command-line arguments and sets the local variables
	 *
	 * @param commandLineArguments
	 *            - the list of command-line arguments
	 */
	protected void parseCommandLineArguments(String[] commandLineArguments) {
		Options options = constructGnuOptions();

		if (commandLineArguments.length < 1) {
			printHelp(options);
		}

		CommandLineParser parser = new GnuParser();
		try {
			CommandLine line = parser.parse(options, commandLineArguments);

			if (line.hasOption(HELP_OPTION)) {
				printHelp(options);
			}

			if (line.hasOption(REGION_OPTION)) {
				regionName = line.getOptionValue(REGION_OPTION);
			}

			if (line.hasOption(TIMEOUT_OPTION)) {
				String timeoutString = line.getOptionValue(TIMEOUT_OPTION);
				timeout = Long.parseLong(timeoutString);
			}

			if (line.hasOption(CLUSTER_OPTION)) {
				clustersProperties = line.getOptionProperties(CLUSTER_OPTION);

				if (clustersProperties.keySet().size() < 2) {
					Utils
						.exitWithFailure("At least two clusters should be defined");
				}
			} else {
				Utils.exitWithFailure("No clusters defined");
			}

		} catch (Throwable t) {
			Utils
				.exitWithFailure(
					"Throwable caught during the command-line arguments parsing",
					t);
		}
	}

	/**
	 * Prints help if requested, or in case of any misconfiguration
	 *
	 * @param options
	 *            - the GNU options
	 */
	protected void printHelp(final Options options) {
		HelpFormatter formatter = new HelpFormatter();
		formatter.printHelp("check-replication [options]", options);

		Utils.exitWithFailure();
	}

	/**
	 * Constructs the set of GNU options
	 *
	 * @return - the constructed options
	 */
	protected Options constructGnuOptions() {
		final Options gnuOptions = new Options();

		gnuOptions
			.addOption("t", TIMEOUT_OPTION, true,
				"Timeout, ms. Default timeout is " + DEFAULT_TIMEOUT)
			.addOption(
				"r",
				REGION_OPTION,
				true,
				"The name of region for this test. Default name is \""
					+ DEFAULT_REGION_NAME + "\"")
			.addOption("h", HELP_OPTION, false, "Print usage information");

		@SuppressWarnings("static-access")
		Option locatorsOption = OptionBuilder
			.hasArgs()
			.withDescription(
				"Cluster name and list of its locators. "
					+ "There should be at least two clusters. "
					+ "Example: -c cluster1=host1[port1],host2[port2] -c cluster2=host3[port3]")
			.withValueSeparator().withArgName("cluster=locators")
			.withLongOpt(CLUSTER_OPTION).create("c");

		gnuOptions.addOption(locatorsOption);

		return gnuOptions;
	}

	/**
	 * Prints debug information if the debug is enabled
	 *
	 * @param message
	 *            - the debug message
	 */
	private void debug(String message) {
		debug(message, null);
	}

	/**
	 * Prints debug information if the debug is enabled
	 *
	 * @param message
	 *            - the debug message
	 * @param t
	 *            - the instance of Throwable
	 */
	private void debug(String message, Throwable t) {
		if (debugEnabled) {
			System.err.println("0 [CheckReplicationTool] " + message);

			if (t != null) {
				t.printStackTrace(System.err);
			}
		}
	}
}


/*
 * 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.admin.AdminDistributedSystem;
import com.gemstone.gemfire.admin.AdminDistributedSystemFactory;
import com.gemstone.gemfire.admin.AdminException;
import com.gemstone.gemfire.cache.RegionService;
import com.gemstone.gemfire.cache.client.*;
import com.gemstone.gemfire.cache.client.internal.AutoConnectionSourceImpl;
import com.gemstone.gemfire.cache.client.internal.ExplicitConnectionSourceImpl;
import com.gemstone.gemfire.cache.client.internal.PoolImpl;
import com.gemstone.gemfire.cache.execute.FunctionService;
import com.gemstone.gemfire.cache.execute.ResultCollector;
import com.gemstone.gemfire.distributed.internal.ServerLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.*;

/**
 * User: Artem Kondratyev, e-mail: kondratevae@gmail.com
 */
public class HashCodeCollector {

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

    private static String REGION_NAME = "data"; //todo: insert from cli

    public static void main(String[] args) throws AdminException {
        AdminDistributedSystem adminDs = AdminDistributedSystemFactory.getDistributedSystem(
                AdminDistributedSystemFactory.defineDistributedSystem()
        );
        adminDs.connect();

        //connect to different clusters. test data                             todo: insert cli usage
        Pool poolForCluster1 = PoolManager.createFactory().addLocator("localhost", 10330).create("cluster1");
        Pool poolForCluster2 = PoolManager.createFactory().addLocator("localhost", 10332).create("cluster2");
        //Pool poolForServer =  PoolManager.createFactory().addServer("localhost", 40405).create("node");
        List<Pool> poolList = new ArrayList<Pool>();
        poolList.add(poolForCluster1);
        poolList.add(poolForCluster2);
        //poolList.add(poolForServer);

        //todo: insert checking that each cluster contains region and one's type is equal (Partitioned, Replicated)

        boolean partitioned = true;        //todo: insert CLI usage  + throw exception if real region has another type

        List<ServerLocation> serverFromPool = new ArrayList<ServerLocation>();
        List<Pool> emptyPools = new ArrayList<Pool>(); //contains pool with no available servers
        for (Pool pool : poolList) {
            List<ServerLocation> allServers = null;
            if (!pool.getLocators().isEmpty())
                allServers = ((AutoConnectionSourceImpl) ((PoolImpl) pool).getConnectionSource()).findAllServers();
            else if (!pool.getServers().isEmpty())
                allServers = Arrays.asList((((PoolImpl) pool).getConnectionSource()).findServer(Collections.emptySet()));

            if (allServers != null)
                serverFromPool.addAll(allServers);
            else {
                logger.info("not found servers on locator {}", pool);
                emptyPools.add(pool);
            }
        }
        poolList.removeAll(emptyPools);

        if (serverFromPool.size() == 0) {
            logger.info("no servers available");
            return;
        }

        for (ServerLocation server : serverFromPool) {
            System.out.println("host: " + server.getHostName());
            System.out.println("port: " + server.getPort());
            System.out.println("----------------------");
        }

        //source for comparison //todo: if this node doesn't contain region! it's problem
        Pool sourcePool;
        if (!partitioned) {
            int randomServerLocation = new Random().nextInt(serverFromPool.size());
            sourcePool = PoolManager.createFactory()
                    .addServer(serverFromPool.get(randomServerLocation).getHostName(),
                            serverFromPool.get(randomServerLocation).getPort()).create("target");
        } else {
            sourcePool = poolList.get(0);
            poolList.remove(0);
        }

        FunctionService.registerFunction(new RegionInfoFunction());
        ResultCollector regionInfoResult = FunctionService.onServers(sourcePool)
                .withArgs(REGION_NAME)
                .execute(new RegionInfoFunction());

        Map regionInfo = (HashMap) ((ArrayList) regionInfoResult.getResult()).get(0);
        System.out.println("region info: " + regionInfo);

        int totalNumBuckets = (Integer) regionInfo.get("totalNumBuckets");
        //logger.debug("total keys' batch counts is ", totalNumBuckets);
        System.out.println("total keys' batch counts is " + totalNumBuckets);
        KeyExtractor keyExtractor = new KeyExtractor(REGION_NAME, sourcePool, partitioned, totalNumBuckets);

        Map<String, Map<String, Set>> clusterDifference = new HashMap<String, Map<String, Set>>();          //key: memeberId list: absent keys, diff values

        //List<ResultCollector> taskResults = new ArrayList<ResultCollector>();
        List<PoolResult> taskResults = new ArrayList<PoolResult>();
        //List<Future<ResultCollector>> collectTasks = new ArrayList<Future<ResultCollector>>(poolList.size());
        List<Future<PoolResult>> collectTasks = new ArrayList<Future<PoolResult>>(poolList.size());
        ExecutorService executorService = Executors.newFixedThreadPool(poolList.size());
        while (keyExtractor.hasKeys()) {
            Set keys = keyExtractor.getNextKeysBatch();
            System.out.println("keys to check: " + keys);
            for (Pool nextPool : poolList)
                collectTasks.add(executorService.submit(new CollectorTask(keys, nextPool, REGION_NAME)));
            System.out.println("active tasks: " + collectTasks.size());
            try {
                //for (Future<ResultCollector> futureTask : collectTasks) {
                for (Future<PoolResult> futureTask : collectTasks) {
                    taskResults.add(futureTask.get());
                }
            } catch (InterruptedException ie) {
                ie.printStackTrace();
            } catch (ExecutionException ee) {
                ee.printStackTrace();
            }
            collectTasks.clear();

            System.out.println("compare contents..");
            //getting original contents
            Map originalData = new HashMap();
            /*           for (ResultCollector taskResultFromPool : taskResults) {
                List memberResult = (ArrayList) taskResultFromPool.getResult();
                if (!partitioned)
                    for (Object result : memberResult) {                        //if cluster has several nodes with replicated region... todo: adopt for partitioned
                        if (regionInfo.get("id").equals(((HashMap) result).get("memberId"))) {
                            originalData = (HashMap) ((HashMap) result).get("map");
                            break;
                        }
                    }
                else {
                    FutureTask<ResultCollector> ft = new FutureTask<ResultCollector>(new CollectorTask(keys, sourcePool, REGION_NAME));
                    try {
                        ResultCollector rc = ft.get();
                    } catch (Exception e) {
                        throw new RuntimeException("error getting key-hashcode from pool: " + sourcePool, e);
                    }
                }
            }*/
            //FutureTask<ResultCollector> ft = new FutureTask<ResultCollector>(new CollectorTask(keys, sourcePool, REGION_NAME));
            FutureTask<PoolResult> ft = new FutureTask<PoolResult>(new CollectorTask(keys, sourcePool, REGION_NAME));
            ft.run();
            try {
                //ResultCollector rc = ft.get();
                PoolResult rc = ft.get();
                List poolResult = (List) rc.getResultCollector().getResult();
                for (Object singleResult : poolResult) {
                    originalData.putAll((Map) ((HashMap) singleResult).get("map"));
                }
            } catch (Exception e) {
                throw new RuntimeException("error getting key-hash from pool: " + sourcePool, e);
            }

            /*    if (true)
            return;

        //todo: aggregate members' data from one cluster
        if (partitioned)
            aggregateSingleClusterMemberData(taskResults);*/

            System.out.println("original data is: " + originalData);
            //for (ResultCollector taskResultFromPool : taskResults) {
            for (PoolResult taskResultFromPool : taskResults) {
                List poolResult = (ArrayList) taskResultFromPool.getResultCollector().getResult();
                if (!partitioned) {
                    for (Object resultFromMember : poolResult) {
                        Map result = (HashMap) resultFromMember;
                        if (regionInfo.get("id").equals(result.get("memberId")))                   //for replicated region
                            continue;
                      /*  System.out.println("member id: " + result.get("memberId"));
                        System.out.println("absent keys is " + result.get("absentKeys"));
                        System.out.println("it's data is: " + result.get("map"));*/
                        String memberId = (String) result.get("memberId");
                        Map memberData = (HashMap) result.get("map");
                        Map memberDataCopy = new HashMap();
                        memberDataCopy.putAll(memberData);
                        memberDataCopy.keySet().retainAll(originalData.keySet());
                        Map<String, Set> aggregationInfo = new HashMap<String, Set>();      //contains aggregation info for a member
                        //keys witch values are different
                        Set keysForDiffValues = new HashSet();
                        Set absentKeys = new HashSet();
                        for (Object regionKey : memberDataCopy.keySet())
                            if (!memberDataCopy.get(regionKey).equals(originalData.get(regionKey)))      //we compare not original values, but it's hash code
                                keysForDiffValues.add(regionKey);

                        aggregationInfo.put("diffValues", keysForDiffValues);
                        //absent keys
                        memberData.keySet().removeAll(memberDataCopy.keySet());

                        absentKeys.addAll(memberData.keySet());
                        absentKeys.addAll((HashSet) result.get("absentKeys"));
                        aggregationInfo.put("absentKeys", absentKeys);

                       /* if (!clusterDifference.containsKey(memberId)) {
                            clusterDifference.put(memberId, aggregationInfo);
                        } else {
                            Map<String, Set> difference = clusterDifference.get(memberId);
                            difference.get("absentKeys").addAll(aggregationInfo.get("absentKeys"));
                            difference.get("diffValues").addAll(aggregationInfo.get("diffValues"));
                            clusterDifference.put(memberId, difference);
                        }*/
                         if (!clusterDifference.containsKey(memberId)) {
                            clusterDifference.put(memberId, aggregationInfo);
                        } else {
                            Map<String, Set> difference = clusterDifference.get(memberId);
                            difference.get("absentKeys").addAll(aggregationInfo.get("absentKeys"));
                            difference.get("diffValues").addAll(aggregationInfo.get("diffValues"));
                            clusterDifference.put(memberId, difference);
                        }
                    }
                } else {
                    Map targetMap = new HashMap();
                    Set absentPoolKeys = new HashSet();     //aggregate absent keys from pool members
                    for (Object memberResult : poolResult) {
                        targetMap.putAll((Map) ((HashMap) memberResult).get("map"));
                        absentPoolKeys.addAll((Set) (((HashMap)memberResult).get("absentKeys")));
                    }
                    Map memberDataCopy = new HashMap();
                    memberDataCopy.putAll(targetMap);
                    memberDataCopy.keySet().retainAll(originalData.keySet());
                    Map<String, Set> aggregationInfo = new HashMap<String, Set>();      //contains aggregation info for a member
                    //keys witch values are different
                    Set keysForDiffValues = new HashSet();
                    Set absentKeys = new HashSet();
                    for (Object regionKey : memberDataCopy.keySet())
                        if (!memberDataCopy.get(regionKey).equals(originalData.get(regionKey)))      //we compare not original values, but it's hash code
                            keysForDiffValues.add(regionKey);

                    aggregationInfo.put("diffValues", keysForDiffValues);
                    //absent keys
                    targetMap.keySet().removeAll(memberDataCopy.keySet());

                    absentKeys.addAll(targetMap.keySet());
                    absentKeys.addAll(absentPoolKeys);
                    //aggregationInfo.put("absentKeys", absentKeys);

                    if (!clusterDifference.containsKey(taskResultFromPool.getPool().toString())) {
                        clusterDifference.put(taskResultFromPool.getPool().toString(), aggregationInfo);
                    } else {
                        Map<String, Set> difference = clusterDifference.get(taskResultFromPool.getPool().toString());
                      //  difference.get("absentKeys").addAll(aggregationInfo.get("absentKeys"));
                        difference.get("diffValues").addAll(aggregationInfo.get("diffValues"));
                        clusterDifference.put(taskResultFromPool.getPool().toString(), difference);
                    }
                }
            }

            taskResults.clear();
        }

        System.out.println("____________________________");
        System.out.println("difference: ");
        System.out.println(clusterDifference);
        executorService.shutdown();
        adminDs.disconnect();
    }

    /**
     * only for partitioned region!
     */
    private static void aggregateSingleClusterMemberData(List<ResultCollector> taskResults) {
        for (ResultCollector singleCluster : taskResults) {
            List membersResult = (List) singleCluster.getResult();
            for (Object resultFromNode : membersResult) {
                System.out.print(((HashMap) resultFromNode).get("ds") + ":");
            }
            System.out.println();
        }
    }

    /*static class CollectorTask implements Callable<ResultCollector> {
        private HashSet keys;
        private Pool pool;
        private String regionName;

        CollectorTask(HashSet keys, Pool pool, String regionName) {
            this.keys = keys;
            this.pool = pool;
            this.regionName = regionName;
        }

        public ResultCollector call() throws Exception {
            Map args = new HashMap();
            args.put("regionName", regionName);
            args.put("keys", keys);
            return FunctionService.onServers(pool)
                    .withArgs((HashMap) args)
                    .execute(new HashCodeCollectorFunction());
        }
    }*/
}


/*
  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.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.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.Size;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.envers.Audited;
import org.jboss.pressgang.ccms.model.base.AuditedEntity;
import org.jboss.pressgang.ccms.model.constants.Constants;
import org.jboss.pressgang.ccms.model.exceptions.CustomConstraintViolationException;
import org.jboss.pressgang.ccms.model.sort.LocaleValueComparator;
import org.jboss.pressgang.ccms.utils.common.FileUtilities;

@Entity
@Audited
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Table(name = "ImageFile")
public class ImageFile extends AuditedEntity implements java.io.Serializable {
    public static final String SELECT_ALL_QUERY = "select imageFile from ImageFile imageFile";
    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";

    private static final long serialVersionUID = -3885332582642450795L;
    private Integer imageFileId;

    private String description;
    private Set<LanguageImage> languageImages = new HashSet<LanguageImage>(0);

    public ImageFile() {
    }

    public ImageFile(final Integer imageFileId) {
        this.imageFileId = imageFileId;
    }

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "ImageFileID", unique = true, nullable = false)
    public Integer getImageFileId() {
        return imageFileId;
    }

    public void setImageFileId(final Integer imageFileId) {
        this.imageFileId = imageFileId;
    }

    @Column(name = "Description", columnDefinition = "TEXT")
    @Size(max = 65535)
    public String getDescription() {
        return description;
    }

    public void setDescription(final String description) {
        this.description = description;
    }

    @Transient
    public String getDocbookFileName() {
        if (languageImages != null && languageImages.size() != 0 && imageFileId != null) {
            for (final LanguageImage firstChild : languageImages) {
                if (firstChild.getOriginalFileName() != null) {
                    final int extensionIndex = firstChild.getOriginalFileName().lastIndexOf(".");
                    if (extensionIndex != -1) return imageFileId + firstChild.getOriginalFileName().substring(extensionIndex);
                }
            }
        }

        return "";
    }

    @Transient
    public String getExtension() {
        if (languageImages.size() != 0) {
            for (final LanguageImage firstChild : languageImages) {
                if (firstChild.getOriginalFileName() != null) {
                    return FileUtilities.getFileExtension(firstChild.getOriginalFileName());
                }
            }
        }

        return null;
    }

    @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";
    }

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

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "imageFile", cascade = CascadeType.ALL, orphanRemoval = true)
    @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
    @BatchSize(size = Constants.DEFAULT_BATCH_SIZE)
    public Set<LanguageImage> getLanguageImages() {
        return languageImages;
    }

    @Transient
    public List<LanguageImage> getLanguageImagesArray() {
        final List<LanguageImage> retValue = new ArrayList<LanguageImage>(languageImages);
        Collections.sort(retValue, new Comparator<LanguageImage>() {
            @Override
            public int compare(final LanguageImage o1, final LanguageImage o2) {
                if (o1.getLocale() == null && o2.getLocale() == null) return 0;
                if (o1.getLocale() == null) return -1;
                if (o2.getLocale() == null) return 1;
                return new LocaleValueComparator().compare(o1.getLocale(), o2.getLocale());
            }
        });

        return retValue;
    }

    public void setLanguageImages(Set<LanguageImage> languageImages) {
        this.languageImages = languageImages;
    }

    /**
     * An image file can be represented by multiple translated images. These images can all be from originally differently names
     * files, and can have different dimensions. However, all translated images need to be of the same file file. The
     * getDocbookFileName() method needs to be able to append a fixed extension to the file name when the image is included in a
     * Docbook XML file.
     * <p/>
     * This method will throw an exception if there are inconsistent file extensions on any of the original file names assigned
     * to the translated images.
     *
     * @throws CustomConstraintViolationException
     *
     */
    public void validate() throws CustomConstraintViolationException {
        final String extension = getExtension();
        if (extension != null) {
            for (final LanguageImage langImg : languageImages) {
                if (langImg.getOriginalFileName() != null && !langImg.getOriginalFileName().isEmpty()) {
                    final String thisExtension = FileUtilities.getFileExtension(langImg.getOriginalFileName());
                    if (!thisExtension.toLowerCase().equals(extension.toLowerCase())) throw new CustomConstraintViolationException(
                            "All LanguageImages contained by an ImageFile need to have the same file extension");
                }
            }
        }
    }

    public void addLanguageImage(final LanguageImage languageImage) {
        languageImages.add(languageImage);
        languageImage.setImageFile(this);
    }

    public void removeLanguageImage(final LanguageImage languageImage) {
        languageImages.remove(languageImage);
        languageImage.setImageFile(null);
    }

}