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

import java.io.Serializable;
import java.util.Comparator;

import org.jboss.pressgang.ccms.model.TagToCategory;

public class TagToCategorySortingComparator implements Comparator<TagToCategory>, Serializable {
    private static final long serialVersionUID = -8971118568273772780L;
    private int lessThan;
    private int greaterThan;

    public TagToCategorySortingComparator(boolean accending) {
        lessThan = accending ? -1 : 1;
        greaterThan = accending ? 1 : -1;
    }

    public TagToCategorySortingComparator() {
        lessThan = -1;
        greaterThan = 1;
    }

    /**
     * Sorting order is preferentially used to sort TagToCategory's, or the name of the Tag that
     * the TagToCategory's point to are used if both TagToCategory's sorting orders are null.
     */
    @Override
    public int compare(final TagToCategory o1, final TagToCategory o2) {
        if (o1 == null && o2 == null) return 0;

        if (o1 == null) return lessThan;

        if (o2 == null) return greaterThan;

        if (o1.getSorting() == null && o2.getSorting() == null) return compareSecondLevel(o1, o2);

        if (o1.getSorting() == null) return greaterThan;
        if (o2.getSorting() == null) return lessThan;

        if (o1.getSorting().equals(o2.getSorting())) return compareSecondLevel(o1, o2);

        return o1.getSorting().compareTo(o2.getSorting()) * greaterThan;
    }

    protected int compareSecondLevel(final TagToCategory o1, final TagToCategory o2) {
        if (o2 == null) return greaterThan;

        if (o1.getTag() == null && o2.getTag() == null) return 0;
        if (o1.getTag() == null) return greaterThan;
        if (o2.getTag() == null) return lessThan;

        if (o1.getTag().getTagName() == null && o2.getTag().getTagName() == null) return 0;
        if (o1.getTag().getTagName() == null) return lessThan;
        if (o2.getTag().getTagName() == null) return greaterThan;

        return o1.getTag().getTagName().compareTo(o2.getTag().getTagName()) * greaterThan;
    }
}


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

import com.googlecode.icegem.serialization.codegen.XProperty;
import com.googlecode.icegem.serialization.codegen.impl.FromDataProcessor;

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

/**
 * Generate code that
 * 1) read 'java.util.Date' field value from DataInput
 * 2) write to bean-setter
 * for method DataSerializer.fromData(...)
 *
 * @author igolovach
 */

public class FromDataFieldDateProcessor implements FromDataProcessor { //todo: LocalGregorianCalendar
    public String process(XProperty field) {
        String fieldName = field.getName();
        return "if (in.readByte() != 0) {\n" +
                tab("result.set" + firstLetterToUpperCase(fieldName) + "(new java.util.Date(in.readLong()));\n") +
                "}\n";
    }
}


package org.jboss.pressgang.ccms.liquibase.base;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;

import liquibase.change.custom.CustomTaskChange;
import liquibase.database.Database;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.CustomChangeException;
import liquibase.exception.DatabaseException;
import liquibase.exception.SetupException;
import liquibase.exception.ValidationErrors;
import liquibase.resource.ResourceAccessor;

public abstract class AbstractMigrateLocales implements CustomTaskChange {
    protected static final Integer BATCH_SIZE = 100;

    protected Map<String, Integer> buildLocaleMap(final Statement stmt) throws SQLException, DatabaseException {
        final HashMap<String, Integer> localeMap = new HashMap<String, Integer>();

        ResultSet rs = null;
        try {
            rs = stmt.executeQuery("SELECT LocaleID, Value FROM Locale");
            while (rs.next()) {
                localeMap.put(rs.getString("Value"), rs.getInt("LocaleID"));
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }

        return localeMap;
    }

    protected void migrateTableWithIndexes(final JdbcConnection conn, final Statement stmt, final String tableName,
            final String localeColumnName) throws SQLException, DatabaseException, CustomChangeException {

        PreparedStatement preparedStmt = null;
        try {
            // Get the mapping
            final Map<String, Integer> localeMap = buildLocaleMap(stmt);

            // Do the updates
            preparedStmt = conn.prepareStatement("UPDATE " + tableName + " SET LocaleID = ? WHERE " + localeColumnName + " = ?");

            for (Map.Entry<String, Integer> entry : localeMap.entrySet()) {
                preparedStmt.setInt(1, entry.getValue());
                preparedStmt.setString(2, entry.getKey());

                // Add the query to the batch
                preparedStmt.addBatch();
            }

            // Workaround some locales
            migrateBrokenLocales(preparedStmt, localeMap);

            preparedStmt.executeBatch();
        } finally {
            if (preparedStmt != null) {
                preparedStmt.close();
            }
        }
    }

    /**
     * Migrates locales from a very early version of PressGang that was using the wrong locale value.
     *
     * @param preparedStmt
     * @param localeMap
     * @throws SQLException
     */
    private void migrateBrokenLocales(final PreparedStatement preparedStmt, final Map<String, Integer> localeMap) throws SQLException {
        final Integer enUSLocaleID = localeMap.get("en-US");
        if (enUSLocaleID != null && enUSLocaleID > 0) {
            migrateBrokenLocale(preparedStmt, localeMap, "es_US", enUSLocaleID);
            migrateBrokenLocale(preparedStmt, localeMap, "en_AU", enUSLocaleID);
            migrateBrokenLocale(preparedStmt, localeMap, "ar", enUSLocaleID);
            migrateBrokenLocale(preparedStmt, localeMap, "as", enUSLocaleID);
        }
    }

    private void migrateBrokenLocale(final PreparedStatement preparedStmt, final Map<String, Integer> localeMap, final String locale,
            final Integer enUSLocaleID) throws SQLException {
        if (!localeMap.containsKey(locale)) {
            preparedStmt.setInt(1, enUSLocaleID);
            preparedStmt.setString(2, locale);

            // Add the query to the batch
            preparedStmt.addBatch();
        }
    }

    protected void migrateTableWithoutIndexes(final JdbcConnection conn, final Statement stmt, final String tableName,
            final String idColumnName, final String localeColumnName) throws SQLException, DatabaseException, CustomChangeException {
        PreparedStatement preparedStmt = null;
        ResultSet rs = null;
        try {
            // Get the mapping
            final Map<String, Integer> localeMap = buildLocaleMap(stmt);

            // Find the data to copy
            final Map<Integer, Integer> specToLocaleMap = new HashMap<Integer, Integer>();
            rs = stmt.executeQuery("SELECT " + idColumnName + ", " + localeColumnName + " FROM " + tableName);
            while (rs.next()) {
                final String locale = rs.getString(localeColumnName);
                if (locale != null && localeMap.containsKey(locale)) {
                    specToLocaleMap.put(rs.getInt(idColumnName), localeMap.get(locale));
                } else if ("es_US".equals(locale) || "en_AU".equals(locale) || "ar".equals(locale) || "as".equals(locale)) {
                    // Fix rogue es_US locales
                    specToLocaleMap.put(rs.getInt(idColumnName), localeMap.get("en-US"));
                } else if (locale != null) {
                    throw new CustomChangeException("No locale found for " + locale);
                }
            }
            rs.close();

            // Do the updates
            preparedStmt = conn.prepareStatement("UPDATE " + tableName + " SET LocaleID = ? WHERE " + idColumnName + " = ?");

            int count = 0;
            for (Map.Entry<Integer, Integer> entry : specToLocaleMap.entrySet()) {
                preparedStmt.setInt(1, entry.getValue());
                preparedStmt.setInt(2, entry.getKey());

                // Add the query to the batch
                preparedStmt.addBatch();

                // Check if we should flush the batches
                if (count % BATCH_SIZE == 0) {
                    preparedStmt.executeBatch();
                }
                count++;
            }

            preparedStmt.executeBatch();
        } finally {
            if (rs != null) {
                rs.close();
            }
            if (preparedStmt != null) {
                preparedStmt.close();
            }
        }
    }

    @Override
    public final void execute(Database database) throws CustomChangeException {
        final JdbcConnection conn = (JdbcConnection) database.getConnection();

        Statement stmt = null;
        try {
            stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
            _execute(conn, stmt);
        } catch (DatabaseException e) {
            throw new CustomChangeException(e);
        } catch (SQLException e) {
            throw new CustomChangeException(e);
        } finally {
            try {
                if (stmt != null) {
                    stmt.close();
                }
            } catch (Exception e) {
                // Ignore this one, already closed probably
            }
        }
    }

    protected abstract void _execute(final JdbcConnection conn, final Statement stmt) throws SQLException, DatabaseException,
            CustomChangeException;

    @Override
    public void setUp() throws SetupException {
    }

    @Override
    public void setFileOpener(ResourceAccessor resourceAccessor) {

    }

    @Override
    public ValidationErrors validate(Database database) {
        return new ValidationErrors();
    }
}


/*
  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 static ch.lambdaj.Lambda.filter;
import static ch.lambdaj.Lambda.having;
import static ch.lambdaj.Lambda.on;
import static ch.lambdaj.collection.LambdaCollections.with;
import static org.hamcrest.Matchers.equalTo;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import org.jboss.pressgang.ccms.model.Category;
import org.jboss.pressgang.ccms.model.MinHash;
import org.jboss.pressgang.ccms.model.MinHashXOR;
import org.jboss.pressgang.ccms.model.Tag;
import org.jboss.pressgang.ccms.model.TagToCategory;
import org.jboss.pressgang.ccms.model.Topic;
import org.jboss.pressgang.ccms.model.TopicToTag;
import org.jboss.pressgang.ccms.model.TopicToTopic;
import org.jboss.pressgang.ccms.model.constants.Constants;
import org.jboss.pressgang.ccms.model.sort.TagToCategorySortingComparator;
import org.jboss.pressgang.ccms.utils.common.HashUtilities;
import org.jboss.pressgang.ccms.utils.common.XMLUtilities;
import org.w3c.dom.Document;

public class TopicUtilities {
    private static final int SHINGLE_WORD_COUNT = 5;

    /**
     * Recalculate the min hash signature for a topic.
     * @param topic The topic to generate a signature for
     * @param minHashXORs The list of XOR values to apply to the hash code
     * @return true if the minhashes were updated, false otherwise
     */
    public static boolean recalculateMinHash(final Topic topic, final List<MinHashXOR> minHashXORs) {
        boolean retValue = false;

        final Set<MinHash> existingMinHashes = topic.getMinHashes();

        final Map<Integer, Integer> minHashes = getMinHashes(topic.getTopicXML(), minHashXORs);

        for (final Integer funcId : minHashes.keySet()) {

            boolean found = false;
            for (final MinHash minHash : existingMinHashes) {
                if (minHash.getMinHashFuncID().equals(funcId)) {
                    if (!minHash.getMinHash().equals(minHashes.get(funcId))) {
                        minHash.setMinHash(minHashes.get(funcId));
                        retValue = true;
                    }
                    found = true;
                    break;
                }
            }

            if (!found) {
                retValue = true;
                final MinHash minHash = new MinHash();
                minHash.setMinHashFuncID(funcId);
                minHash.setMinHash(minHashes.get(funcId));
                topic.addMinHash(minHash);
            }
        }

        return retValue;
    }

