<commit_before>/*
 * Copyright (C) 2012 <NAME> <http://www.gedge.ca>
 *
 * This file is part of the OpGraph project.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ca.gedge.opgraph.app.util;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JComponent;

import ca.gedge.opgraph.InputField;
import ca.gedge.opgraph.OpGraph;
import ca.gedge.opgraph.OpLink;
import ca.gedge.opgraph.OpNode;
import ca.gedge.opgraph.OutputField;
import ca.gedge.opgraph.app.extensions.NodeSettings;
import ca.gedge.opgraph.app.extensions.Note;
import ca.gedge.opgraph.app.extensions.Notes;
import ca.gedge.opgraph.dag.CycleDetectedException;
import ca.gedge.opgraph.dag.VertexNotFoundException;
import ca.gedge.opgraph.exceptions.ItemMissingException;
import ca.gedge.opgraph.extensions.CompositeNode;
import ca.gedge.opgraph.extensions.NodeMetadata;
import ca.gedge.opgraph.extensions.Publishable;
import ca.gedge.opgraph.extensions.Publishable.PublishedInput;
import ca.gedge.opgraph.extensions.Publishable.PublishedOutput;
import ca.gedge.opgraph.io.OpGraphSerializer;
import ca.gedge.opgraph.io.OpGraphSerializerFactory;

/**
 * Helper methods for graphs.
 * 
 * TODO Need to generalize cloning of extensions. This would be best done through implementing
 *      Cloneable and Object#clone() for everything, and throwing up a warning dialog whenever
 *      an extension is encountered which doesn't implement Cloneable.
 */
public class GraphUtils {
	/** Logger */
	private final static Logger LOGGER = Logger.getLogger(GraphUtils.class.getName());
	
	/**
	 * Gets the bounding rectangle of a given set of nodes.
	 * 
	 * @return the bounding rectangle of the given collection of nodes
	 */
	public static Rectangle getBoundingRect(Collection<OpNode> nodes) {
		int xmin = Integer.MAX_VALUE;
		int xmax = Integer.MIN_VALUE;
		int ymin = Integer.MAX_VALUE;
		int ymax = Integer.MIN_VALUE;

		for(OpNode node : nodes) {
			final JComponent comp = node.getExtension(JComponent.class);
			if(comp != null) {
				final Dimension pref = comp.getPreferredSize();
				xmin = Math.min(xmin, comp.getX());
				xmax = Math.max(xmax, comp.getX() + pref.width);
				ymin = Math.min(ymin, comp.getY());
				ymax = Math.max(ymax, comp.getY() + pref.height);
			}
		}

		return new Rectangle(xmin, ymin, xmax-xmin, ymax-ymin);
	}