    /**
     * Generate the min hashes
     * @param xml The content to apply the signature to
     * @param minHashXORs The list of XOR values to apply to the hash code
     * @return
     */
    public static Map<Integer, Integer> getMinHashes(final String xml, final List<MinHashXOR> minHashXORs) {
        final Map<Integer, Integer> retValue = new HashMap<Integer, Integer>();

        // Clean the XML to remove element names and other useless data
        final String cleanedXML = cleanXMLForMinHash(xml);

        // the first minhash uses the builtin hashcode only
        final Integer baseMinHash = getMinHashInternal(cleanedXML, null);
        if (baseMinHash != null) {
            retValue.put(0, baseMinHash);
        }

        for (int funcId = 1; funcId < org.jboss.pressgang.ccms.model.constants.Constants.NUM_MIN_HASHES; ++funcId) {
            boolean foundMinHash = false;
            for (final MinHashXOR minHashXOR : minHashXORs) {
                if (minHashXOR.getMinHashXORFuncId() == funcId) {
                    final Integer minHash = getMinHashInternal(cleanedXML, minHashXOR.getMinHashXOR());
                    if (minHash != null) {
                        retValue.put(funcId, minHash);
                    }
                    foundMinHash = true;
                    break;
                }
            }
            if (!foundMinHash) {
                throw new IllegalStateException("Did not find a minhash xor int for function " + funcId);
            }
        }

        return retValue;
    }

    /**
     * Returns the minimum hash of the sentences in an XML file.
     * @param xml The xml to analyse
     * @param xor the number to xor the hash against. Null if the standard hashCode() method should be used alone.
     * @return The minimum hash
     */
    public static Integer getMinHash(final String xml, final Integer xor) {
        return getMinHashInternal(cleanXMLForMinHash(xml), xor);
    }

    /**
     * Cleans the provided XML by removing element names and other useless data.
     *
     * @param xml The xml to be cleaned.
     * @return The cleaned xml.
     */
    protected static String cleanXMLForMinHash(final String xml) {
        // Treat null and empty strings the same
        final String fixedXML = xml == null ? "" : xml;

        String text = null;
        try {
            final Document doc = XMLUtilities.convertStringToDocument(fixedXML);
            if (doc != null) {
                text = doc.getDocumentElement().getTextContent();
            }
        } catch (final Exception ex) {
            // Do nothing
        }

        // the xml was invalid, so just strip out xml elements manually
        if (text == null) {
            text = fixedXML.replaceAll("</?.*?/?>", " ");
        }

        return text;
    }

    /**
     * Returns the minimum hash of the sentences in an XML file.
     * @param cleanedXML The cleaned xml to analyse
     * @param xor the number to xor the hash against. Null if the standard hashCode() method should be used alone.
     * @return The minimum hash
     */
    protected static Integer getMinHashInternal(final String cleanedXML, final Integer xor) {
        // now generate the minhashes
        String[] words = cleanedXML.replaceAll("\\p{Punct}", " ").split("\\s+");

        if (words.length == 0) {
            words = new String[] {""};
        }

        final List<String> shingles = new ArrayList<String>();

        if (words.length < SHINGLE_WORD_COUNT) {
            final StringBuilder shingle = new StringBuilder();
            for (int i = 0; i < words.length; ++i) {
                if (shingle.length() != 0) {
                    shingle.append(" ");
                }
                shingle.append(words[i]);
            }
            final int hash =  shingle.toString().hashCode();
            if (xor != null) {
                return hash ^ xor;
            } else {
                return hash;
            }
        } else {
            for (int i = 0; i < words.length - SHINGLE_WORD_COUNT + 1; ++i) {
                final StringBuilder shingle = new StringBuilder();
                for (int j = i; j < words.length && j < i + SHINGLE_WORD_COUNT; ++j) {
                    if (shingle.length() != 0) {
                        shingle.append(" ");
                    }
                    shingle.append(words[j]);
                }
                shingles.add(shingle.toString());
            }

            Integer minHash = null;
            for (final String string : shingles) {
                final int hash = string.hashCode();
                final int finalHash = xor != null ? hash ^ xor : hash;
                if (minHash == null || finalHash < minHash) {
                    minHash = finalHash;
                }
            }
            return minHash;
        }
    }

    public static List<Integer> getMatchingMinHash(final EntityManager entityManager, final Integer topicId, final Float threshold) {
        // get the source topic
        final Topic sourceTopic = entityManager.find(Topic.class, topicId);
        if (sourceTopic.getMinHashes().size() == 0) {
            /*
                If the source topic does not have a minhash signature, force the search query to
                match a non existent topic id so no results are returned.
             */
            return Arrays.asList(-1);
        }

        final Map<Integer, Integer> minhashes = new HashMap<Integer, Integer>();
        for (final MinHash minHash : sourceTopic.getMinHashes()) {
            minhashes.put(minHash.getMinHashFuncID(), minHash.getMinHash());
        }

        final List<Integer> matchingTopics = getMatchingMinHash(entityManager, minhashes, threshold);
        matchingTopics.remove(topicId);
        if (matchingTopics.size() == 0) {
            /*
                If there are no matches, force the search query to
                match a non existent topic id so no results are returned.
             */
            return Arrays.asList(-1);
        }

        return matchingTopics;
    }

    /**
     * Matching the minhash signature of a document relies on a process known as locality sensitive hashing.
     * A good explaination of this process can be found at http://infolab.stanford.edu/~ullman/mmds/ch3.pdf.
     *
     * To implement this feature, we need to group the minhash signatures into bands. When two topics share
     * the same minhash in a single band, they are considered a candidate pair for further testing.
     *
     * The documentation above suggests hashing the minhash values that fall into a band, and then comparing
     * these hashed band values to find candidates. Our implementation will defer this to the database by
     * finding the number of topics that have matching minhash values in all rows in a band.
     *
     * The number of rows and bands is calculated such that the threshold is approximately Math.pow(1/b, 1/r). This
     * formula means that increasing the threshold results in an increased number of rows and a decreased number
     * of bands. We get a close approximation by running through a bunch of combinations and seeing what fits best.
     *
     * @param entityManager The entity manager to use for queries
     * @param minhashes The minhash signature to match topics to
     * @param threshold How similar we want two documents to be to be considered a match. This will be forced to a value
     *                  between 0.6 and 0.9.
     * @return
     */
    public static List<Integer> getMatchingMinHash(final EntityManager entityManager, final Map<Integer, Integer> minhashes, final Float threshold) {
        try {

            if (minhashes.size() == 0) {
                /*
                    If the source does not have a minhash signature, force the search query to
                    match a non existent topic id so no results are returned.
                 */
                return new ArrayList<Integer>(){{add(-1);}};
            }

            Float fixedThreshold = Constants.MIN_DOCUMENT_SIMILARITY;
            if (threshold > Constants.MAX_DOCUMENT_SIMILARITY) {
                fixedThreshold = Constants.MAX_DOCUMENT_SIMILARITY;
            } else if (threshold >= Constants.MIN_DOCUMENT_SIMILARITY) {
                fixedThreshold = threshold;
            }

            final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
            final CriteriaQuery<Integer> criteriaQuery = criteriaBuilder.createQuery(Integer.class);
            final Root<MinHash> minHashRoot = criteriaQuery.from(MinHash.class);

            final Set<Integer> candidates = new HashSet<Integer>();

            /*
                Note to self - It may be possible to perform these band queries as one big query through something like
                cb.having(cb.equal(cb.toInteger(cb.prod(minHashRoot.<Integer>get("minHashFuncID"), 1.0/lhsRows), band).
                However, unless lhsRows * bands exactly equals Constants.NUM_MIN_HASHES there needs to be some additional
                logic for the last band, which will have less than lhsRows rows in it.
             */

            for (int band = 0; band < Constants.MIN_HASH_BANDS; ++band) {
                final List<Predicate> rowMatches = new ArrayList<Predicate>();
                for (int row = band * Constants.MIN_HASH_ROWS; row < (band * Constants.MIN_HASH_ROWS) + Constants.MIN_HASH_ROWS; ++row) {
                    Integer sourceMinHash = null;
                    if (minhashes.containsKey(row)) {
                        sourceMinHash = minhashes.get(row);
                    } else {
                        throw new IllegalArgumentException("minhashes did not contain a minhash for function " + row);
                    }

                    rowMatches.add(criteriaBuilder.and(
                            criteriaBuilder.equal(minHashRoot.<Integer>get("minHashFuncID"), row),
                            criteriaBuilder.equal(minHashRoot.<Integer>get("minHash"), sourceMinHash)
                    ));
                }

                final Predicate minHashOrs = criteriaBuilder.or(rowMatches.toArray(new Predicate[]{}));

                final CriteriaQuery<Integer> query = criteriaQuery
                        .select(minHashRoot.<Topic>get("topic").<Integer>get("topicId"))
                        .distinct(true)
                        .where(minHashOrs)
                        .groupBy(minHashRoot.<Integer>get("topic").<Integer>get("topicId"))
                        .having(criteriaBuilder.equal(criteriaBuilder.count(minHashRoot.<Integer>get("topic").<Integer>get("topicId")), rowMatches.size()));

                candidates.addAll(entityManager.createQuery(query).getResultList());
            }

            final List<Integer> matchingTopics = new ArrayList<Integer>();

            if (candidates.size() != 0) {
                // at this point candidates should now list topic ids that are a potential match to the source topic.
                final CriteriaQuery<Topic> topicCQ = criteriaBuilder.createQuery(Topic.class);
                final Root<Topic> topicRoot = topicCQ.from(Topic.class);
                final CriteriaBuilder.In<Integer> in = criteriaBuilder.in(topicRoot.<Integer>get("topicId"));
                for (final Integer candidate : candidates) {
                    in.value(candidate);
                }
                final CriteriaQuery<Topic> topicQuery = topicCQ.select(topicRoot).where(in);
                final List<Topic> topics = entityManager.createQuery(topicQuery).getResultList();

                // we now have a list of topics that are possible candidates for a match. Now we compare the minhash values
                // to see what the similarity actually is.

                for (final Topic topic : topics) {
                    int matches = 0;

                    for (final MinHash otherMinHash : topic.getMinHashes()) {
                        if (minhashes.containsKey(otherMinHash.getMinHashFuncID())) {
                            if (minhashes.get(otherMinHash.getMinHashFuncID()).equals(otherMinHash.getMinHash())) {
                                ++matches;
                            }
                        } else {
                            throw new IllegalArgumentException("minhashes did not contain a minhash for function " + otherMinHash.getMinHashFuncID());
                        }
                    }

                    if ((float)matches / Constants.NUM_MIN_HASHES >= fixedThreshold) {
                        matchingTopics.add(topic.getId());
                    }
                }

                if (matchingTopics.size() != 0) {
                    return matchingTopics;
                }
            }

            return Arrays.asList(-1);
        } catch (final Exception ex) {
            return null;
        }
    }

    /**
     * Validate and Fix a topics relationships to ensure that the topics related topics are still matched by the Related Topics
     * themselves.
     *
     * @param topic The topic to validate and fix the relationships for.
     */
    public static void validateAndFixRelationships(final Topic topic) {
        /* remove relationships to this topic in the parent collection */
        final ArrayList<TopicToTopic> removeList = new ArrayList<TopicToTopic>();
        for (final TopicToTopic topicToTopic : topic.getParentTopicToTopics())
            if (topicToTopic.getRelatedTopic().getTopicId().equals(topic.getTopicId())) removeList.add(topicToTopic);

        for (final TopicToTopic topicToTopic : removeList)
            topic.getParentTopicToTopics().remove(topicToTopic);

        /* remove relationships to this topic in the child collection */
        final ArrayList<TopicToTopic> removeChildList = new ArrayList<TopicToTopic>();
        for (final TopicToTopic topicToTopic : topic.getChildTopicToTopics())
            if (topicToTopic.getMainTopic().getTopicId().equals(topic.getTopicId())) removeChildList.add(topicToTopic);

        for (final TopicToTopic topicToTopic : removeChildList)
            topic.getChildTopicToTopics().remove(topicToTopic);
    }

    /**
     * Set the content hash on the topic.
     * @param topic The topic to update
     */
    public static void updateContentHash(final Topic topic) {
        if (topic.getTopicXML() != null) {
            topic.setTopicContentHash(HashUtilities.generateSHA256(topic.getTopicXML()).toCharArray());
        }
    }