	/**
	 * Clone a node along with {@link NodeSettings}, {@link NodeMetadata},
	 * {@link CompositeNode}, and {@link Publishable} extensions cloned.
	 * 
	 * @param node  the node to clone
	 * 
	 * @return the cloned node, or <code>null</code> if the node could not be cloned
	 * 
	 * @throws NullPointerException  if node is <code>null</code>
	 */
	public static OpNode cloneNode(OpNode node) {
		if(node == null)
			throw new NullPointerException();
		
		final Class<? extends OpNode> nodeClass = node.getClass();
		
		final OpGraphSerializer serializer = 
				OpGraphSerializerFactory.getDefaultSerializer();
		
		// using the serializer ensure we clone all data necessary
		// to rebuild the object
		if(serializer !=  null) {
			try {
				final OpGraph tempGraph = new OpGraph();
				tempGraph.setId("root");
				tempGraph.add(node);
				
				final ByteArrayOutputStream bout = new ByteArrayOutputStream();
				serializer.write(tempGraph, bout);
				
				final ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
				final OpGraph inGraph = serializer.read(bin);
				
				final OpNode clonedNode = inGraph.getVertices().get(0);
				clonedNode.setId(Long.toHexString(UUID.randomUUID().getMostSignificantBits()));
				return clonedNode;
			} catch (IOException e) {
				LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
			}
		} else {
			try {
				final OpNode newNode = nodeClass.newInstance();
				newNode.setName(node.getName());
	
				// copy node settings (if available)
				final NodeSettings nodeSettings = node.getExtension(NodeSettings.class);
				final NodeSettings newNodeSettings = newNode.getExtension(NodeSettings.class);
				if(nodeSettings != null && newNodeSettings != null) {
					newNodeSettings.loadSettings(nodeSettings.getSettings());
				}
	
				// copy meta data (if available)
				final NodeMetadata metaData = node.getExtension(NodeMetadata.class);
				if(metaData != null) {
					final NodeMetadata newMetaData = new NodeMetadata(metaData.getX(), metaData.getY());
					newNode.putExtension(NodeMetadata.class, newMetaData);<commit_msg>New utility method for modifying all node ids for a graph<commit_after>/*
 * Copyright (C) 2012 <NAME> <http://www.gedge.ca>
 *
 * This file is part of the OpGraph project.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ca.gedge.opgraph.app.util;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JComponent;

import ca.gedge.opgraph.InputField;
import ca.gedge.opgraph.OpGraph;
import ca.gedge.opgraph.OpLink;
import ca.gedge.opgraph.OpNode;
import ca.gedge.opgraph.OutputField;
import ca.gedge.opgraph.app.extensions.NodeSettings;
import ca.gedge.opgraph.app.extensions.Note;
import ca.gedge.opgraph.app.extensions.Notes;
import ca.gedge.opgraph.dag.CycleDetectedException;
import ca.gedge.opgraph.dag.VertexNotFoundException;
import ca.gedge.opgraph.exceptions.ItemMissingException;
import ca.gedge.opgraph.extensions.CompositeNode;
import ca.gedge.opgraph.extensions.NodeMetadata;
import ca.gedge.opgraph.extensions.Publishable;
import ca.gedge.opgraph.extensions.Publishable.PublishedInput;
import ca.gedge.opgraph.extensions.Publishable.PublishedOutput;
import ca.gedge.opgraph.io.OpGraphSerializer;
import ca.gedge.opgraph.io.OpGraphSerializerFactory;

/**
 * Helper methods for graphs.
 * 
 * TODO Need to generalize cloning of extensions. This would be best done through implementing
 *      Cloneable and Object#clone() for everything, and throwing up a warning dialog whenever
 *      an extension is encountered which doesn't implement Cloneable.
 */
public class GraphUtils {
	/** Logger */
	private final static Logger LOGGER = Logger.getLogger(GraphUtils.class.getName());
	
	/**
	 * Gets the bounding rectangle of a given set of nodes.
	 * 
	 * @return the bounding rectangle of the given collection of nodes
	 */
	public static Rectangle getBoundingRect(Collection<OpNode> nodes) {
		int xmin = Integer.MAX_VALUE;
		int xmax = Integer.MIN_VALUE;
		int ymin = Integer.MAX_VALUE;
		int ymax = Integer.MIN_VALUE;

		for(OpNode node : nodes) {
			final JComponent comp = node.getExtension(JComponent.class);
			if(comp != null) {
				final Dimension pref = comp.getPreferredSize();
				xmin = Math.min(xmin, comp.getX());
				xmax = Math.max(xmax, comp.getX() + pref.width);
				ymin = Math.min(ymin, comp.getY());
				ymax = Math.max(ymax, comp.getY() + pref.height);
			}
		}

		return new Rectangle(xmin, ymin, xmax-xmin, ymax-ymin);
	}
	
	/**
	 * Change id for all nodes in a graph.  This method is recursive
	 * and will traverse nodes with the CompositeNode extension.
	 * 
	 * @param graph
	 */
	public static void changeNodeIds(OpGraph graph) {
		for(OpNode node:graph) {
			final String newId = Long.toHexString(UUID.randomUUID().getMostSignificantBits());
			node.setId(newId);
			
			final CompositeNode cmpNode = node.getExtension(CompositeNode.class);
			if(cmpNode != null) {
				final OpGraph subGraph = cmpNode.getGraph();
				changeNodeIds(subGraph);
			}
		}
	}

	/**
	 * Clone a node along with {@link NodeSettings}, {@link NodeMetadata},
	 * {@link CompositeNode}, and {@link Publishable} extensions cloned. 
	 * The new node will have a new unique id.
	 * 
	 * @param node  the node to clone
	 * 
	 * @return the cloned node, or <code>null</code> if the node could not be cloned
	 * 
	 * @throws NullPointerException  if node is <code>null</code>
	 */
	public static OpNode cloneNode(OpNode node) {
		if(node == null)
			throw new NullPointerException();
		
		final Class<? extends OpNode> nodeClass = node.getClass();
		
		final OpGraphSerializer serializer = 
				OpGraphSerializerFactory.getDefaultSerializer();
		
		// using the serializer ensure we clone all data necessary
		// to rebuild the object
		if(serializer !=  null) {
			try {
				final OpGraph tempGraph = new OpGraph();
				tempGraph.setId("root");
				tempGraph.add(node);
				
				final ByteArrayOutputStream bout = new ByteArrayOutputStream();
				serializer.write(tempGraph, bout);
				
				final ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
				final OpGraph inGraph = serializer.read(bin);
				changeNodeIds(inGraph);
				
				final OpNode clonedNode = inGraph.getVertices().get(0);
				return clonedNode;
			} catch (IOException e) {
				LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
			}
		} else {
			try {
				final OpNode newNode = nodeClass.newInstance();
				newNode.setName(node.getName());
	
				// copy node settings (if available)
				final NodeSettings nodeSettings = node.getExtension(NodeSettings.class);
				final NodeSettings newNodeSettings = newNode.getExtension(NodeSettings.class);
				if(nodeSettings != null && newNodeSettings != null) {
					newNodeSettings.loadSettings(nodeSettings.getSettings());
				}
	
				// copy meta data (if available)
				final NodeMetadata metaData = node.getExtension(NodeMetadata.class);
				if(metaData != null) {
					final NodeMetadata newMetaData = new NodeMetadata(metaData.getX(), metaData.getY());
					newNode.putExtension(NodeMetadata.class, newMetaData);
{'fraction_non_alphanumeric': 0.10290708043517033, 'fraction_numerical': 0.001070090957731407, 'mean_word_length': 4.054078413699865, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6034334', 'n_tokens_mistral': 3687, 'n_tokens_neox': 3294, 'n_words': 967}