    /**
     * Validate and Fix a topics tags so that mutually exclusive tags are enforced and also remove any tags that may have been
     * duplicated.
     *
     * @param topic The topic to fix the tags for.
     */
    public static void validateAndFixTags(final Topic topic) {
        /*
         * validate the tags that are applied to this topic. generally the gui should enforce these rules, with the exception of
         * the bulk tag apply function
         */

        // Create a collection of Categories mapped to TagToCategories, sorted by the Category sorting order
        final TreeMap<Category, ArrayList<TagToCategory>> tagDB = new TreeMap<Category, ArrayList<TagToCategory>>(
                Collections.reverseOrder());

        for (final TopicToTag topicToTag : topic.getTopicToTags()) {
            final Tag tag = topicToTag.getTag();
            for (final TagToCategory tagToCategory : tag.getTagToCategories()) {
                final Category category = tagToCategory.getCategory();

                if (!tagDB.containsKey(category)) tagDB.put(category, new ArrayList<TagToCategory>());

                tagDB.get(category).add(tagToCategory);
            }
        }

        // now remove conflicting tags
        for (final Category category : tagDB.keySet()) {
            /* sort by the tags position in the category */
            Collections.sort(tagDB.get(category), new TagToCategorySortingComparator(false));

            /*
             * because of the way we have ordered the tagDB collections, and the ArrayLists it contains, this process will
             * remove those tags that belong to lower priority categories, and lower priority tags in those categories
             */

            final ArrayList<TagToCategory> tagToCategories = tagDB.get(category);

            // remove tags in the same mutually exclusive categories
            if (category.isMutuallyExclusive() && tagToCategories.size() > 1) {
                while (tagToCategories.size() > 1) {
                    final TagToCategory tagToCategory = tagToCategories.get(1);
                    /* get the lower priority tag */
                    final Tag removeTag = tagToCategory.getTag();
                    /* remove it from the tagDB collection */
                    tagToCategories.remove(tagToCategory);

                    /* and remove it from the tag collection */
                    final ArrayList<TopicToTag> removeTopicToTagList = new ArrayList<TopicToTag>();
                    for (final TopicToTag topicToTag : topic.getTopicToTags()) {
                        if (topicToTag.getTag().equals(removeTag)) removeTopicToTagList.add(topicToTag);
                    }

                    for (final TopicToTag removeTopicToTag : removeTopicToTagList) {
                        topic.getTopicToTags().remove(removeTopicToTag);
                    }
                }
            }

            /* remove tags that are explicitly defined as mutually exclusive */
            for (final TagToCategory tagToCategory : tagToCategories) {
                final Tag tag = tagToCategory.getTag();
                for (final Tag exclusionTag : tag.getExcludedTags()) {
                    if (filter(having(on(TopicToTag.class).getTag(), equalTo(tagToCategory.getTag())),
                            topic.getTopicToTags()).size() != 0 && // make
                            /*
                             * sure that we have not removed this tag already
                             */
                            filter(having(on(TopicToTag.class).getTag(), equalTo(exclusionTag)),
                                    topic.getTopicToTags()).size() != 0 && // make
                            /*
                             * sure the exclusion tag exists
                             */
                            !exclusionTag.equals(tagToCategory.getTag())) // make
                    /*
                     * sure we are not trying to remove ourselves
                     */ {
                        with(topic.getTopicToTags()).remove(having(on(TopicToTag.class).getTag(), equalTo(exclusionTag)));
                    }
                }
            }
        }
    }

    private TopicUtilities() {
    }
}


/*-
 * #%L
 * Nazgul Project: nazgul-tools-visualization-spi-doclet
 * %%
 * Copyright (C) 2010 - 2018 jGuru Europe AB
 * %%
 * Licensed under the jGuru Europe AB license (the "License"), based
 * on Apache License, Version 2.0; you may not use this file except
 * in compliance with the License.
 *
 * You may obtain a copy of the License at
 *
 *       http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

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

import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.Doc;
import com.sun.javadoc.MemberDoc;
import com.sun.javadoc.PackageDoc;

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

/**
 * Utility class collecting comparators and algorithms for working with RootDoc, ClassDoc, MemberDoc etc.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public final class Comparators {

    /**
     * Forgiving name converter which substitutes a null input with an empty string.
     */
    public static String toString(final Doc aDoc) {
        return aDoc == null ? "" : aDoc.name();
    }

    public enum MemberOrder {

        CONSTANT,

        STATIC_METHOD,

        FIELD,

        CONSTRUCTOR,

        METHOD;

        public static MemberOrder parse(final MemberDoc memberDoc) {

            MemberOrder toReturn = null;

            // #1) Sort fields.
            //
            if (memberDoc.isField()) {
                toReturn = memberDoc.isStatic() && memberDoc.isFinal() ? CONSTANT : FIELD;
            }

            // #2) Sort methods.
            //
            if (memberDoc.isMethod()) {
                toReturn = memberDoc.isStatic() ? STATIC_METHOD : METHOD;
            }

            // #3) Handle constructors.
            //
            if (memberDoc.isConstructor()) {
                toReturn = CONSTRUCTOR;
            }

            // All Done.
            return toReturn;
        }
    }

    public enum VisibilityModifier {

        PUBLIC,

        PROTECTED,

        PACKAGE_PRIVATE,

        PRIVATE;

        public static VisibilityModifier parse(final MemberDoc memberDoc) {

            VisibilityModifier toReturn = null;
            if (memberDoc.isPublic()) {
                toReturn = PUBLIC;
            } else if (memberDoc.isProtected()) {
                toReturn = PROTECTED;
            } else if (memberDoc.isPackagePrivate()) {
                toReturn = PACKAGE_PRIVATE;
            } else {
                toReturn = PRIVATE;
            }

            // All Done.
            return toReturn;
        }
    }

    /**
     * Comparator relating {@link PackageDoc}s to their names, using {@link #toString(Doc)}.
     */
    public static final Comparator<PackageDoc> PACKAGE_NAME_COMPARATOR = Comparator.comparing(Comparators::toString);

    /**
     * Comparator relating {@link ClassDoc}s to their names, using {@link #toString(Doc)}.
     */
    public static final Comparator<ClassDoc> CLASS_NAME_COMPARATOR = Comparator.comparing(Comparators::toString);

    /**
     * Comparator relating {@link MemberDoc}s to their natural sort order.
     * This comparator does not handle null values.
     */
    public static final Comparator<MemberDoc> NATURAL_ORDER_MEMBER_COMPARATOR = (l, r) -> {

        // First, compare types
        final MemberOrder leftOrder = MemberOrder.parse(l);
        final MemberOrder rightOrder = MemberOrder.parse(r);
        int toReturn = leftOrder.compareTo(rightOrder);

        // Second, compare visibilities
        if (toReturn == 0) {
            final VisibilityModifier leftVis = VisibilityModifier.parse(l);
            final VisibilityModifier rightVis = VisibilityModifier.parse(r);
            toReturn = leftVis.compareTo(rightVis);
        }

        // Third, compare names.
        if (toReturn == 0) {
            toReturn = toString(l).compareTo(toString(r));
        }

        // All Done.
        return toReturn;
    };

    /**
     * Sorts all given ClassDocs into a SortedMap keyed by their respective PackageDocs.
     *
     * @param classDocs an array of ClassDoc objects.
     * @return A SortedMap relating all ClassDocs to their respective PackageDoc.
     */
    public static SortedMap<PackageDoc, SortedSet<ClassDoc>> sortClassesPerPackage(final ClassDoc... classDocs) {

        final SortedMap<PackageDoc, SortedSet<ClassDoc>> toReturn = new TreeMap<>(Comparators.PACKAGE_NAME_COMPARATOR);

        if (classDocs != null) {

            Arrays.stream(classDocs).forEach(current -> {

                // Retrieve (or create) the SortedSet of ClassDocs for the current PackageDocs.
                final SortedSet<ClassDoc> classDocsForCurrentPackage = toReturn.computeIfAbsent(
                        current.containingPackage(),
                        k -> new TreeSet<>(Comparators.CLASS_NAME_COMPARATOR));

                // Add the current ClassDoc to the SortedSet.
                classDocsForCurrentPackage.add(current);
            });
        }

        // All Done.
        return toReturn;
    }
}


/*
 * #%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.EnforcerRule;
import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 * Abstract Enforcer rule specification, handling some pretty printing of exceptions.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public abstract class AbstractEnforcerRule implements EnforcerRule {

    /**
     * Defines if the results of this AbstractEnforcerRule is cacheable.
     *
     * @see #isCacheable()
     */
    protected boolean cacheable = false;

    /**
     * This is the interface into the rule. This method should throw an exception
     * containing a reason message if the rule fails the check. The plugin will
     * then decide based on the fail flag if it should stop or just log the
     * message as a warning.
     *
     * @param helper The helper provides access to the log, MavenSession and has
     *               helpers to get common components. It is also able to lookup components
     *               by class name.
     * @throws org.apache.maven.enforcer.rule.api.EnforcerRuleException the enforcer rule exception
     */
    @Override
    @SuppressWarnings("PMD.PreserveStackTrace")
    public final void execute(final EnforcerRuleHelper helper) throws EnforcerRuleException {

        final MavenProject project;
        try {
            project = (MavenProject) helper.evaluate("${project}");
        } catch (final ExpressionEvaluationException e) {

            // Whoops.
            final String msg = "Could not acquire MavenProject. (Expression lookup failure for: "
                    + e.getLocalizedMessage() + ")";
            throw new EnforcerRuleException(msg, e);
        }

        // Delegate.
        try {
            performValidation(project, helper);
        } catch (RuleFailureException e) {

            // Create a somewhat verbose failure message.
            String message =
                    "\n"
                            + "\n#"
                            + "\n# Structure rule failure:"
                            + "\n# " + getShortRuleDescription()
                            + "\n# "
                            + "\n# Message: " + e.getLocalizedMessage()
                            + "\n# " + "\n# Offending project [" + project.getGroupId() + ":"
                            + project.getArtifactId() + ":" + project.getVersion() + "]" + "\n#";

            final Artifact art = e.getOffendingArtifact();
            if (art != null) {

                message += "\n# Offending artifact [" + art.getGroupId() + ":" + art.getArtifactId()
                        + ":" + art.getVersion() + "]"
                        + "\n#";
            }
            message += "\n";

            // Re-throw for pretty print
            throw new EnforcerRuleException(message);
        }
    }

    /**
     * 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.
     */
    protected abstract void performValidation(final MavenProject project, final EnforcerRuleHelper helper)
            throws RuleFailureException;

    /**
     * @return A human-readable short description for this AbstractEnforcerRule.
     * (Example: "No -impl dependencies permitted in this project")
     */
    protected abstract String getShortRuleDescription();

    /**
     * This method tells the enforcer if the rule results may be cached.
     * If the result is true, the results will be remembered for future executions
     * in the same build (ie children).
     * Subsequent iterations of the rule will be queried to see if they are also cacheable.
     * This will allow the rule to be uncached further down the tree if needed.
     *
     * @return true if rule is cacheable
     */
    @Override
    public boolean isCacheable() {
        return cacheable;
    }

    /**
     * Helper method which splices the provided string into a List, separating on commas.
     *
     * @param toSplice The string to splice
     * @return A list holding the elements of the spliced string.
     */
    protected static List<String> splice(final String toSplice) {

        final List<String> toReturn = new ArrayList<String>();

        final StringTokenizer tok = new StringTokenizer(toSplice, ",", false);
        while (tok.hasMoreTokens()) {
            toReturn.add(tok.nextToken());
        }

        return toReturn;
    }

    /**
     * Helper method which splices the provided String into a List of Pattern instances.
     *
     * @param toSplice The string to splice
     * @return A List holding the elements of the spliced string, converted to Patterns
     * @throws PatternSyntaxException if the {@code Pattern.compile} method could not compile the provided string.
     */
    protected static List<Pattern> splice2Pattern(final String toSplice) throws PatternSyntaxException {

        final List<Pattern> toReturn = new ArrayList<Pattern>();

        for (final String current : splice(toSplice)) {
            toReturn.add(Pattern.compile(current));
        }

        return toReturn;
    }

    /**
     * Matches the provided {@code toMatch} string with all Patterns in the patternList.
     * If one pattern matches, this method returns {@code true}.
     *
     * @param toMatch     The string to match to every Pattern in the supplied patternList.
     * @param patternList The List of Patterns to use in matching.
     * @return {@code true} if one pattern in the patternList matches, this method returns {@code true}.
     */
    protected static boolean matches(final String toMatch, final List<Pattern> patternList) {

        for (final Pattern current : patternList) {
            if (current.matcher(toMatch).matches()) {
                return true;
            }
        }

        return false;
    }

    /**
     * Checks if any element within source startsWith the provided toCheck string.
     *
     * @param source  The list of strings which could possibly contain toCheck.
     * @param toCheck The string to validate.
     * @return true if any element within source returns true to
     * toCheck.startsWith(element).
     */
    protected static boolean containsPrefix(final List<String> source, final String toCheck) {

        if (source != null) {

            for (final String current : source) {
                if (toCheck.startsWith(current)) {
                    return true;
                }
            }
        }

        // The prefix was not found within the provided string toCheck.
        return false;
    }
}


package nl.pwiddershoven.jfactory;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.*;

public class Factory {

    private static List<String> factoryPackages = new ArrayList<String>();
    private static Map<Class<?>, Class<?extends ObjectFactory>> factoryClasses;

    public static void addFactoryScanPackage(String factoriesPackage) {
        Factory.factoryPackages.add(factoriesPackage);
    }

    public static <T> T build(Class<T> objectClass, Object... attributes) {
        ObjectFactory<T> objectFactory = getFactory(objectClass);
        return objectFactory.build(attributes);
    }

    public static <T> T create(Class<T> objectClass, Object... attributes) {
        PersistableObjectFactory<T> objectFactory = getFactory(objectClass);
        return objectFactory.create(attributes);
    }

    @SuppressWarnings("unchecked")
    public static <T> T getFactory(final Class<?> factoryClass) {
        Class<?extends ObjectFactory> factory = getFactoryClass(factoryClass);

        try {
            Constructor constructor = factory.getConstructor();
            return (T)constructor.newInstance();
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Get the class of the Factory for the given factoryClass.
     * @param factoryClass
     * @return
     */
    private static Class<?extends ObjectFactory> getFactoryClass(Class<?> factoryClass) {
        if(factoryPackages.size() == 0) throw new IllegalArgumentException("No package provide to look for factories.");

        if(factoryClasses == null) {
            factoryClasses = new HashMap<Class<?>, Class<? extends ObjectFactory>>();
            Set<Class<?extends ObjectFactory>> classes = ReflectionUtils.getSubclassesOf(ObjectFactory.class, factoryPackages);

            for(Class<?extends ObjectFactory> clazz : classes) {
                if(! Modifier.isAbstract(clazz.getModifiers())) {
                    try {
                        Constructor constructor = clazz.getConstructor();
                        ObjectFactory factory = (ObjectFactory)constructor.newInstance();
                        factoryClasses.put(factory.getFactoryClass(), factory.getClass());
                    } catch (Exception e) {
                        throw new RuntimeException(e); //should not happen, compiler forces factory classes to implement correct constructor.
                    }
                }
            }
        }

        return factoryClasses.get(factoryClass);
    }
}


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

/**
 * AbstractStringRenderer implementation which renders a full Graph.
 * Delegates most rendering to an internal StatementsRenderer instance.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 * @see StatementsRenderer
 */
public class GraphRenderer extends AbstractStringRenderer<Graph> {

    // Internal state
    private StatementsRenderer statementsRenderer;

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

        // Delegate
        super(Graph.class);

        // Assign internal state
        this.statementsRenderer = new StatementsRenderer();
    }

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

        // First, configure the RenderConfiguration for directed-ness.
        config.setDirectedGraph(graph.isDigraph());

        // We should use a 3-step rendering strategy.
        final String prologue =
                config.getIndent() + (graph.isStrict() ? "strict " : "")
                        + (graph.isDigraph() ? "digraph" : "graph")
                        + " " + quote(graph.getId())
                        + " {" + config.getNewline();

        // Render all the statements within the supplied Graph.
        // Increase the indentation level to achieve pretty printing.
        //
        // Also, note that the render() method appends a NEWLINE to the result.
        final String renderedStatements = statementsRenderer.render(
                config.cloneAndChangeIndentation(1),
                graph.getStatements());

        // Render the epilogue.
        final String epilogue = config.getIndent() + "}";

        // All Done.
        return prologue + renderedStatements + epilogue;
    }
}


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

import com.googlecode.icegem.utils.PropertiesHelper;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

/**
 * Service to simplify the email sending
 */
public class EmailService {

	private static EmailService instance = null;

	private Session mailSession;

	private PropertiesHelper propertiesHelper;

	public static EmailService getInstance() {
		if (instance == null) {
			try {
				instance = new EmailService();
			} catch (Exception e) {
				throw new IllegalStateException(
						"Cannot initialize EmailService", e);
			}
		}

		return instance;
	}

	/**
	 * Sends email with subject and content to receivers specified in property file after key "mail.to"
	 *
	 * @param subject - the subject
	 * @param content - the content
	 * @throws MessagingException
	 */
	public void send(String subject, String content) throws MessagingException {
		MimeMessage message = compose(subject, content,
				propertiesHelper.getStringProperty("mail.to"));
		transport(message);
	}

	/**
	 * Sends email with subject and content to receivers specified by argument "to"
	 *
	 * @param subject - the subject
	 * @param content - the content
	 * @param to - the CSV list of receivers
	 * @throws MessagingException
	 */
	public void send(String subject, String content, String to)
			throws MessagingException {
		MimeMessage message = compose(subject, content, to);
		transport(message);
	}

	private EmailService() throws FileNotFoundException, IOException {
		propertiesHelper = new PropertiesHelper("/monitoring.properties");
		mailSession = Session.getDefaultInstance(
				propertiesHelper.getProperties(),
				new javax.mail.Authenticator() {
					protected PasswordAuthentication getPasswordAuthentication() {
						return new PasswordAuthentication(propertiesHelper
								.getStringProperty("mail.user"),
								propertiesHelper
										.getStringProperty("mail.password"));
					}
				});
	}

	private Set<String> csvToSetOfString(String csv) {
		Set<String> resultSet = new HashSet<String>();

		if ((csv != null) && (csv.trim().length() > 0)) {
			for (String s : csv.split(",")) {
				resultSet.add(s.trim());
			}
		}

		return resultSet;
	}

	private MimeMessage compose(String subject, String content, String to)
			throws MessagingException {
		MimeMessage message = new MimeMessage(mailSession);
		message.setSubject(subject);
		message.setContent(content, "text/html; charset=ISO-8859-1");

		message.setFrom(new InternetAddress(propertiesHelper
				.getStringProperty("mail.from")));
		for (String email : csvToSetOfString(to)) {
			message.addRecipient(Message.RecipientType.TO, new InternetAddress(
					email));
		}

		return message;
	}

	private void transport(MimeMessage message) throws MessagingException {
		Transport.send(message);
	}
}


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

import java.util.List;

import com.gemstone.gemfire.cache.client.Pool;
import com.gemstone.gemfire.cache.execute.FunctionException;
import com.gemstone.gemfire.cache.execute.FunctionService;
import com.gemstone.gemfire.cache.execute.ResultCollector;
import com.googlecode.icegem.cacheutils.monitor.function.ZeroFunction;

public class FunctionExecutionThread extends Thread {
	private Pool pool;
	private int zero = -1;

	public FunctionExecutionThread(Pool pool) {
		this.pool = pool;
	}

	@Override
	public void run() {
		try {
			zero = executeZeroFunction(pool);
		} catch (Throwable t) {
			t.printStackTrace();
		}
	}

	/**
	 * Executes the "zero" function using pool for the concrete server.
	 *
	 * @param pool
	 *            - the pool for the concrete server
	 * @return 0 in case of function executed without problems, -1 otherwise
	 * @throws FunctionException
	 * @throws InterruptedException
	 */
	private int executeZeroFunction(Pool pool) throws FunctionException,
		InterruptedException {
		int result = -1;

		ResultCollector<?, ?> collector = FunctionService.onServer(pool)
			.execute(new ZeroFunction());

		List<?> functionResult = (List<?>) collector.getResult();
		if ((functionResult != null) && (functionResult.size() == 1)
			&& (functionResult.get(0) instanceof Integer)) {
			result = (Integer) functionResult.get(0);
		}

		return result;
	}

	public int getZero() {
		return zero;
	}
}


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

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

    // Internal state
    private NodeIdRenderer nodeIdRenderer;

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

        // Delegate
        super(RightSideEdge.class);

        // Create internal state
        nodeIdRenderer = new NodeIdRenderer();
    }

    /**
     *
Renders this {@link RightSideEdge} according to the structure:

     *
edgeop (node_id | subgraph) [ edgeRHS ]

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

        final String prefix = RightSideEdge.getEdgeSeparator(config.isDirectedGraph())
                + " "
                + (entity.getNodeID() != null
                ? nodeIdRenderer.doRender(config, entity.getNodeID())
                : entity.getSubgraph().getId());

        // Recurse if this RightSideEdge has a RightSideEdge in turn.
        String renderedRightSideOfThisEdge = "";
        if (entity.getRightSideEdge() != null) {
            renderedRightSideOfThisEdge = " " + doRender(config, entity.getRightSideEdge());
        }

        // All Done.
        return prefix + renderedRightSideOfThisEdge;
    }
}


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

import com.gemstone.gemfire.cache.execute.*;
import com.gemstone.gemfire.cache.partition.PartitionRegionHelper;
import com.gemstone.gemfire.cache.query.*;
import com.gemstone.gemfire.cache.query.internal.DefaultQuery;
import com.gemstone.gemfire.internal.cache.LocalDataSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * Function for executing query on a specified set of buckets.
 * Do not call this function directly. Use bucket oriented query service instead.
 *
 * @see com.googlecode.icegem.query.bucketoriented.BucketOrientedQueryService
 *
 * @author Andrey Stepanov aka standy
 */
public class BucketOrientedQueryFunction extends FunctionAdapter {
    /** Function ID. */
    private final static String FUNCTION_ID = BucketOrientedQueryFunction.class.getName();

    /** Serial version UID. */
    private static final long serialVersionUID = -8818891792083706794L;

    /** Logger. */
    private Logger logger = LoggerFactory.getLogger(BucketOrientedQueryFunction.class);

    /**
     * Executes query using arguments query string and query parameters.
     *
     * @param functionContext Function context.
     */
    @Override
    @SuppressWarnings({ "ThrowableInstanceNeverThrown", "unchecked" })
    public void execute(FunctionContext functionContext) {
	ResultSender<Serializable> resultSender = functionContext.getResultSender();

	RegionFunctionContext regionFunctionContext = (RegionFunctionContext) functionContext;

	if (functionContext.getArguments() == null) {
	    handleException(new FunctionException("You must specify function argument for query execution."),
		    resultSender, null);

	    return;
	}

	if (!(functionContext.getArguments() instanceof BucketOrientedQueryFunctionArgument)) {
	    handleException(new FunctionException("Function arguments must be of type "
		    + BucketOrientedQueryFunctionArgument.class.getName() + "."), resultSender, null);

	    return;
	}

	BucketOrientedQueryFunctionArgument argument = (BucketOrientedQueryFunctionArgument) functionContext
		.getArguments();

	LocalDataSet localData = (LocalDataSet) PartitionRegionHelper.getLocalDataForContext(regionFunctionContext);

	QueryService queryService = localData.getCache().getQueryService();

	String queryStr = argument.getQueryString();

	try {
	    Query query = queryService.newQuery(queryStr);

	    SelectResults<?> result = (SelectResults<?>) localData.executeQuery((DefaultQuery) query,
		    argument.getQueryParameters(), localData.getBucketSet());

	    resultSender.lastResult((Serializable) formatResults(result));
	} catch (Exception e) {
	    handleException(e, resultSender, queryStr);
	}
    }

    /**
     * Gets function id.
     *
     * @return Function id.
     */
    @Override
    public String getId() {
	return FUNCTION_ID;
    }

    /**
     * If you use redundancy for partitioned region then GemFire will send this
     * function to those members that contain primary or redundant copy of bucket(s).
     * It can increase number of members that will execute this function.
     * But if you want to send this function only to those members that store primary
     * copy of bucket, you must enable a function option "optimizeForWrite".
     *
     * See a forum link
     * http://forums.gemstone.com/viewtopic.php?f=3&t=496&hilit=bucket+Id&sid=f3b823b748bb253e5019e489c8480fbd
     * for details.
     *
     * @return boolean
     */
    @Override
    public boolean optimizeForWrite() {
	return false;
    }

    /**
     * Handles exceptions during query execution.
     *
     * @param e Exception to handle.
     * @param resultSender of type ResultSender<Serializable>
     */
    @SuppressWarnings({ "ThrowableInstanceNeverThrown" })
    private void handleException(Throwable e, ResultSender<Serializable> resultSender, String queryString) {
	logger.error("Failed to execute bucket oriented query" + (queryString != null ? ": " + queryString : "."), e);

	resultSender.sendException(new FunctionException(e.getMessage()));
    }

    /**
     * Formats results of query execution.
     *
     * @param selectResults of type SelectResults
     * @return List<Object>
     */
    private List<Object> formatResults(SelectResults<?> selectResults) {
	List<Object> results = new ArrayList<Object>(selectResults.size() + 1);

	results.addAll(selectResults.asList());
	results.add(selectResults.getCollectionType().getElementType());

	return results;
    }
}


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

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

/**
 * Abstract implementation of the ErrorMessageContainer interface.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public class AbstractErrorMessageContainer extends IllegalStateException implements ErrorMessageContainer {

    // Internal state
    private static final long serialVersionUID = 2958878066896490587L;
    private final List<String> errorMessages = new ArrayList<String>();

    /**
     * Adds the provided error message to this ErrorMessageContainer.
     *
     * @param message The message to add, provided that the message is neither null nor empty.
     */
    @Override
    public final void addErrorMessage(final String message) {
        if (message == null || "".equals(message)) {
            return;
        }

        // All done.
        errorMessages.add(message);
    }

    /**
     * Checks if this ErrorMessageContainer contains any errors, and - if so -
     * throws an exception to indicate that fact.
     *
     * @throws IllegalStateException if the internal state of this ErrorMessageContainer implies an
     *                               invalid state.
     */
    @Override
    public final void endExpressionAndValidate() throws IllegalStateException {
        if (!errorMessages.isEmpty()) {
            throw this;
        }
    }

    /**
     * Returns the detail message string of this throwable.
     *
     * @return the detail message string of this <tt>Throwable</tt> instance (which may be <tt>null</tt>).
     */
    @Override
    public String getMessage() {

        final StringBuilder builder = new StringBuilder();
        for (final String current : errorMessages) {
            builder.append(current).append("\n");
        }

        // All done.
        return builder.toString();
    }

    //
    // Private constructors.
    //

    /**
     * Constructs an IllegalStateException with no detail message.
     * A detail message is a String that describes this particular exception.
     */
    protected AbstractErrorMessageContainer() {
    }

    /**
     * Constructs a new exception with the specified cause and a detail
     * message of <tt>(cause==null ? null : cause.toString())</tt> (which
     * typically contains the class and detail message of <tt>cause</tt>).
     * This constructor is useful for exceptions that are little more than
     * wrappers for other throwables (for example, {@link
     * java.security.PrivilegedActionException}).
     *
     * @param cause the cause (which is saved for later retrieval by the
     *              {@link Throwable#getCause()} method).  (A <tt>null</tt> value is
     *              permitted, and indicates that the cause is nonexistent or
     *              unknown.)
     * @since 1.5
     */
    protected AbstractErrorMessageContainer(final Throwable cause) {
        super(cause);
    }
}


/*-
 * #%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.statement.CommonEdgeAttributes;
import se.jguru.nazgul.tools.visualization.model.diagram.statement.CommonGraphAttributes;
import se.jguru.nazgul.tools.visualization.model.diagram.statement.CommonNodeAttributes;
import se.jguru.nazgul.tools.visualization.model.diagram.statement.Edge;
import se.jguru.nazgul.tools.visualization.model.diagram.statement.Identifier;
import se.jguru.nazgul.tools.visualization.model.diagram.statement.Node;
import se.jguru.nazgul.tools.visualization.model.diagram.statement.Statements;
import se.jguru.nazgul.tools.visualization.model.diagram.statement.Subgraph;

import java.util.List;

/**
 *
Utility class which renders statements and comments in a particular order.
 * The default order (which should yield no problematic/circular references) is:

 *

 *
AttributeStatements, which implies zero or all of the types {@link CommonGraphAttributes},
 * {@link CommonEdgeAttributes} and {@link CommonNodeAttributes}

 *
{@link Node} statements

 *
{@link Identifier} statements

 *
{@link Edge} statements

 *
{@link Subgraph} statements

 *

 *
Each section is prefaced by a comment.

 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
public class StatementsRenderer extends AbstractStringRenderer<Statements> {

    // Internal state
    private CommentRenderer commentRenderer;
    private NodeRenderer nodeRenderer;
    private IdentifierRenderer identifierRenderer;
    private EdgeRenderer edgeRenderer;
    private SubgraphRenderer subgraphRenderer;
    private CommonGraphAttributesRenderer commonGraphAttributesRenderer;
    private CommonNodeAttributesRenderer commonNodeAttributesRenderer;
    private CommonEdgeAttributesRenderer commonEdgeAttributesRenderer;

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

        // Delegate
        super(Statements.class);

        // Assign internal state
        this.commentRenderer = new CommentRenderer();
        this.nodeRenderer = new NodeRenderer();
        this.identifierRenderer = new IdentifierRenderer();
        this.edgeRenderer = new EdgeRenderer();
        this.subgraphRenderer = new SubgraphRenderer();
        this.commonEdgeAttributesRenderer = new CommonEdgeAttributesRenderer();
        this.commonGraphAttributesRenderer = new CommonGraphAttributesRenderer();
        this.commonNodeAttributesRenderer = new CommonNodeAttributesRenderer();
    }

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

        final StringBuilder builder = new StringBuilder(config.getIndent());

        // #1) Render the AttributeStatements plus comment, if they exist.
        final boolean hasAttributeStatements = statements.getCommonGraphAttributes() != null
                || statements.getCommonNodeAttributes() != null
                || statements.getCommonEdgeAttributes() != null;

        if (hasAttributeStatements) {

            // Add the comment first.
            builder.append(commentRenderer.render(config, statements.getCommonAttributesComment()));

            // Add the attributes in order
            if (statements.getCommonGraphAttributes() != null) {
                builder.append(commonGraphAttributesRenderer.render(config, statements.getCommonGraphAttributes()));
            }

            if (statements.getCommonNodeAttributes() != null) {
                builder.append(commonNodeAttributesRenderer.render(config, statements.getCommonNodeAttributes()));
            }

            if (statements.getCommonEdgeAttributes() != null) {
                builder.append(commonEdgeAttributesRenderer.render(config, statements.getCommonEdgeAttributes()));
            }
        }

        // #2) Render the other Statements in order, namely
        //
        //     Node statements
        //     Identifier statements
        //     Edge statements
        //     Subgraph statements
        final List<Node> nodes = statements.getNodes();
        if (!nodes.isEmpty()) {

            builder.append(commentRenderer.render(config, statements.getNodesComment()));

            // Render all Nodes in order.
            nodes.forEach(n -> builder.append(nodeRenderer.render(config, n)));
        }

        final List<Identifier> identifiers = statements.getIdentifiers();
        if (!identifiers.isEmpty()) {

            builder.append(commentRenderer.render(config, statements.getIdentifiersComment()));

            // Render all Identifiers in order.
            identifiers.forEach(i -> builder.append(identifierRenderer.render(config, i)));
        }

        final List<Edge> edges = statements.getEdges();
        if (edges != null && !edges.isEmpty()) {

            builder.append(commentRenderer.render(config, statements.getEdgesComment()));

            // Render all Edges in order.
            edges.forEach(i -> builder.append(edgeRenderer.render(config, i)));
        }

        final List<Subgraph> subgraphs = statements.getSubgraphs();
        if (!subgraphs.isEmpty()) {

            builder.append(commentRenderer.render(config, statements.getSubgraphComment()));

            // Render all Subgraphs in order.
            subgraphs.forEach(s -> builder.append(subgraphRenderer.render(config, s)));
        }

        // All Done.
        return builder.toString();
    }
}


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

import com.googlecode.icegem.serialization.codegen.XProperty;
import com.googlecode.icegem.serialization.codegen.impl.ToDataProcessor;

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

/**
 * Seserialize field of type concrete successor of Enum but not Enum.
 *
 * @author igolovach
 */

public class ToDataFieldEnumProcessor implements ToDataProcessor { //todo: you can pack enum very well (only ordinal, not class name)
    //todo: need write null/notnull flag if we use DataSerializer.writeEnum?
    //todo: how GemFire serialize enum? what if it contains mutable fields? enum {int s; getA(){ret s} setB(arg) {s = arg}}
    // todo: warn if enum contains mutable fields?

    public String process(XProperty field) {
        String fieldName = field.getName();
        String getter = "get" + firstLetterToUpperCase(fieldName) + "()";
        return "if (concrete." + getter + " == null) {\n" +
                "    out.writeByte(0);\n" +
                "} else {\n" +
                "    out.writeByte(1);\n" +
                "    out.writeUTF(concrete." + getter + ".getClass().getName());\n" +
                "    com.gemstone.gemfire.DataSerializer.writeEnum(concrete." + getter + ", out);\n" +
                "}\n";
    }
}


/*-
 * #%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.attribute.NodeAttributes;
import se.jguru.nazgul.tools.visualization.model.diagram.statement.Node;

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

    // Internal state
    private AttributeRenderer attributeRenderer;
    private NodeIdRenderer nodeIdRenderer;

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

        // Delegate
        super(Node.class);

        // Assign internal state
        attributeRenderer = new AttributeRenderer();
        nodeIdRenderer = new NodeIdRenderer();
    }

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

        // Do we have a non-empty NodeAttributes within the supplied Node?
        // Don't add the extra newline after the attributes - so call doRender directly.
        final NodeAttributes nodeAttributes = node.getAttributes();
        final String renderedNodeAttributes = attributeRenderer.doRender(config, nodeAttributes);

        // All Done.
        return config.getIndent() + nodeIdRenderer.doRender(config, node.getNodeID())
                + (renderedNodeAttributes.isEmpty() ? "" : " " + renderedNodeAttributes);
    }
}


/*-
 * #%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 se.jguru.nazgul.tools.visualization.model.diagram.statement.Edge;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
 * Graph statement Renderer, complying to the specification in the
 * DOT language specification.
 *
 * @author Lennart Jörelid, jGuru Europe AB
 */
@XmlRootElement(namespace = AbstractIdentifiable.NAMESPACE)
@XmlType(namespace = AbstractIdentifiable.NAMESPACE, propOrder = {"isDigraph", "isStrict"})
@XmlAccessorType(XmlAccessType.FIELD)
public class Graph extends AbstractGraph {

    /**
     * 'true' to make this Graph a directed graph, and 'false' to make it an undirected one.
     */
    @XmlAttribute
    private boolean isDigraph;

    /**
     * 'true' to make this Graph strict.
     */
    @XmlAttribute
    private boolean isStrict;

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

    /**
     * Compound constructor creating a {@link Graph} wrapping the supplied data.
     *
     * @param id        The ID of this Graph. While Graphviz/Dot permits the absence of an ID, this
     *                  builder requires one - and hence the ID can neither be null nor empty.
     * @param isDigraph {@code true} to make this Graph a directed graph, and false to make it an undirected one.
     * @param isStrict  {@code true} to make this Graph strict.
     */
    public Graph(final String id, final boolean isDigraph, final boolean isStrict) {

        // Delegate
        super(id);

        // Assign internal state
        this.isDigraph = isDigraph;
        this.isStrict = isStrict;
    }

    /**
     * Indicates if this Graph is directed ("digraph") or not ("graph").
     *
     * @return {@code true} if this Graph is directed ("digraph").
     */
    public boolean isDigraph() {
        return isDigraph;
    }

    /**
     * Indicates if this Graph is defined as strict or not.
     *
     * @return {@code true} if this Graph is strict.
     */
    public boolean isStrict() {
        return isStrict;
    }

    /**
     * Convenience method to add and return an Edge between a Node/Subgraph and another Node/Subgraph within the
     * immediate Statements in this Graph.
     *
     * @param fromId The non-empty ID of the Node/Subgraph from which the Edge should originate.
     * @param toId   The non-empty ID of the Node/Subgraph to which the Edge should be directed.
     * @return The newly created Edge - or {@code null} if the Edge could not be created.
     */
    public Edge addEdge(final String fromId, final String toId) {
        return getStatements().addEdge(fromId, toId, this);
    }
}


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

import java.io.IOException;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import com.googlecode.icegem.cacheutils.monitor.controller.model.Node;
import com.googlecode.icegem.serialization.AutoSerializable;
import com.googlecode.icegem.serialization.HierarchyRegistry;

/**
 * The different utility operations related to the monitoring tool and its
 * components functionality.
 */
public class Utils {
    /** */
    private static final SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

    /**
     * Gets the string representation of the node's socket in format host:port.
     *
     * @param node
     *            - the node.
     * @return - the string representation of the node's socket in format
     *         host:port.
     */
    public static String toKey(Node node) {
	return toKey(node.getHost(), node.getPort());
    }

    /**
     * Gets the string representation of the host and port in format host:port.
     *
     * @param host
     *            - the host.
     * @param port
     *            - the port.
     * @return - the string representation of the host and port in format
     *         host:port.
     */
    public static String toKey(String host, int port) {
	return host + ":" + port;
    }

    /**
     * Checks if the socket of specified host and port is alive.
     *
     * @param host
     *            - the host.
     * @param port
     *            - the port.
     * @return - true if alive, false otherwise.
     */
    public static boolean isSocketAlive(String host, int port) {
	boolean socketAlive = false;
	Socket socket = null;

	try {
	    socket = new Socket(host, port);

	    socketAlive = socket.isConnected();
	} catch (Throwable t) {
	    // do nothing
	} finally {
	    if (socket != null) {
		try {
		    socket.close();
		} catch (IOException e) {
		    // do nothing
		}
	    }
	}

	return socketAlive;
    }

    /**
     * Executes the thread with specified timeout.
     *
     * @param thread
     *            - the thread to execute.
     * @param timeout
     *            - the timeout.
     */
    public static void execute(Thread thread, long timeout) {
	thread.start();

	try {
	    thread.join(timeout);
	} catch (InterruptedException e) {
	    // Should not be interrupted normally.
	}

	if (thread.isAlive()) {
	    thread.interrupt();
	}
    }

    /**
     * @param runnable - Runnable job to execute in a separate thread.
     * @param timeout - Maximum timeout to execute a new thread.
     */
    public static void execute(Runnable runnable, long timeout) {
	execute(new Thread(runnable), timeout);
    }

    /**
     * Formats date to string.
     *
     * @param date
     *            - the date.
     * @return - the string representation of the Date object.
     */
    public static String dateToString(Date date) {
	return formatter.format(date);
    }

    /**
     * Formats millisecond time to string.
     *
     * @param date
     *            - the date in milliseconds.
     * @return - the string representation of the date in milliseconds.
     */
    public static String dateToString(long date) {
	return dateToString(new Date(date));
    }

    /**
     * Formats the current date.
     *
     * @return - the string representation of the current date.
     */
    public static String currentDate() {
	return dateToString(System.currentTimeMillis());
    }

    public static void exitWithSuccess() {
	System.exit(0);
    }

    public static void exitWithFailure(String message, Throwable t) {
	if (message != null) {
	    System.err.println(message);
	}

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

	System.exit(1);
    }

    public static void exitWithFailure(String message) {
	exitWithFailure(message, null);
    }

    public static void exitWithFailure() {
	exitWithFailure(null);
    }

    public static void registerClasses(List<String> packages) throws Exception {
	if ((packages != null) && (packages.size() > 0)) {
	    List<Class<?>> classesFromPackages = new ArrayList<Class<?>>();

	    for (String pack : packages) {
		ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(
			false);

		provider.addIncludeFilter(new AnnotationTypeFilter(AutoSerializable.class));

		Set<BeanDefinition> candidateComponents = provider.findCandidateComponents(pack);

		for (BeanDefinition beanDefinition : candidateComponents) {
		    String className = beanDefinition.getBeanClassName();

		    final Class<?> clazz = Class.forName(className);

		    classesFromPackages.add(clazz);
		}
	    }

	    HierarchyRegistry.registerAll(Thread.currentThread().getContextClassLoader(), classesFromPackages);
	}
    }
}