public class CombinatorialExpansionUtil {

	public static SBOLDocument createCombinatorialDesign(java.awt.Component panel, SBOLDocument doc) throws SBOLValidationException, FileNotFoundException {
		CombinatorialDerivation derivation = CombinatorialDerivationInputDialog.pickCombinatorialDerivation(panel, doc, null);
		if (derivation == null) {
			JOptionPane.showMessageDialog(null, "There are no combinatorial designs");
			return null;
		}

		HashSet<ComponentDefinition> enumeration = enumerate(doc, derivation);
		if (enumeration.isEmpty()) {
			JOptionPane.showMessageDialog(null, "There are no variants to enumerate");
			return null;
		}

		if (!derivation.isSetStrategy()) {
			int choice = JOptionPane.showOptionDialog(null,
					"The strategy property is not set.  Would you like to enumerate or sample?",
					"Combinatorial Design Strategy", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null,
					StrategyType.values(), StrategyType.values()[0]);
			if (choice == JOptionPane.CLOSED_OPTION) {
				return null;
			}

			derivation.setStrategy(StrategyType.values()[choice]);
		}

		int tocsv = JOptionPane.showConfirmDialog(null, 
				"Would you like to export the enumerated design to a .csv file?", 
				"Export to CSV", JOptionPane.YES_NO_OPTION);
        if (tocsv == JOptionPane.YES_OPTION) {
        	JFileChooser chooser = new JFileChooser();
            chooser.setDialogTitle("choosertitle");
            chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            chooser.setAcceptAllFileFilterUsed(false);
            int result = chooser.showSaveDialog(null);
            if (result == chooser.APPROVE_OPTION)
            {
            	File f = chooser.getSelectedFile();
            	if (FilenameUtils.getExtension(f.getName()).equalsIgnoreCase("csv")) {
            	    // filename is OK as-is
            	} else {
            	    f = new File(f.toString() + ".csv");  // append .xml if "foo.jpg.xml" is OK
            	}
            	PrintWriter writer = new PrintWriter(f);
            	String s;
	        	for(ComponentDefinition cd: enumeration)
	    		{
	        		s = "";
	    			for(Component comp: cd.getSortedComponents())
	    			{
	    				s += comp.getDefinition().getDisplayId() + ", ";
	    			}
	    			writer.println(s);
	    		}
	        	writer.close();
            }
        }
		SBOLDocument generated = new SBOLDocument();
		generated.setDefaultURIprefix(SBOLEditorPreferences.INSTANCE.getUserInfo().getURI().toString());

		if (derivation.getStrategy() == StrategyType.SAMPLE) {
			ComponentDefinition[] a = enumeration.toArray(new ComponentDefinition[0]);
			ComponentDefinition sample = a[ThreadLocalRandom.current().nextInt(a.length)];
			ProvenanceUtil.createProvenance(doc, sample, derivation);
			doc.createRecursiveCopy(generated, sample);
		} else if (derivation.getStrategy() == StrategyType.ENUMERATE) {
			ProvenanceUtil.createProvenance(doc, enumeration.iterator().next(), derivation);
			for (ComponentDefinition CD : enumeration) {
				doc.createRecursiveCopy(generated, CD);
			}
		} else {
			throw new IllegalArgumentException();
		}

		return generated;
	}

	private static ComponentDefinition createTemplateCopy(SBOLDocument doc, CombinatorialDerivation derivation)
			throws SBOLValidationException {
		ComponentDefinition template = derivation.getTemplate();

		String uniqueId = SBOLUtils.getUniqueDisplayId(null, null, template.getDisplayId() + "_GeneratedInstance",
				template.getVersion(), "CD", doc);
		//ComponentDefinition copy = (ComponentDefinition) doc.createCopy(template, uniqueId, template.getVersion());
		ComponentDefinition copy = doc.createComponentDefinition(uniqueId, template.getVersion(), template.getTypes());
		copy.setRoles(template.getRoles());
		Component prev = null;
		Component curr;
		for(Component c : template.getSortedComponents())
		{
			curr = copy.createComponent(c.getDisplayId(), c.getAccess(), c.getDefinitionURI());
			if(prev != null)
			{
				uniqueId = SBOLUtils.getUniqueDisplayId(copy, null,
						copy.getDisplayId() + "_SequenceConstraint", null, "SequenceConstraint", null);
				copy.createSequenceConstraint(uniqueId, RestrictionType.PRECEDES, prev.getIdentity(),
						curr.getIdentity());
			}
			prev = curr;
		}
		copy.addWasDerivedFrom(template.getIdentity());
		copy.addWasDerivedFrom(derivation.getIdentity());
		for (Component component : copy.getComponents()) {
			component.addWasDerivedFrom(template.getComponent(component.getDisplayId()).getIdentity());
		}

		//copy.clearSequenceAnnotations();

		return copy;
	}

	private static HashSet<ComponentDefinition> enumerate(SBOLDocument doc, CombinatorialDerivation derivation)
			throws SBOLValidationException {
		HashSet<ComponentDefinition> parents = new HashSet<>();
		parents.add(createTemplateCopy(doc, derivation));
		for (VariableComponent vc : derivation.getVariableComponents()) {
			HashSet<ComponentDefinition> newParents = new HashSet<>();
			for (ComponentDefinition parent : parents) {
				for (HashSet<ComponentDefinition> children : group(collectVariants(doc, vc), vc.getOperator())) {
					
					// create copy of parent
					String uniqueId = SBOLUtils.getUniqueDisplayId(null, null, parent.getDisplayId(),
							parent.getVersion(), "CD", doc);
					ComponentDefinition newParent = (ComponentDefinition) doc.createCopy(parent, uniqueId, "1");

					// add children
					ComponentDefinition template = derivation.getTemplate();
					addChildren(template, template.getComponent(vc.getVariableURI()), newParent, children);

					// add to newParents
					newParents.add(newParent);
				}
			}

			parents = newParents;
		}

		return parents;
	}
	

	private static void addChildren(ComponentDefinition originalTemplate, Component originalComponent,
			ComponentDefinition newParent, HashSet<ComponentDefinition> children) throws SBOLValidationException {
		Component newComponent = newParent.getComponent(originalComponent.getDisplayId());
		newComponent.addWasDerivedFrom(originalComponent.getIdentity());

		if (children.isEmpty()) {
			removeConstraintReferences(newParent, newComponent);
			for (SequenceAnnotation sa : newParent.getSequenceAnnotations()) {
				if (sa.isSetComponent() && sa.getComponentURI().equals(newComponent.getIdentity())) {
					newParent.removeSequenceAnnotation(sa);
				}
			}
			newParent.removeComponent(newComponent);
			return;
		}

		boolean first = true;
		for (ComponentDefinition child : children) {
			if (first) {
				// take over the definition of newParent's version of the
				// original component
				newComponent.setDefinition(child.getIdentity());
				first = false;
			} else {
				// create a new component
				String uniqueId = SBOLUtils.getUniqueDisplayId(newParent, null, child.getDisplayId() + "_Component",
						"1", "Component", null);
				Component link = newParent.createComponent(uniqueId, AccessType.PUBLIC, child.getIdentity());
				link.addWasDerivedFrom(originalComponent.getIdentity());

				// create a new 'prev precedes link' constraint
				Component oldPrev = getBeforeComponent(originalTemplate, originalComponent);
				if (oldPrev != null) {
					Component newPrev = newParent.getComponent(oldPrev.getDisplayId());
					if (newPrev != null) {
						uniqueId = SBOLUtils.getUniqueDisplayId(newParent, null,
								newParent.getDisplayId() + "_SequenceConstraint", null, "SequenceConstraint", null);
						newParent.createSequenceConstraint(uniqueId, RestrictionType.PRECEDES, newPrev.getIdentity(),
								link.getIdentity());
					}
				}

				// create a new 'link precedes next' constraint
				Component oldNext = getAfterComponent(originalTemplate, originalComponent);
				if (oldNext != null) {
					Component newNext = newParent.getComponent(oldNext.getDisplayId());
					if (newNext != null) {
						uniqueId = SBOLUtils.getUniqueDisplayId(newParent, null,
								newParent.getDisplayId() + "_SequenceConstraint", null, "SequenceConstraint", null);
						newParent.createSequenceConstraint(uniqueId, RestrictionType.PRECEDES, link.getIdentity(),
								newNext.getIdentity());
					}
				}
			}
		}
	}

	private static void removeConstraintReferences(ComponentDefinition newParent, Component newComponent) throws SBOLValidationException {
		Component subject = null;
		Component object = null;
		for (SequenceConstraint sc : newParent.getSequenceConstraints()) {
			if (sc.getSubject().equals(newComponent)) {
				object = sc.getObject();
				//If we know what the new subject of this sequence constraint should be, modify it
				if(subject != null) {
					sc.setSubject(subject.getIdentity());
					object = null;
					subject = null;
				}else {//else remove it
					newParent.removeSequenceConstraint(sc);
				}
			}
			if(sc.getObject().equals(newComponent)){
				subject = sc.getSubject();
				//If we know what the new object of this sequence constraint should be, modify it
				if(object != null) {
					sc.setObject(object.getIdentity());
					object = null;
					subject = null;
				}else {//else remove it
					newParent.removeSequenceConstraint(sc);
				}
			}
		}
	}

	private static Component getBeforeComponent(ComponentDefinition template, Component component) {
		for (SequenceConstraint sc : template.getSequenceConstraints()) {
			if (sc.getRestriction().equals(RestrictionType.PRECEDES) && sc.getObject().equals(component)) {
				return sc.getSubject();
			}
		}
		return null;
	}

	private static Component getAfterComponent(ComponentDefinition template, Component component) {
		for (SequenceConstraint sc : template.getSequenceConstraints()) {
			if (sc.getRestriction().equals(RestrictionType.PRECEDES) && sc.getSubject().equals(component)) {
				return sc.getObject();
			}
		}
		return null;
	}

	private static HashSet<HashSet<ComponentDefinition>> group(HashSet<ComponentDefinition> variants,
			OperatorType operator) {
		HashSet<HashSet<ComponentDefinition>> groups = new HashSet<>();

		for (ComponentDefinition CD : variants) {
			HashSet<ComponentDefinition> group = new HashSet<>();
			group.add(CD);
			groups.add(group);
		}

		if (operator == OperatorType.ONE) {
			return groups;
		}

		if (operator == OperatorType.ZEROORONE) {
			groups.add(new HashSet<>());
			return groups;
		}

		groups.clear();
		generateCombinations(groups, variants.toArray(new ComponentDefinition[0]), 0, new HashSet<>());
		if (operator == OperatorType.ONEORMORE) {
			return groups;
		}

		if (operator == OperatorType.ZEROORMORE) {
			groups.add(new HashSet<>());
			return groups;
		}

		throw new IllegalArgumentException(operator.toString() + " operator not supported");
	}

	/**
	 * Generates all combinations except the empty set.
	 */
	private static void generateCombinations(HashSet<HashSet<ComponentDefinition>> groups,
			ComponentDefinition[] variants, int i, HashSet<ComponentDefinition> set) {
		if (i == variants.length) {
			if (!set.isEmpty()) {
				groups.add(set);
			}
			return;
		}

		HashSet<ComponentDefinition> no = new HashSet<>(set);
		generateCombinations(groups, variants, i + 1, no);

		HashSet<ComponentDefinition> yes = new HashSet<>(set);
		yes.add(variants[i]);
		generateCombinations(groups, variants, i + 1, yes);
	}

	private static HashSet<ComponentDefinition> collectVariants(SBOLDocument doc, VariableComponent vc)
			throws SBOLValidationException {
		HashSet<ComponentDefinition> variants = new HashSet<>();

		//Recursively collect variants from possible nested VariantDerivations 
//		for(CombinatorialDerivation cd : vc.getVariantDerivations())
//		{
//			for (VariableComponent v : cd.getVariableComponents()) {
//				variants.addAll(collectVariants(doc, v));
//				
//			}
//		}
		// add all variants
		variants.addAll(vc.getVariants());

		// add all variants from variantCollections
		for (Collection c : vc.getVariantCollections()) {
			for (TopLevel tl : c.getMembers()) {
				if (tl instanceof ComponentDefinition) {
					variants.add((ComponentDefinition) tl);
				}
			}
		}

		// add all variants from variantDerivations
		for (CombinatorialDerivation derivation : vc.getVariantDerivations()) {
			variants.addAll(enumerate(doc, derivation));
		}

		return variants;
	}
}
 public class ProvenanceUtil {
	/**
	 * Adds an SBOLDesignerActivity -> SBOLDesignerAgent to the wasDerivedFrom
	 * of every TopLevel that doesn't have an Activity. If an
	 * SBOLDesignerActivity already exists on the root, updates the end time.
	 */
	public static void createProvenance(SBOLDocument doc, ComponentDefinition root) throws SBOLValidationException {
		createProvenance(doc, root, null);
	}

	private static final URI SEQUENCE_EDITOR = URI.create("http://sbols.org/v2#sequenceEditor");

	/**
	 * Same as others, except usage will get added as a usage of the Activity.
	 */
	public static void createProvenance(SBOLDocument doc, ComponentDefinition root, Identified usage)
			throws SBOLValidationException {
		// Create or get the activity
		String activityId = root.getDisplayId() + "_SBOLDesignerActivity";
		Activity activity = null;
		for (Activity a : doc.getActivities()) {
			if (root.getWasGeneratedBys().contains(a.getIdentity()) && a.getDisplayId().equals(activityId)) {
				activity = a;
				break;
			}
		}

		if (activity == null) {
			activity = doc.createActivity(activityId, "1");
		}

		// Set the ended at time
		activity.setEndedAtTime(DateTime.now());

		// Set the usage
		if (usage != null) {
			String usageId = usage.getDisplayId() + "_Usage";
			if (activity.getUsage(usageId) == null) {
				Usage used = activity.createUsage(usageId, usage.getIdentity());
				used.addRole(SEQUENCE_EDITOR);
			}
		}

		// Set the creator
		String creator = SBOLEditorPreferences.INSTANCE.getUserInfo().getName();
		boolean hasCreator = false;
		for (Annotation a : activity.getAnnotations()) {
			if (a.getQName().getLocalPart().equals("creator") && a.isStringValue()
					&& a.getStringValue().equals(creator)) {
				hasCreator = true;
				break;
			}
		}
		if (!hasCreator) {
			activity.createAnnotation(new QName("http://purl.org/dc/elements/1.1/", "creator", "dc"), creator);
		}

		// Create the qualifiedAssociation
		URI designerURI = URI.create("https://synbiohub.org/public/SBOL_Software/SBOLDesigner/3.1");
		String designerPrefix = "https://synbiohub.org/public/SBOL_Software/SBOLDesigner/";
		boolean hasAssociation = false;
		for (Association a : activity.getAssociations()) {
			if (a.getAgentURI() != null && a.getAgentURI().toString().startsWith(designerPrefix)) {
				hasAssociation = true;
				break;
			}
		}
		if (!hasAssociation) {
			Association association = activity.createAssociation("Association", designerURI);
			association.addRole(SEQUENCE_EDITOR);
		}
		createSBOLDesignerAgent(doc);
		// Link with all TopLevels
		for (TopLevel tl : doc.getTopLevels()) {
			// check if in namespace
			if (SBOLUtils.notInNamespace(tl) || tl instanceof Activity || tl instanceof CombinatorialDerivation) {
				continue;
			}

			boolean hasActivity = false;

			// Check if hasActivity
			for (URI uri : tl.getWasGeneratedBys()) {
				TopLevel generatedBy = doc.getTopLevel(uri);
				if (generatedBy != null && generatedBy.getDisplayId().equals(activity.getDisplayId())
						&& generatedBy instanceof Activity) {
					hasActivity = true;
				}
			}

			// Attach if there is no existing Activity
			if (!hasActivity) {
				tl.addWasGeneratedBy(activity.getIdentity());
			}
		}
	}

	/*
	 * The reference implementation for generating the SBOLDesigner Agent.
	 */
	private static Agent createSBOLDesignerAgent(SBOLDocument design) throws SBOLValidationException {
		Agent designerAgent = design.getAgent(URI.create("https://synbiohub.org/public/SBOL_Software/SBOLDesigner/3.1"));

		if (designerAgent == null) {
			designerAgent = design.createAgent("https://synbiohub.org/public/SBOL_Software", "SBOLDesigner", "3.1");
			designerAgent.setName("SBOLDesigner CAD Tool");
			designerAgent.setDescription(
					"SBOLDesigner is a simple, biologist-friendly CAD software tool for creating and "
					+ "manipulating the sequences of genetic constructs using the Synthetic Biology Open Language (SBOL) 2 data model. "
					+ "Throughout the design process, SBOL Visual symbols, a system of schematic glyphs, provide standardized visualizations "
					+ "of individual parts. SBOLDesigner completes a workflow for users of genetic design automation tools. It combines a "
					+ "simple user interface with the power of the SBOL standard and serves as a launchpad for more detailed designs involving "
					+ "simulations and experiments. Some new features in SBOLDesigner are the ability to add variant collections to combinatorial derivations"
					+ ", enumerating those collections, and the ability to view sequence features hierarchically. There are also some small changes to the way"
					+ " that preferences work in regards to saving a design with incomplete sequences.");
			designerAgent.createAnnotation(new QName("http://purl.org/dc/elements/1.1/", "creator", "dc"),
					"Samuel Bridge");
			designerAgent.createAnnotation(new QName("http://purl.org/dc/elements/1.1/", "creator", "dc"),
					"Michael Zhang");
			designerAgent.createAnnotation(new QName("http://purl.org/dc/elements/1.1/", "creator", "dc"),
					"Chris Myers");
			designerAgent.createAnnotation(new QName("http://purl.org/dc/elements/1.1/", "creator", "dc"),
					"Michal Galdzicki");
			designerAgent.createAnnotation(new QName("http://purl.org/dc/elements/1.1/", "creator", "dc"),
					"Bryan Bartley");
			designerAgent.createAnnotation(new QName("http://purl.org/dc/elements/1.1/", "creator", "dc"),
					"Sean Sleight");
			designerAgent.createAnnotation(new QName("http://purl.org/dc/elements/1.1/", "creator", "dc"),
					"Evren Sirin");
			designerAgent.createAnnotation(new QName("http://purl.org/dc/elements/1.1/", "creator", "dc"),
					"John Gennari");
		}

		return designerAgent;
	}
	
	public static void main(String[] args) throws SBOLValidationException, IOException, SBOLConversionException {
		SBOLDocument doc = new SBOLDocument();
		createSBOLDesignerAgent(doc);
		String home = System.getProperty("user.home");
		SBOLWriter.write(doc, new File(home+"/Downloads/provenanceutil.txt"));
	}
}
 public class SBOLUtils {
	/**
	 * Returns an int which guarantees a unique URI. Pass in the parent CD (if
	 * dataType isn't a TopLevel), the displayId you want, the version (if
	 * dataType is a TopLevel), the type of object, and the SBOLDocument
	 * containing the design.
	 * 
	 * @throws SBOLValidationException
	 */
	public static String getUniqueDisplayId(ComponentDefinition comp, CombinatorialDerivation derivation,
			String displayId, String version, String dataType, SBOLDocument design) throws SBOLValidationException {
		// if can get using some displayId, then try the next number
		switch (dataType) {
		case "CD":
			for (int i = 1; true; i++) {
				if (i == 1 && design.getComponentDefinition(displayId, version) == null) {
					return displayId;
				}
				if (design.getComponentDefinition(displayId + i, version) == null) {
					return displayId + i;
				}
			}
		case "SequenceAnnotation":
			for (int i = 1; true; i++) {
				if (i == 1 && comp.getSequenceAnnotation(displayId) == null) {
					return displayId;
				}
				if (comp.getSequenceAnnotation(displayId + i) == null) {
					return displayId + i;
				}
			}
		case "SequenceConstraint":
			for (int i = 1; true; i++) {
				if (i == 1 && comp.getSequenceConstraint(displayId) == null) {
					return displayId;
				}
				if (comp.getSequenceConstraint(displayId + i) == null) {
					return displayId + i;
				}
			}
		case "Component":
			for (int i = 1; true; i++) {
				if (i == 1 && comp.getComponent(displayId) == null) {
					return displayId;
				}
				if (comp.getComponent(displayId + i) == null) {
					return displayId + i;
				}
			}
		case "Sequence":
			for (int i = 1; true; i++) {
				if (i == 1 && design.getSequence(displayId, version) == null) {
					return displayId;
				}
				if (design.getSequence(displayId + i, version) == null) {
					return displayId + i;
				}
			}
		case "Range":
			test: for (int i = 1; true; i++) {
				for (SequenceAnnotation sa : comp.getSequenceAnnotations()) {
					if (i == 1 && sa.getLocation(displayId) != null) {
						continue test;
					}
					if (sa.getLocation(displayId + i) != null) {
						continue test;
					}
				}
				// This will always return Range, Range2, Range3... etc,
				// skipping Range1
				return i == 1 ? displayId : displayId + i;
			}
		case "CombinatorialDerivation":
			for (int i = 1; true; i++) {
				if (i == 1 && design.getCombinatorialDerivation(displayId, version) == null) {
					return displayId;
				}
				if (design.getCombinatorialDerivation(displayId + i, version) == null) {
					return displayId + i;
				}
			}
		case "VariableComponent":
			for (int i = 1; true; i++) {
				if (i == 1 && derivation.getVariableComponent(displayId) == null) {
					return displayId;
				}
				if (derivation.getVariableComponent(displayId + i) == null) {
					return displayId + i;
				}
			}
		default:
			throw new IllegalArgumentException();
		}
	}

	/**
	 * Creates an alphabetized String[] representing SO names of descendant
	 * roles based on the passed in part's role.
	 */
	public static String[] createRefinements(Part part) {
		SequenceOntology so = new SequenceOntology();
		String[] descendantNames;
		if (part.getRole() != null) {
			descendantNames = so.getDescendantNamesOf(part.getRole()).toArray(new String[0]);
			Arrays.sort(descendantNames);
		} else {
			descendantNames = new String[0];
		}
		String[] refine = new String[descendantNames.length + 1];
		refine[0] = "None";
		for (int i = 1; i < descendantNames.length + 1; i++) {
			refine[i] = descendantNames[i - 1];
		}
		return refine;
	}

	/**
	 * Returns a list of all roles of a CD that are descendants of the part's
	 * role.
	 */
	public static List<URI> getRefinementRoles(Identified comp, Part part) {
		ArrayList<URI> list = new ArrayList<URI>();
		SequenceOntology so = new SequenceOntology();
		Set<URI> roles;
		if (comp instanceof ComponentDefinition) {
			roles = ((ComponentDefinition) comp).getRoles();
		} else if (comp instanceof Component) {
			roles = ((Component) comp).getRoles();
		} else if (comp instanceof SequenceAnnotation) {
			roles = ((SequenceAnnotation) comp).getRoles();
		} else {
			return list;
		}
		for (URI r : roles) {
			// assumes the part role is always the first role in the list
			if (so.isDescendantOf(r, part.getRole())) {
				list.add(r);
			}
		}
		return list;
	}

	/**
	 * Prompts the user to choose a file and reads it, returning the output
	 * SBOLDocument. If the user cancels or the file in unable to be imported,
	 * returns null.
	 * 
	 * importPath is different from path
	 */
	public static SBOLDocument importDoc() {
		String path = Preferences.userRoot().node("path").get("importPath", setupFile().getPath());
		//int prefs = SBOLEditorPreferences.INSTANCE.getFileChooserBehavior();
		JFileChooser fc = new JFileChooser(new File(path));
		fc.setMultiSelectionEnabled(false);
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		fc.setAcceptAllFileFilterUsed(true);
		fc.setFileFilter(
				new FileNameExtensionFilter("SBOL file (*.xml, *.rdf, *.sbol), GenBank (*.gb, *.gbk), FASTA (*.fasta)",
						"xml", "rdf", "sbol", "gb", "gbk", "fasta"));

		int returnVal = fc.showOpenDialog(null);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			File directory = fc.getCurrentDirectory();
			Preferences.userRoot().node("path").put("importPath", directory.getPath());
			SBOLDocument doc = null;
			try {
				SBOLReader.setURIPrefix(SBOLEditorPreferences.INSTANCE.getUserInfo().getURI().toString());
				SBOLReader.setCompliant(true);
				doc = SBOLReader.read(fc.getSelectedFile());
			} catch (Exception e1) {
				MessageDialog.showMessage(null, "This file is unable to be imported: ", e1.getMessage());
				e1.printStackTrace();
			}
			return doc;
		}
		return null;
	}

	/**
	 * Prompts the user to choose a file and returns it. Returns null otherwise.
	 * 
	 * importPath is different from path
	 */
	public static File importFile() {
		String path = Preferences.userRoot().node("path").get("importPath", setupFile().getPath());
		JFileChooser fc = new JFileChooser(new File(path));
		fc.setMultiSelectionEnabled(false);
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		fc.setAcceptAllFileFilterUsed(true);
		fc.setFileFilter(
				new FileNameExtensionFilter("SBOL file (*.xml, *.rdf, *.sbol), GenBank (*.gb, *.gbk), FASTA (*.fasta)",
						"xml", "rdf", "sbol", "gb", "gbk", "fasta"));

		int returnVal = fc.showOpenDialog(null);
		if (returnVal == JFileChooser.APPROVE_OPTION) {
			File directory = fc.getCurrentDirectory();
			Preferences.userRoot().node("path").put("importPath", directory.getPath());
			return fc.getSelectedFile();
		}
		return null;
	}

	/**
	 * Gets the path from Preferences and returns a File
	 * 
	 * importPath is different from path
	 */
	public static File setupFile() {
		String path = Preferences.userRoot().node("path").get("path", "");
		return new File(path);
	}

	public static JFileChooser setupFC() {
		JFileChooser fc = new JFileChooser(SBOLUtils.setupFile());
		fc.setMultiSelectionEnabled(false);
		fc.setFileSelectionMode(JFileChooser.FILES_ONLY);
		fc.setAcceptAllFileFilterUsed(true);
		fc.setFileFilter(
				new FileNameExtensionFilter("SBOL file (*.xml, *.rdf, *.sbol), GenBank (*.gb, *.gbk), FASTA (*.fasta)",
						"xml", "rdf", "sbol", "gb", "gbk", "fasta"));

		return fc;
	}

	public static File selectFile(java.awt.Component parent, JFileChooser fc) {
		fc.setSelectedFile(SBOLUtils.setupFile());
		int returnVal = fc.showSaveDialog(parent);

		if (returnVal != JFileChooser.APPROVE_OPTION) {
			return null;
		}

		File file = fc.getSelectedFile();

		Preferences.userRoot().node("path").put("path", file.getPath());

		return file;
	}

	/**
	 * adds all registries in SynBioHubFrontend and local preferences to doc's
	 * internal registries
	 */
	public static void populateRegistries(SBOLDocument doc) {
		// add preferences registries to doc
		Registries.get().forEach(registry -> {
			if (registry.isMetadata()) {
				doc.addRegistry(registry.getLocation(), registry.getUriPrefix());
			}
		});
	}

	private static String getNucleotides(ComponentDefinition comp) {
		// Sequence seq = comp.getSequence();
		Sequence seq = null;
		if (comp.getSequences().size() > 0) {
			seq = comp.getSequenceByEncoding(Sequence.IUPAC_DNA);
		}
		return (seq == null) ? null : seq.getElements();
	}

	/**
	 * Returns whether the user canceled whatever operation to rename
	 * UnamedPart.
	 */
	public static boolean rootCalledUnamedPart(ComponentDefinition root, JPanel panel) {
		// if UnnamedPart still, remind user to rename
		if (root.getDisplayId().equals("UnnamedPart")) {
			int cancel = JOptionPane.showOptionDialog(panel,
					"The root part is still called \"UnnamedPart\".  Would you like to rename this part?", null,
					JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null);
			if (cancel == JOptionPane.YES_OPTION) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Pass in the nucleotides and the SBOLDocument you want to create the
	 * Sequence in.
	 */
	private static Sequence createSequence(String nucleotides, SBOLDocument design) {
		try {
			String uniqueId = SBOLUtils.getUniqueDisplayId(null, null, "Sequence", "1", "Sequence", design);
			return design.createSequence(uniqueId, "1", nucleotides, Sequence.IUPAC_DNA);
		} catch (SBOLValidationException e) {
			e.printStackTrace();
			return null;
		}
	}

	public static boolean notInNamespace(TopLevel tl) {
		URI uri = tl.getIdentity();
		return uri != null
				&& !uri.toString().startsWith(SBOLEditorPreferences.INSTANCE.getUserInfo().getURI().toString());
	}

	/**
	 * Finds the UncoveredSequences of comp using SAs. This is all inside of
	 * design.
	 */
	public static Map<Integer, Sequence> findUncoveredSequences(ComponentDefinition comp,
			List<SequenceAnnotation> annotations, SBOLDocument design) {
		String sequence = SBOLUtils.getNucleotides(comp);
		if (sequence == null) {
			return ImmutableMap.of();
		}

		Map<Integer, Sequence> uncoveredSequences = Maps.newLinkedHashMap();
		int size = annotations.size();
		int location = 1;
		for (int i = 0; i < size; i++) {
			SequenceAnnotation ann = annotations.get(i);

			// Integer start = ann.getBioStart();
			// Integer end = ann.getBioEnd();
			Integer start = null;
			Integer end = null;
			Location loc = ann.getLocations().iterator().next();
			// TODO Only taking into account locations of type Range.
			if (loc instanceof Range) {
				Range range = (Range) loc;
				start = range.getStart();
				end = range.getEnd();
			}

			if (start == null || end == null) {
				return null;
			}

			if (start > location) {
				Sequence seq = SBOLUtils.createSequence(sequence.substring(location - 1, start - 1), design);
				uncoveredSequences.put(-i - 1, seq);
			}

			if (ann.isSetComponent()) {
				if (SBOLUtils.getNucleotides(ann.getComponentDefinition()) == null) {
					Sequence seq = SBOLUtils.createSequence(sequence.substring(start - 1, end), design);
					uncoveredSequences.put(i, seq);
				}
			}

			location = end + 1;
		}

		if (location < sequence.length()) {
			Sequence seq = SBOLUtils.createSequence(sequence.substring(location - 1, sequence.length()), design);
			uncoveredSequences.put(-size - 1, seq);
		}

		return uncoveredSequences;
	}

	public enum Types {
		All_types, DNA, Complex, Effector, Protein, RNA, Small_molecule;
	}

	/**
	 * Turns the type specified in types to a HashSet<URI> of types
	 */
	public static Set<URI> convertTypesToSet(Types types) {
		URI uri;
		switch (types) {
		case All_types:
			return new HashSet<URI>();
		case DNA:
			uri = ComponentDefinition.DNA;
			break;
		case Complex:
			uri = ComponentDefinition.COMPLEX;
			break;
		case Effector:
			uri = ComponentDefinition.EFFECTOR;
			break;
		case Protein:
			uri = ComponentDefinition.PROTEIN;
			break;
		case RNA:
			uri = ComponentDefinition.RNA;
			break;
		case Small_molecule:
			uri = ComponentDefinition.SMALL_MOLECULE;
			break;
		default:
			System.out.println("Invalid type");
			return new HashSet<URI>();
		}
		HashSet<URI> set = new HashSet<URI>();
		set.add(uri);
		return set;
	}

	/**
	 * Returns the Types enum associated with a type URI in types. If none
	 * exist, returns null.
	 */
	public static Types convertURIsToType(Set<URI> types) {
		for (URI type : types) {
			if (type.equals(ComponentDefinition.DNA)) {
				return Types.DNA;
			} else if (type.equals(ComponentDefinition.COMPLEX)) {
				return Types.Complex;
			} else if (type.equals(ComponentDefinition.EFFECTOR)) {
				return Types.Effector;
			} else if (type.equals(ComponentDefinition.PROTEIN)) {
				return Types.Protein;
			} else if (type.equals(ComponentDefinition.RNA)) {
				return Types.RNA;
			} else if (type.equals(ComponentDefinition.SMALL_MOLECULE)) {
				return Types.Small_molecule;
			}
		}
		return null;
	}

	/**
	 * Returns a list with all the CDs in list which contain type (URI).
	 */
	public static List<ComponentDefinition> getCDOfType(List<ComponentDefinition> list, Types type) {
		if (type == Types.All_types) {
			return list;
		}
		URI uri = convertTypesToSet(type).iterator().next();
		List<ComponentDefinition> result = new ArrayList<ComponentDefinition>();
		for (ComponentDefinition CD : list) {
			if (CD.getTypes().contains(uri)) {
				result.add(CD);
			}
		}
		return result;
	}
	
	/**
	 * Returns a list with all the CDs in list which contain type (URI).
	 */
	public static List<TopLevel> getTopLevelOfType(List<TopLevel> list, Types type) {
		if (type == Types.All_types) {
			return list;
		}
		URI uri = convertTypesToSet(type).iterator().next();
		List<TopLevel> result = new ArrayList<TopLevel>();
		for (TopLevel CD : list) {
			if(CD instanceof ComponentDefinition) {
				if (((ComponentDefinition)CD).getTypes().contains(uri)) {
					result.add(CD);
				}
			}
		}
		return result;
	}
	
	public static List<TopLevel> getCDCollectionsAndComboDerv(SBOLDocument doc, Part part)
	{
		List<TopLevel> list = new ArrayList<TopLevel>();
		
		for(org.sbolstandard.core2.Collection col : doc.getCollections())
			list.add(col);
		for(ComponentDefinition cd : getCDOfRole(doc, part))
			list.add(cd);
		for(CombinatorialDerivation comb : doc.getCombinatorialDerivations())
			list.add(comb);
		return list;
	}

	/**
	 * Returns all the CDs in doc with the same role as that of part. If the
	 * part doesn't have any roles, returns all the CDs.
	 */
	public static List<ComponentDefinition> getCDOfRole(SBOLDocument doc, Part part) {
		return getCDOfRole(doc.getComponentDefinitions(), part);
	}

	/**
	 * Returns all the CDs in setCD with the same role as that of part. If the
	 * part doesn't have any roles, returns all the CDs.
	 */
	public static List<ComponentDefinition> getCDOfRole(Set<ComponentDefinition> setCD, Part part) {
		List<ComponentDefinition> list = new ArrayList<ComponentDefinition>();

		if (part == null || part.getRoles() == null || part.getRoles().isEmpty()) {
			// roles don't exist
			for (ComponentDefinition cd : setCD) {
				list.add(cd);
			}
		} else {
			// roles exist
			for (ComponentDefinition cd : setCD) {
				SequenceOntology so = new SequenceOntology();
				for (URI role : cd.getRoles()) {
					if (so.isDescendantOf(role, part.getRole()) || role.equals(part.getRole())) {
						list.add(cd);
						break;
					}
				}
			}
		}
		return list;
	}

	/**
	 * Inserts all the TopLevels (CDs and Sequences) in doc into design. If a
	 * TopLevel already exists, it will be overwritten.
	 */
	public static void insertTopLevels(SBOLDocument doc, SBOLDocument design) throws Exception {
		for (TopLevel tl : doc.getTopLevels()) {
			if (design.getTopLevel(tl.getIdentity()) != null) {
				design.removeTopLevel(design.getTopLevel(tl.getIdentity()));
			}
		}
		design.createCopy(doc);
	}

	public static void copyReferencedCombinatorialDerivations(SBOLDocument toDoc, SBOLDocument fromDoc)
			throws SBOLValidationException {
		for (CombinatorialDerivation derivation : fromDoc.getCombinatorialDerivations()) {
			if (toDoc.getComponentDefinitions().contains(derivation.getTemplate())) {
				fromDoc.createRecursiveCopy(toDoc, derivation);
			}
		}
	}

	/**
	 * Returns an int that represents the passed in version. 0 if version isn't
	 * a number.
	 */
	public static int getVersion(String version) {
		int v;
		try {
			v = Integer.parseInt(version);
		} catch (NumberFormatException e) {
			v = 0;
		}
		return v;
	}
}
 public enum Types {
	All_types, DNA, Complex, Effector, Protein, RNA, Small_molecule;
}
 public class ComponentDefinitionBox {
	public ComponentDefinition cd;
}
 public class MessageDialog {

	public static void showMessage(Component parentComponent, String title, List<String> messages) {
		StringBuilder sb = new StringBuilder();
		for (String message : messages) {
			sb.append(message);
			sb.append("\n");
		}
		showMessage(parentComponent, title, sb.toString());
	}

	public static void showMessage(Component parentComponent, String title, String message) {
		JTextArea jta = new JTextArea(message);
		jta.setLineWrap(true);
		jta.setWrapStyleWord(true);
		jta.setEditable(false);
		JScrollPane jsp = new JScrollPane(jta) {
			@Override
			public Dimension getPreferredSize() {
				return new Dimension(580, 320);
			}
		};
		JOptionPane.showMessageDialog(parentComponent, jsp, title, JOptionPane.ERROR_MESSAGE);
	}
}
 public class PartEditDialog extends JDialog implements ActionListener, DocumentListener {
	private static final String TITLE = "Part: ";

	private ComponentDefinition parentCD;
	private ComponentDefinition CD;
	private SequenceAnnotation SA;
	private SBOLDocument design;
	private boolean canEdit;
	private Component parent;
	private boolean updateParents;

	private final JComboBox<Part> roleSelection = new JComboBox<Part>(Iterables.toArray(Parts.sorted(), Part.class));
	private final JComboBox<Types> typeSelection = new JComboBox<Types>(Types.values());
	private final JComboBox<String> roleRefinement;
	private final JButton saveButton;
	private final JButton cancelButton;
	private final JButton importSequence;
	private final JButton importCD;
	private final JButton importFromRegistry;
	private final JButton openAnnotations;
	private final JTextField displayId = new JTextField();
	private final JTextField name = new JTextField();
	private final JTextField version = new JTextField();
	private final JTextField description = new JTextField();
	private final JLabel URIlink = new JLabel();
	private final JLabel derivedFrom = new JLabel();
	private final JTextArea sequenceField = new JTextArea(10, 80);
	private final JComboBox<String> sequenceEncoding = new JComboBox<String>(
			new String[] { "IUPAC_DNA", "IUPAC_PROTEIN", "IUPAC_RNA", "SMILES" });

	/**
	 * Returns the ComponentDefinition edited by PartEditDialog. Null if the
	 * dialog throws an exception. Also pass in the design.
	 */
	public static ComponentDefinition editPart(Component parent, ComponentDefinition parentCD, ComponentDefinition CD,
			boolean enableSave, boolean canEdit, SBOLDocument design, boolean updateParents) {
		try {
			PartEditDialog dialog = new PartEditDialog(parent, parentCD, CD, canEdit, design);
			dialog.saveButton.setEnabled(enableSave);
			if(canEdit == false) {
				dialog.importSequence.setEnabled(false);
				dialog.importFromRegistry.setEnabled(false);
				dialog.importCD.setEnabled(false);
				dialog.sequenceEncoding.setEnabled(false);
				dialog.description.setEnabled(false);
				dialog.name.setEnabled(false);
				dialog.displayId.setEnabled(false);
				dialog.roleRefinement.setEnabled(false);
				dialog.roleSelection.setEnabled(false);
				dialog.typeSelection.setEnabled(false);
			}
			dialog.updateParents = updateParents;
			dialog.setVisible(true);
			return dialog.CD;
		} catch (Exception e) {
			e.printStackTrace();
			MessageDialog.showMessage(parent, "Error", "Error editing component: " + e.getMessage());
			return null;
		}
	}

	/**
	 * Returns the SequenceAnnotation edited by PartEditDialog. Null if the
	 * dialog throws an exception. Also pass in the design.
	 */
	public static SequenceAnnotation editPart(Component parent, ComponentDefinition CD, SequenceAnnotation SA,
			boolean enableSave, boolean canEdit, SBOLDocument design) {
		try {
			PartEditDialog dialog = new PartEditDialog(parent, CD, SA, canEdit, design);
			dialog.saveButton.setEnabled(enableSave);
			dialog.setVisible(true);
			return dialog.SA;
		} catch (Exception e) {
			e.printStackTrace();
			MessageDialog.showMessage(parent, "Error", "Error editing sequenceAnnotation: " + e.getMessage());
			return null;
		}
	}

	private static String title(Identified comp) {
		String title = comp.getDisplayId();
		if (title == null) {
			title = comp.getName();
		}
		if (title == null) {
			URI uri = comp.getIdentity();
			title = (uri == null) ? null : uri.toString();
		}

		return (title == null) ? "" : CharSequenceUtil.shorten(title, 20).toString();
	}

	private PartEditDialog(final Component parent, final ComponentDefinition parentCD, final ComponentDefinition CD,
			boolean canEdit, SBOLDocument design) {
		super(JOptionPane.getFrameForComponent(parent), TITLE + title(CD), true);

		this.parentCD = parentCD;
		this.CD = CD;
		this.design = design;
		this.canEdit = canEdit;
		this.parent = parent;

		cancelButton = new JButton("Cancel");
		cancelButton.registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
				JComponent.WHEN_IN_FOCUSED_WINDOW);
		cancelButton.addActionListener(this);

		saveButton = new JButton("Save");
		saveButton.addActionListener(this);
		saveButton.setEnabled(false);
		getRootPane().setDefaultButton(saveButton);

		importFromRegistry = new JButton("Import registry part");
		importFromRegistry.addActionListener(this);
		importSequence = new JButton("Import sequence");
		importSequence.addActionListener(this);
		importCD = new JButton("Import part");
		importCD.addActionListener(this);
		openAnnotations = new JButton("Open annotations");
		openAnnotations.addActionListener(this);

		typeSelection.setSelectedItem(SBOLUtils.convertURIsToType(CD.getTypes()));
		typeSelection.addActionListener(this);

		roleSelection.setSelectedItem(Parts.forIdentified(CD));
		roleSelection.setRenderer(new PartCellRenderer());
		roleSelection.addActionListener(this);

		// set up the JComboBox for role refinement
		Part selectedPart = (Part) roleSelection.getSelectedItem();
		roleRefinement = new JComboBox<String>();
		updateRoleRefinement();
		List<URI> refinementRoles = SBOLUtils.getRefinementRoles(CD, selectedPart);
		if (!refinementRoles.isEmpty()) {
			SequenceOntology so = new SequenceOntology();
			roleRefinement.setSelectedItem(so.getName(refinementRoles.get(0)));
		} else {
			roleRefinement.setSelectedItem("None");
		}
		roleRefinement.addActionListener(this);

		// set up clickable URIlink
		URIlink.setText(CD.getIdentity().toString());
		URIlink.setCursor(new Cursor(Cursor.HAND_CURSOR));
		URIlink.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				try {
					Desktop.getDesktop().browse(CD.getIdentity());
				} catch (IOException e1) {
					MessageDialog.showMessage(parent, "Error", "The URI could not be opened: " + e1.getMessage());
				}
			}
		});

		sequenceEncoding.addActionListener(this);
		if (!CD.getSequences().isEmpty()) {
			Sequence s = CD.getSequences().iterator().next();
			sequenceEncoding.setSelectedItem(sequenceEncodingString(s.getEncoding()));
		} else {
			sequenceEncoding.setSelectedItem("IUPAC_DNA");
		}

		// put the controlsPane together
		// some fields are optional
		FormBuilder builder = new FormBuilder();
		builder.add("Part type", typeSelection);
		builder.add("Part role", roleSelection);
		builder.add("Role refinement", roleRefinement);
		builder.add("Display ID", displayId, CD.getDisplayId());
		builder.add("Name", name, CD.getName());
		builder.add("Description", description, CD.getDescription());
		if (CD.isSetVersion()) {
			version.setEditable(false);
			builder.add("Version", version, CD.getVersion());
		}
		builder.add("URI", URIlink);
		if (CD.isSetWasDerivedFrom()) {
			derivedFrom.setText(CD.getWasDerivedFrom().toString());
			derivedFrom.setCursor(new Cursor(Cursor.HAND_CURSOR));
			derivedFrom.addMouseListener(new MouseAdapter() {
				@Override
				public void mouseClicked(MouseEvent e) {
					try {
						Desktop.getDesktop().browse(CD.getWasDerivedFrom());
					} catch (IOException e1) {
						MessageDialog.showMessage(parent, "Error", "The URI could not be opened: " + e1.getMessage());
					}
				}
			});
			builder.add("Derived from", derivedFrom);
		}
		builder.add("Sequence encoding", sequenceEncoding);
		JPanel controlsPane = builder.build();

		JScrollPane tableScroller = new JScrollPane(sequenceField);
		tableScroller.setPreferredSize(new Dimension(550, 200));
		tableScroller.setAlignmentX(LEFT_ALIGNMENT);

		JPanel tablePane = new JPanel();
		tablePane.setLayout(new BoxLayout(tablePane, BoxLayout.PAGE_AXIS));
		JLabel label = new JLabel("Sequence");
		label.setLabelFor(sequenceField);
		tablePane.add(label);
		tablePane.add(Box.createRigidArea(new Dimension(0, 5)));
		tablePane.add(tableScroller);
		tablePane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

		sequenceField.setLineWrap(true);
		sequenceField.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
		Sequence seq = CD.getSequences().isEmpty() ? null : CD.getSequences().iterator().next();
		if (seq != null && !seq.getElements().isEmpty()) {
			sequenceField.setText(seq.getElements());
		}

		// Lay out the buttons from left to right.
		JPanel buttonPane = new JPanel();
		buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
		buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
		buttonPane.add(importFromRegistry);
		buttonPane.add(importCD);
		buttonPane.add(importSequence);
		buttonPane.add(openAnnotations);
		buttonPane.add(Box.createHorizontalStrut(100));
		buttonPane.add(Box.createHorizontalGlue());
		buttonPane.add(cancelButton);
		buttonPane.add(saveButton);

		// Put everything together, using the content pane's BorderLayout.
		Container contentPane = getContentPane();
		contentPane.add(controlsPane, BorderLayout.PAGE_START);
		contentPane.add(tablePane, BorderLayout.CENTER);
		contentPane.add(buttonPane, BorderLayout.PAGE_END);

		displayId.getDocument().addDocumentListener(this);
		name.getDocument().addDocumentListener(this);
		version.getDocument().addDocumentListener(this);
		description.getDocument().addDocumentListener(this);
		sequenceField.getDocument().addDocumentListener(this);

		pack();

		setLocationRelativeTo(parent);
		displayId.requestFocusInWindow();
	}

	private PartEditDialog(final Component parent, final ComponentDefinition CD, final SequenceAnnotation SA,
			boolean canEdit, SBOLDocument design) {
		super(JOptionPane.getFrameForComponent(parent), TITLE + title(SA), true);

		this.SA = SA;
		this.design = design;
		this.canEdit = canEdit;

		cancelButton = new JButton("Cancel");
		cancelButton.registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
				JComponent.WHEN_IN_FOCUSED_WINDOW);
		cancelButton.addActionListener(this);

		saveButton = new JButton("Save");
		saveButton.addActionListener(this);
		saveButton.setEnabled(false);
		getRootPane().setDefaultButton(saveButton);

		importFromRegistry = new JButton("Import registry part");
		importFromRegistry.addActionListener(this);
		importSequence = new JButton("Import sequence");
		importSequence.addActionListener(this);
		importCD = new JButton("Import part");
		importCD.addActionListener(this);
		openAnnotations = new JButton("Open annotations");
		openAnnotations.addActionListener(this);

		typeSelection.setSelectedItem(SBOLUtils.convertURIsToType(CD.getTypes()));
		typeSelection.addActionListener(this);

		roleSelection.setSelectedItem(Parts.forIdentified(SA));
		roleSelection.setRenderer(new PartCellRenderer());
		roleSelection.addActionListener(this);

		// set up the JComboBox for role refinement
		Part selectedPart = (Part) roleSelection.getSelectedItem();
		roleRefinement = new JComboBox<String>();
		updateRoleRefinement();
		List<URI> refinementRoles = SBOLUtils.getRefinementRoles(SA, selectedPart);
		if (!refinementRoles.isEmpty()) {
			SequenceOntology so = new SequenceOntology();
			roleRefinement.setSelectedItem(so.getName(refinementRoles.get(0)));
		} else {
			roleRefinement.setSelectedItem("None");
		}
		roleRefinement.addActionListener(this);

		// set up clickable URIlink
		URIlink.setText(CD.getIdentity().toString());
		URIlink.setCursor(new Cursor(Cursor.HAND_CURSOR));
		URIlink.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				try {
					Desktop.getDesktop().browse(CD.getIdentity());
				} catch (IOException e1) {
					MessageDialog.showMessage(parent, "Error", "The URI could not be opened: " + e1.getMessage());
				}
			}
		});

		// put the controlsPane together
		// some fields are optional
		FormBuilder builder = new FormBuilder();
		builder.add("Part type", typeSelection);
		builder.add("Part role", roleSelection);
		builder.add("Role refinement", roleRefinement);
		builder.add("Display ID", displayId, SA.getDisplayId());
		builder.add("Name", name, SA.getName());
		builder.add("Description", description, SA.getDescription());
		if (SA.isSetVersion()) {
			version.setEditable(false);
			builder.add("Version", version, SA.getVersion());
		}
		builder.add("URI", URIlink);
		if (SA.isSetWasDerivedFrom()) {
			derivedFrom.setText(SA.getWasDerivedFrom().toString());
			derivedFrom.setCursor(new Cursor(Cursor.HAND_CURSOR));
			derivedFrom.addMouseListener(new MouseAdapter() {
				@Override
				public void mouseClicked(MouseEvent e) {
					try {
						Desktop.getDesktop().browse(SA.getWasDerivedFrom());
					} catch (IOException e1) {
						MessageDialog.showMessage(parent, "Error", "The URI could not be opened: " + e1.getMessage());
					}
				}
			});
			builder.add("Derived from", derivedFrom);
		}
		JPanel controlsPane = builder.build();

		// TODO: read only for now
		roleSelection.setEnabled(false);
		roleRefinement.setEnabled(false);
		displayId.setEditable(false);
		name.setEditable(false);
		description.setEditable(false);
		importFromRegistry.setEnabled(false);
		importSequence.setEnabled(false);
		importCD.setEnabled(false);

		JScrollPane tableScroller = new JScrollPane(sequenceField);
		tableScroller.setPreferredSize(new Dimension(550, 200));
		tableScroller.setAlignmentX(LEFT_ALIGNMENT);

		JPanel tablePane = new JPanel();
		tablePane.setLayout(new BoxLayout(tablePane, BoxLayout.PAGE_AXIS));
		JLabel label = new JLabel("DNA sequence");
		label.setLabelFor(sequenceField);
		tablePane.add(label);
		tablePane.add(Box.createRigidArea(new Dimension(0, 5)));
		tablePane.add(tableScroller);
		tablePane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

		sequenceField.setLineWrap(true);
		sequenceField.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
		Sequence seq = CD.getSequenceByEncoding(Sequence.IUPAC_DNA);
		if (seq != null && !seq.getElements().isEmpty()) {
			if (SA.getLocations().size() == 1) {
				Location location = SA.getLocations().iterator().next();
				if (location instanceof Range) {
					Range range = (Range) location;
					sequenceField.setText(seq.getElements().substring(range.getStart() - 1, range.getEnd()));
				} else if (location instanceof Cut) {
					// TODO: need to consider how to display this
				}
			} else {
				// TODO: need to consider how to display multiple locations
			}
		}

		// TODO: read-only for now
		sequenceField.setEditable(false);

		// Lay out the buttons from left to right.
		JPanel buttonPane = new JPanel();
		buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
		buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
		buttonPane.add(importFromRegistry);
		buttonPane.add(importCD);
		buttonPane.add(importSequence);
		buttonPane.add(openAnnotations);
		buttonPane.add(Box.createHorizontalStrut(100));
		buttonPane.add(Box.createHorizontalGlue());
		buttonPane.add(cancelButton);
		buttonPane.add(saveButton);

		// Put everything together, using the content pane's BorderLayout.
		Container contentPane = getContentPane();
		contentPane.add(controlsPane, BorderLayout.PAGE_START);
		contentPane.add(tablePane, BorderLayout.CENTER);
		contentPane.add(buttonPane, BorderLayout.PAGE_END);

		displayId.getDocument().addDocumentListener(this);
		name.getDocument().addDocumentListener(this);
		version.getDocument().addDocumentListener(this);
		description.getDocument().addDocumentListener(this);
		sequenceField.getDocument().addDocumentListener(this);

		pack();

		setLocationRelativeTo(parent);
		displayId.requestFocusInWindow();
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		boolean keepVisible = false;
		if (e.getSource().equals(roleSelection) || e.getSource().equals(roleRefinement)
				|| e.getSource().equals(typeSelection) || e.getSource().equals(sequenceEncoding)) {
			if (canEdit) {
				saveButton.setEnabled(true);
			}
			if (e.getSource().equals(roleSelection)) {
				updateRoleRefinement();
			}
			if (e.getSource().equals(sequenceEncoding)) {
				validateSequenceEncoding(sequenceEncodingURI((String) sequenceEncoding.getSelectedItem()));
			}
			return;
		}

		if (e.getSource() == importSequence) {
			importSequenceHandler();
			return;
		}

		try {
			if (e.getSource() == importCD) {
				boolean isImported = false;
				isImported = importCDHandler();
				if (isImported) {
					setVisible(false);
				}
				return;
			}

			if (e.getSource() == importFromRegistry) {
				boolean isImported = false;
				isImported = importFromRegistryHandler();
				if (isImported) {
					setVisible(false);
				}
				return;
			}
		} catch (Exception e1) {
			MessageDialog.showMessage(parent, "Error", "This part cannot be imported: " + e1.getMessage());
			e1.printStackTrace();
		}

		if (e.getSource() == openAnnotations) {
			openAnnotationHandler();
			return;
		}

		try {
			if (e.getSource().equals(saveButton)) {
				saveButtonHandler();
			} else {
				// Sets comp to null if things don't get edited/cancel is
				// pressed
				CD = null;
			}
		} catch (Exception e1) {
			if (e1.getMessage().startsWith("sbol-10204")) {
				MessageDialog.showMessage(this, "What you have entered is invalid", "The displayId must not be empty.");
			} else {
				MessageDialog.showMessage(this, "What you have entered is invalid", e1.getMessage());
				e1.printStackTrace();
			}
			keepVisible = true;
		}

		if (!keepVisible) {
			setVisible(false);
		} else {
			keepVisible = false;
		}
	}

	private void updateRoleRefinement() {
		roleRefinement.removeAllItems();
		for (String s : SBOLUtils.createRefinements((Part) roleSelection.getSelectedItem())) {
			roleRefinement.addItem(s);
		}
	}

	private void validateSequenceEncoding(URI encoding) {
		if ((encoding == Sequence.IUPAC_DNA || encoding == Sequence.IUPAC_RNA)
				&& !(CD.containsType(ComponentDefinition.DNA) || CD.containsType(ComponentDefinition.RNA))) {
			JOptionPane.showMessageDialog(this,
					"Warning! If the sequence is DNA or RNA, then the part should be of type DNA or RNA.");
		}
		if (encoding == Sequence.IUPAC_PROTEIN && !CD.containsType(ComponentDefinition.PROTEIN)) {
			JOptionPane.showMessageDialog(this,
					"Warning! If the sequence is PROTEIN, then the part should be of type PROTEIN.");
		}
		if (encoding == Sequence.SMILES && !CD.containsType(ComponentDefinition.SMALL_MOLECULE)) {
			JOptionPane.showMessageDialog(this,
					"Warning! If the sequence is SMILES, then the part should be of type SMALL_MOLECULE.");
		}
	}

	private String sequenceEncodingString(URI encoding) {
		HashMap<URI, String> map = new HashMap<>();
		map.put(Sequence.IUPAC_RNA, "IUPAC_RNA");
		map.put(Sequence.IUPAC_DNA, "IUPAC_DNA");
		map.put(Sequence.IUPAC_PROTEIN, "IUPAC_PROTEIN");
		map.put(Sequence.SMILES, "SMILES");
		return map.containsKey(encoding) ? map.get(encoding) : encoding.toString();
	}

	private URI sequenceEncodingURI(String encoding) {
		HashMap<String, URI> map = new HashMap<>();
		map.put("IUPAC_RNA", Sequence.IUPAC_RNA);
		map.put("IUPAC_DNA", Sequence.IUPAC_DNA);
		map.put("IUPAC_PROTEIN", Sequence.IUPAC_PROTEIN);
		map.put("SMILES", Sequence.SMILES);
		return map.containsKey(encoding) ? map.get(encoding) : URI.create(encoding);
	}

	private void openAnnotationHandler() {
		new AnnotationEditor(this, CD);
	}

	/**
	 * Handles importing of a CD and all its dependencies from a registry.
	 * Returns true if something was imported. False otherwise.
	 */
	private boolean importFromRegistryHandler() throws Exception {
		Part part = roleSelection.getSelectedItem().equals("None") ? PartInputDialog.ALL_PARTS
				: (Part) roleSelection.getSelectedItem();
		URI role = new SequenceOntology().getURIbyName((String) roleRefinement.getSelectedItem());
		Types type = (Types) typeSelection.getSelectedItem();

		// User selects the CD
		ComponentDefinitionBox root = new ComponentDefinitionBox();
		SBOLDocument selection = new RegistryInputDialog(this, root, part, type, role).getInput();
		if (selection == null) {
			return false;
		} else {
			// copy the rest of the design into design
			SBOLUtils.insertTopLevels(selection, design);
			this.CD = root.cd;
			return true;
		}
	}

	/**
	 * Handles importing of a CD and all its dependencies. Returns true if
	 * something was imported. False otherwise.
	 */
	private boolean importCDHandler() throws Exception {
		SBOLDocument doc = SBOLUtils.importDoc();
		if (doc != null) {
			ComponentDefinition[] CDs = doc.getComponentDefinitions().toArray(new ComponentDefinition[0]);

			switch (CDs.length) {
			case 0:
				JOptionPane.showMessageDialog(this, "There are no parts to import");
				return false;
			case 1:
				CD = CDs[0];
				SBOLDocument newDoc = doc.createRecursiveCopy(CD);
				SBOLUtils.copyReferencedCombinatorialDerivations(newDoc, doc);
				SBOLUtils.insertTopLevels(newDoc, design);
				return true;
			default:
				Part criteria = roleSelection.getSelectedItem().equals("None") ? PartInputDialog.ALL_PARTS
						: (Part) roleSelection.getSelectedItem();

				// User selects the CD
				SBOLDocument selection = new PartInputDialog(this, doc, criteria).getInput();
				if (selection == null) {
					return false;
				} else {
					this.CD = selection.getRootComponentDefinitions().iterator().next();
					// copy the rest of the design into design
					SBOLUtils.insertTopLevels(selection, design);
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Fills in a CD and Sequence based on this dialog's state.
	 */
	private void saveButtonHandler() throws SBOLValidationException {
		if (SBOLUtils.notInNamespace(CD)) {
			// Rename CD and use that
			CD = confirmEditing(this, CD, design);
			if (CD == null) {
				return;
			}
		}

		// try to get CD if it exists. Otherwise, create it.
		if (design.getComponentDefinition(displayId.getText(), version.getText()) != null) {
			CD = design.getComponentDefinition(displayId.getText(), version.getText());
		} else {
			String uniqueId = SBOLUtils.getUniqueDisplayId(null, null, displayId.getText(), version.getText(), "CD",
					design);
			CD = (ComponentDefinition) design.rename(CD, uniqueId, version.getText());
		}

		if (name.getText().length() == 0) {
			if (CD.isSetName()) {
				CD.unsetName();
			}
		} else {
			CD.setName(name.getText());
		}
		CD.setDescription(description.getText());

		Types type = (Types) typeSelection.getSelectedItem();
		if (type != null) {
			CD.setTypes(SBOLUtils.convertTypesToSet(type));
		}

		Part part = (Part) roleSelection.getSelectedItem();
		if (part != null) {
			// change parts list of roles to set of roles
			Set<URI> setRoles = new HashSet<URI>(part.getRoles());
			// use the role from roleRefinement if not "None"
			if (!roleRefinement.getSelectedItem().equals("None")) {
				SequenceOntology so = new SequenceOntology();
				setRoles.clear();
				URI roleURI = so.getURIbyName((String) roleRefinement.getSelectedItem());
				if (!so.isDescendantOf(roleURI, part.getRole())) {
					throw new IllegalArgumentException(roleRefinement.getSelectedItem() + " isn't applicable for "
							+ roleSelection.getSelectedItem());
				}
				setRoles.add(roleURI);
			}
			CD.setRoles(setRoles);
		}

		String seq = sequenceField.getText();
		if (seq == null || seq.isEmpty()) {
			CD.clearSequences();
		} else if (CD.getSequences().isEmpty() || !Objects.equal(CD.getSequences().iterator().next().getElements(), seq)
				|| !Objects.equal(CD.getSequences().iterator().next().getEncoding(),
						sequenceEncodingURI((String) sequenceEncoding.getSelectedItem()))) {
			CD.clearSequences();
			String uniqueId = SBOLUtils.getUniqueDisplayId(null, null, CD.getDisplayId() + "Sequence", CD.getVersion(),
					"Sequence", design);
			Sequence sequence = design.createSequence(uniqueId, CD.getVersion(), seq,
					sequenceEncodingURI((String) sequenceEncoding.getSelectedItem()));
			CD.addSequence(sequence);
		}
	}

	/**
	 * Handles when the import sequence button is clicked.
	 */
	private void importSequenceHandler() {
		SBOLDocument doc = SBOLUtils.importDoc();
		if (doc != null) {
			Set<Sequence> seqSet = doc.getSequences();
			String importedNucleotides = "";
			if (seqSet.size() == 1) {
				// only one Sequence
				importedNucleotides = seqSet.iterator().next().getElements();
			} else {
				// multiple Sequences
				importedNucleotides = new SequenceInputDialog(this, seqSet).getInput();
			}
			sequenceField.setText(importedNucleotides);
		}
	}

	private static boolean sequenceFieldHandlerIsActive = false;

	private void handleSequenceField(DocumentEvent paramDocumentEvent) {
		if (!sequenceFieldHandlerIsActive) {
			if (paramDocumentEvent.getDocument() == sequenceField.getDocument()) {
				Runnable changeText = new Runnable() {
					@Override
					public void run() {
						sequenceFieldHandlerIsActive = true;
						String sequence = sequenceField.getText();
						sequenceField.setText(sequence.replaceAll("\\s+", ""));
						sequenceFieldHandlerIsActive = false;
					}
				};
				SwingUtilities.invokeLater(changeText);
			}
		}
	}

	@Override
	public void removeUpdate(DocumentEvent paramDocumentEvent) {
		handleSequenceField(paramDocumentEvent);
		if (canEdit) {
			saveButton.setEnabled(true);
		}
	}

	@Override
	public void insertUpdate(DocumentEvent paramDocumentEvent) {
		handleSequenceField(paramDocumentEvent);
		if (canEdit) {
			saveButton.setEnabled(true);
		}
	}

	@Override
	public void changedUpdate(DocumentEvent paramDocumentEvent) {
		handleSequenceField(paramDocumentEvent);
		if (canEdit) {
			saveButton.setEnabled(true);
		}
	}

	/**
	 * Returns the renamed CD or null if the user chooses "no". Also pass in the
	 * SBOLDocument containing the design.
	 */
	public static ComponentDefinition confirmEditing(Component parent, ComponentDefinition comp, SBOLDocument design)
			throws SBOLValidationException {
		int result = JOptionPane.showConfirmDialog(parent,
				"The part '" + comp.getDisplayId() + "' is not owned by you \n" + "and cannot be edited.\n\n"
						+ "Do you want to create an editable copy of\n" + "this part and save your changes?",
				"Edit registry part", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);

		if (result == JOptionPane.NO_OPTION) {
			return null;
		}
		ComponentDefinition cd = (ComponentDefinition) design.createCopy(comp,
				SBOLEditorPreferences.INSTANCE.getUserInfo().getURI().toString(), comp.getDisplayId(),
				comp.getVersion());
		return cd;
	}
}
 public class RegistryInputDialog extends InputDialog<SBOLDocument> {

	private final ComboBoxRenderer<Registry> registryRenderer = new ComboBoxRenderer<Registry>() {
		@Override
		protected String getLabel(Registry registry) {
			StringBuilder sb = new StringBuilder();
			if (registry != null) {
				sb.append(registry.getName());
				if (!registry.getLocation().equals("N/A")) {
					sb.append(" (");
					sb.append(CharSequenceUtil.shorten(registry.getLocation(), 30));
					sb.append(")");
				}
			}
			return sb.toString();
		}

		@Override
		protected String getToolTip(Registry registry) {
			return registry == null ? "" : registry.getDescription();
		}
	};

	private final ComboBoxRenderer<IdentifiedMetadata> collectionsRenderer = new ComboBoxRenderer<IdentifiedMetadata>() {
		@Override
		protected String getLabel(IdentifiedMetadata collection) {
			if (collection != null) {
				return collection.getDisplayId();
			} else {
				return "Unknown";
			}
		}

		@Override
		protected String getToolTip(IdentifiedMetadata collection) {
			return collection == null ? "" : collection.getDescription();
		}
	};

	private static final String TITLE = "Select a part from registry";

	public static final Part ALL_PARTS = new Part("All parts", "All");
	// represents what part we should display in role selection
	private Part part;
	// represents the role of the template CD, could be used in roleRefinement
	private URI refinementRole;
	private JComboBox<Part> roleSelection;
	private JComboBox<String> roleRefinement;
	private ActionListener roleRefinementListener = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent event) {
			updateTable();
		}
	};
	private Types type;
	private SBOLDocument workingDoc;
	private JComboBox<Types> typeSelection;
	private JComboBox<IdentifiedMetadata> collectionSelection;
	private boolean updateCollection = true;
	private ActionListener collectionSelectionListener = new ActionListener() {
		@Override
		public void actionPerformed(ActionEvent event) {
			// only update collectionSelection when we aren't programmatically
			// modifying it in collectionSelectionListener
			if (updateCollection) {
				updateCollectionSelection(false, null);
				updateTable();
			}
		}
	};
	private static HashMap<Registry, ArrayList<IdentifiedMetadata>> collectionPaths = new HashMap<>();

	private JTable table;
	private JLabel tableLabel;
	private JScrollPane scroller;

	final JTextField filterSelection = new JTextField();
	/*
	 * Determines whether the table should be refreshed when a user types in
	 * filter text. This is true when the results from SynBioHubQuery exceeds
	 * the QUERY_LIMIT.
	 */
	private boolean refreshSearch = false;
	/*
	 * Stores the filter text that caused the current ArrayList<TableMetadata>.
	 */
	private String cacheKey = "";

	private ComponentDefinitionBox root;

	private static SynBioHubFrontend synBioHub;

	private boolean allowCollectionSelection = false;

	private String objectType = "ComponentDefinition";

	/**
	 * Allows a collection to be selected.
	 */
	public void allowCollectionSelection() {
		allowCollectionSelection = true;
	}

	public void setObjectType(String objectType) {
		this.objectType = objectType;
	}

	/**
	 * For when the working document is known and the root is not needed.
	 */
	public RegistryInputDialog(final Component parent, final Part part, Types type, URI refinementRole,
			SBOLDocument workingDoc) {
		super(parent, TITLE);
		this.workingDoc = workingDoc;
		setup(null, part, type, refinementRole);
	}

	/**
	 * For when the working document is unknown and the root is not needed.
	 */
	public RegistryInputDialog(final Component parent, final Part part, Types type, URI refinementRole) {
		super(parent, TITLE);
		this.workingDoc = null;
		setup(null, part, type, refinementRole);
	}

	/**
	 * For when the working document is known and preferences node shouldn't be
	 * used
	 */
	public RegistryInputDialog(final Component parent, ComponentDefinitionBox root, final Part part, Types type,
			URI refinementRole, SBOLDocument workingDoc) {
		super(parent, TITLE);
		this.workingDoc = workingDoc;
		setup(root, part, type, refinementRole);
	}

	/**
	 * For when the working document is unknown and preferences node should be
	 * used
	 */
	public RegistryInputDialog(final Component parent, ComponentDefinitionBox root, final Part part, Types type,
			URI refinementRole) {
		super(parent, TITLE);
		this.workingDoc = null;
		setup(root, part, type, refinementRole);
	}

	/**
	 * root, if not null, will reference the root CD that was selected.
	 */
	private void setup(ComponentDefinitionBox root, final Part part, Types type, URI refinementRole) {
		this.root = root;
		this.part = part;
		this.refinementRole = refinementRole;
		this.type = type;

		Registries registries = Registries.get();
		int selectedRegistry = registries.getVersionRegistryIndex();

		registrySelection = new JComboBox<Registry>(Iterables.toArray(registries, Registry.class));
		if (registries.size() > 0) {
			registrySelection.setSelectedIndex(selectedRegistry);
		}
		registrySelection.addActionListener(actionListener);
		registrySelection.setRenderer(registryRenderer);
		builder.add("Registry", registrySelection);

		if (registries.size() == 0) {
			JOptionPane.showMessageDialog(this,
					"No parts registries are defined.\nPlease click 'Options' and add a parts registry.");
			location = null;
			uriPrefix = null;
		} else {
			location = registries.get(selectedRegistry).getLocation();
			uriPrefix = registries.get(selectedRegistry).getUriPrefix();
		}
	}

	@Override
	public void initFormPanel(FormBuilder builder) {
		// set up type selection
		typeSelection = new JComboBox<Types>(Types.values());
		typeSelection.setSelectedItem(type);
		typeSelection.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				updateTable();
				updateContext();
			}
		});
		if (objectType == "ComponentDefinition" || objectType == "Variant") {
			builder.add("Part type", typeSelection);
		}

		// set up collection selection
		collectionSelection = new JComboBox<IdentifiedMetadata>();
		collectionSelection.setRenderer(collectionsRenderer);
		updateCollectionSelection(true, null);
		collectionSelection.addActionListener(collectionSelectionListener);
		builder.add("Collection", collectionSelection);

		// set up role selection
		List<Part> parts = Lists.newArrayList(Parts.sorted());
		parts.add(0, ALL_PARTS);
		roleSelection = new JComboBox<Part>(parts.toArray(new Part[0]));
		roleSelection.setRenderer(new PartCellRenderer());
		roleSelection.setSelectedItem(part);
		roleSelection.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				part = (Part) roleSelection.getSelectedItem();
				updateRoleRefinement();
				updateTable();
			}
		});
		if (objectType == "ComponentDefinition" || objectType == "Variant") {
			builder.add("Part role", roleSelection);
		}

		// set up the JComboBox for role refinement
		roleRefinement = new JComboBox<String>();
		updateRoleRefinement();
		roleRefinement.removeActionListener(roleRefinementListener);
		if (refinementRole != null && refinementRole != part.getRole()) {
			String roleName = new SequenceOntology().getName(refinementRole);
			if (!comboBoxContains(roleRefinement, roleName)) {
				roleRefinement.addItem(roleName);
			}
			roleRefinement.setSelectedItem(roleName);
		}
		roleRefinement.addActionListener(roleRefinementListener);
		if (objectType == "ComponentDefinition" || objectType == "Variant") {
			builder.add("Role refinement", roleRefinement);
		}
		updateContext();

		// set up the filter
		filterSelection.getDocument().addDocumentListener(new DocumentListener() {
			@Override
			public void removeUpdate(DocumentEvent paramDocumentEvent) {
				searchOrFilterTable();
			}

			@Override
			public void insertUpdate(DocumentEvent paramDocumentEvent) {
				searchOrFilterTable();
			}

			@Override
			public void changedUpdate(DocumentEvent paramDocumentEvent) {
				searchOrFilterTable();
			}

			private void searchOrFilterTable() {
				/*
				 * System.out.println();
				 * System.out.println("searchOrFilterTable");
				 * System.out.println("refreshSearch: " + refreshSearch);
				 * System.out.println("cacheKey: " + cacheKey);
				 * System.out.println("filter: " + filterSelection.getText());
				 */
				if ((refreshSearch || filterSelection.getText().equals("")
						|| !filterSelection.getText().contains(cacheKey)) && isMetadata()) {
					searchParts(part, synBioHub, filterSelection.getText());
				} else {
					updateFilter(filterSelection.getText());
				}
			}
		});
		builder.add("Filter parts", filterSelection);
	}

	/**
	 * Returns whether box contains s
	 */
	private boolean comboBoxContains(JComboBox<String> box, String s) {
		for (int i = 0; i < box.getItemCount(); i++) {
			if (s != null && s.equals(roleRefinement.getItemAt(i))) {
				return true;
			}
		}
		return false;
	}

	@Override
	protected JPanel initMainPanel() {
		JPanel panel;

		Part part = null;
		if (roleSelection.isEnabled() && roleRefinement.isEnabled()) {
			String roleName = (String) roleRefinement.getSelectedItem();
			if (roleName == null || roleName.equals("None")) {
				part = (Part) roleSelection.getSelectedItem();
			} else {
				SequenceOntology so = new SequenceOntology();
				URI role = so.getURIbyName(roleName);
				part = new Part(role, null, null);
			}
		} else {
			part = ALL_PARTS;
		}

		if (isMetadata()) {
			searchParts(part, synBioHub, filterSelection.getText());
			TableMetadataTableModel tableModel = new TableMetadataTableModel(new ArrayList<TableMetadata>());
			panel = createTablePanel(tableModel, "Matching parts (" + tableModel.getRowCount() + ")");
		} else if(objectType == "Variant"){
			List<TopLevel> topLevels = searchForPotentialVariants(part);
			TopLevelTableModel model = new TopLevelTableModel(topLevels);
			panel = createTablePanel(model, "Matching parts (" + model.getRowCount() + ")");
		}else {
			List<ComponentDefinition> components = searchParts(part);
			ComponentDefinitionTableModel tableModel = new ComponentDefinitionTableModel(components);
			panel = createTablePanel(tableModel, "Matching parts (" + tableModel.getRowCount() + ")");
		}

		table = (JTable) panel.getClientProperty("table");
		tableLabel = (JLabel) panel.getClientProperty("label");
		scroller = (JScrollPane) panel.getClientProperty("scroller");

		return panel;
	}

	/**
	 * Checks to see if the registry we are working on is represented by
	 * IdentifiedMetadata.
	 */
	private boolean isMetadata() {
		return location.startsWith("http://") || location.startsWith("https://");
	}

	private List<TopLevel> searchForPotentialVariants(Part part) {
		try {
			if (isMetadata()) {
				throw new Exception("Incorrect state.  url isn't a path");
			}

			if (part.equals(ALL_PARTS)) {
				part = null;
			}

			SBOLReader.setURIPrefix(SBOLEditorPreferences.INSTANCE.getUserInfo().getURI().toString());
			SBOLReader.setCompliant(true);
			SBOLDocument doc;
			Registry registry = (Registry) registrySelection.getSelectedItem();

			if (registry.equals(Registry.BUILT_IN)) {
				// read from BuiltInParts.xml
				doc = SBOLReader.read(Registry.class.getResourceAsStream("/BuiltInParts.xml"));

			} else if (registry.equals(Registry.WORKING_DOCUMENT)) {
				if (workingDoc != null) {
					// workingDoc is specified, so use that
					doc = workingDoc;
				} else {
					// read from SBOLUtils.setupFile();
					File file = SBOLUtils.setupFile();

					if (file.exists()) {
						doc = SBOLReader.read(file);
					} else {
						// JOptionPane.showMessageDialog(null, "The working
						// document could not be found on disk. Try opening the
						// file again.");
						return new ArrayList<TopLevel>();
					}
				}

			} else {
				// read from the location (path)
				doc = SBOLReader.read(location);
			}

			doc.setDefaultURIprefix(SBOLEditorPreferences.INSTANCE.getUserInfo().getURI().toString());
			return SBOLUtils.getCDCollectionsAndComboDerv(doc, part);

		} catch (Exception e) {
			e.printStackTrace();
			MessageDialog.showMessage(null, "Getting the SBOLDocument from path failed: ", e.getMessage());
			Registries registries = Registries.get();
			registries.setVersionRegistryIndex(0);
			registries.save();
			return null;
		}
	}
	
	/**
	 * Gets the SBOLDocument from the path (file on disk) and returns all its
	 * CDs.
	 */
	private List<ComponentDefinition> searchParts(Part part) {
		try {
			if (isMetadata()) {
				throw new Exception("Incorrect state.  url isn't a path");
			}

			if (part.equals(ALL_PARTS)) {
				part = null;
			}

			SBOLReader.setURIPrefix(SBOLEditorPreferences.INSTANCE.getUserInfo().getURI().toString());
			SBOLReader.setCompliant(true);
			SBOLDocument doc;
			Registry registry = (Registry) registrySelection.getSelectedItem();

			if (registry.equals(Registry.BUILT_IN)) {
				// read from BuiltInParts.xml
				doc = SBOLReader.read(Registry.class.getResourceAsStream("/BuiltInParts.xml"));

			} else if (registry.equals(Registry.WORKING_DOCUMENT)) {
				if (workingDoc != null) {
					// workingDoc is specified, so use that
					doc = workingDoc;
				} else {
					// read from SBOLUtils.setupFile();
					File file = SBOLUtils.setupFile();

					if (file.exists()) {
						doc = SBOLReader.read(file);
					} else {
						// JOptionPane.showMessageDialog(null, "The working
						// document could not be found on disk. Try opening the
						// file again.");
						return new ArrayList<ComponentDefinition>();
					}
				}

			} else {
				// read from the location (path)
				doc = SBOLReader.read(location);
			}

			doc.setDefaultURIprefix(SBOLEditorPreferences.INSTANCE.getUserInfo().getURI().toString());
			return SBOLUtils.getCDOfRole(doc, part);

		} catch (Exception e) {
			e.printStackTrace();
			MessageDialog.showMessage(null, "Getting the SBOLDocument from path failed: ", e.getMessage());
			Registries registries = Registries.get();
			registries.setVersionRegistryIndex(0);
			registries.save();
			return null;
		}
	}

	/**
	 * Queries SynBioHub for CDs matching the role(s), type(s), and
	 * collection(s) of the part. Also filters by the filterText.
	 */
	private void searchParts(Part part, SynBioHubFrontend synbiohub, String filterText) {
		try {
			if (!isMetadata()) {
				throw new Exception("Incorrect state.  url is a path");
			}

			if (synbiohub == null) {
				synbiohub = createSynBioHubFrontend(location, uriPrefix);
			}

			if (part != null) {
				// create the query
				IdentifiedMetadata selectedCollection = (IdentifiedMetadata) collectionSelection.getSelectedItem();

				if (selectedCollection == null) {
					return;
				}

				Set<URI> setCollections = new HashSet<URI>(Arrays.asList(URI.create(selectedCollection.getUri())));
				Set<URI> setRoles = new HashSet<URI>(part.getRoles());
				Set<URI> setTypes = SBOLUtils.convertTypesToSet((Types) typeSelection.getSelectedItem());
				String type = objectType;
				if(type == "Variant")
					type = "ComponentDefinition";
				SynBioHubQuery query = new SynBioHubQuery(synbiohub, setRoles, setTypes, setCollections, filterText,
						type, new TableUpdater(), this);
				// non-blocking: will update using the TableUpdater
				query.execute();
			}

		} catch (Exception e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(null, "Querying this repository failed: " + e.getMessage() + "\n"
					+ " Internet connection is required for importing from SynBioHub. Setting default registry to built-in parts, which doesn't require an internet connection.");
			Registries registries = Registries.get();
			registries.setVersionRegistryIndex(0);
			registries.save();
		}
	}
	
	public URI getSelectedURI() {
		TopLevel comp = null;
		int row = table.convertRowIndexToModel(table.getSelectedRow());

		if (isMetadata()) {
			TableMetadata compMeta = ((TableMetadataTableModel) table.getModel()).getElement(row);
			return URI.create(compMeta.identified.getUri());
		} else {
			comp = ((TopLevelTableModel) table.getModel()).getElement(row);
			return comp.getIdentity();
		}
	}

	@Override
	protected SBOLDocument getSelection() {
		try {
			SBOLDocument document = null;
			TopLevel comp = null;
			int row = table.convertRowIndexToModel(table.getSelectedRow());

			if (isMetadata()) {
				TableMetadata compMeta = ((TableMetadataTableModel) table.getModel()).getElement(row);

				if (synBioHub == null) {
					System.out.print(uriPrefix);
					synBioHub = createSynBioHubFrontend(location, uriPrefix);
				}

				if (compMeta.isCollection) {
					if(!allowCollectionSelection)
					{
						JOptionPane.showMessageDialog(getParent(), "Selecting collections is not allowed");
						return new SBOLDocument();
					}else {
						document = synBioHub.getSBOL(URI.create(compMeta.identified.getUri()));
						return document;
					}
					
				}
				

				if(!compMeta.identified.getUri().toString().startsWith(uriPrefix)) {
					Registries regs = Registries.get();
					for(Registry reg : regs){
					    if(compMeta.identified.getUri().toString().startsWith(reg.getUriPrefix())) {
					    		synBioHub = createSynBioHubFrontend(reg.getLocation(), reg.getUriPrefix());
					    		break;
					    }
					}
				}
				document = synBioHub.getSBOL(URI.create(compMeta.identified.getUri()));
				comp = document.getComponentDefinition(URI.create(compMeta.identified.getUri()));

				if (comp == null) {
					// if cannot find it then return root component definition
					// from document
					for (ComponentDefinition cd : document.getRootComponentDefinitions()) {
						comp = cd;
					}
				}
			} else {
				document = new SBOLDocument();
				comp = ((TopLevelTableModel) table.getModel()).getElement(row);
				document = document.createRecursiveCopy(comp);
			}

			if (root != null) {
				root.cd = document.getComponentDefinition(comp.getIdentity());
			}

			return document;
		} catch (SBOLValidationException | SynBioHubException e) {
			e.printStackTrace();
			MessageDialog.showMessage(null, "Getting this selection failed: ", e.getMessage());
			return null;
		}
	}

	@Override
	protected void registryChanged() {
		if (isMetadata()) {
			synBioHub = createSynBioHubFrontend(location, uriPrefix);
		}
		loginButton.setEnabled(isMetadata());
		updateCollectionSelection(true, null);
		updateTable();
	}

	private void updateCollectionSelection(boolean registryChanged, IdentifiedMetadata newCollection) {
		collectionSelection.setEnabled(isMetadata());
		if (!isMetadata()) {
			return;
		}
		if (synBioHub == null) {
			synBioHub = createSynBioHubFrontend(location, uriPrefix);
		}
		Registry registry = (Registry) registrySelection.getSelectedItem();

		updateCollection = false;
		if (registryChanged) {
			// display only "rootCollections"
			IdentifiedMetadata allCollections = new IdentifiedMetadata();
			allCollections.setName("All Collections");
			allCollections.setDisplayId("All Collections");
			allCollections.setUri("http://AllCollections");
			IdentifiedMetadata rootCollections = new IdentifiedMetadata();
			rootCollections.setName("Root Collections");
			rootCollections.setDisplayId("Root Collections");
			rootCollections.setUri("http://RootCollections");
			collectionSelection.removeAllItems();
			collectionSelection.addItem(allCollections);
			collectionSelection.addItem(rootCollections);
			collectionSelection.setSelectedItem(rootCollections);

			// restore/create cached collection path
			if (collectionPaths.containsKey(registry)) {
				for (IdentifiedMetadata collection : collectionPaths.get(registry)) {
					collectionSelection.addItem(collection);
					collectionSelection.setSelectedItem(collection);
				}
			} else {
				collectionPaths.put(registry, new ArrayList<>());
			}
		} else {
			// clicked on different collection
			if (newCollection != null) {
				collectionSelection.addItem(newCollection);
				collectionSelection.setSelectedItem(newCollection);
				collectionPaths.get(registry).add(newCollection);
			} else {
				int stackMod = 1;
				if(collectionSelection.getSelectedIndex() == 0) {
					stackMod = 2;
				}
				while (collectionSelection.getSelectedIndex() + stackMod < collectionSelection.getItemCount()) {
					collectionSelection.removeItemAt(collectionSelection.getSelectedIndex() + stackMod);
					// TODO: new check
					if (collectionSelection.getSelectedIndex() < collectionPaths.get(registry).size()) {
						collectionPaths.get(registry).remove(collectionSelection.getSelectedIndex());
					}
				}
			}
		}

		updateCollection = true;
	}

	private void updateRoleRefinement() {
		roleRefinement.removeActionListener(roleRefinementListener);
		roleRefinement.removeAllItems();
		for (String s : SBOLUtils.createRefinements((Part) roleSelection.getSelectedItem())) {
			roleRefinement.addItem(s);
		}
		roleRefinement.addActionListener(roleRefinementListener);
	}

	private void updateContext() {
		boolean enableRoles = typeSelection.getSelectedItem() == Types.DNA
				|| typeSelection.getSelectedItem() == Types.RNA;
		roleSelection.setEnabled(enableRoles);
		roleRefinement.setEnabled(enableRoles);
	}

	public void updateTable() {
		// create the part criteria
		Part part = null;
		if (roleSelection.isEnabled() && roleRefinement.isEnabled()) {
			String roleName = (String) roleRefinement.getSelectedItem();
			if (roleName == null || roleName.equals("None")) {
				part = (Part) roleSelection.getSelectedItem();
			} else {
				SequenceOntology so = new SequenceOntology();
				URI role = so.getURIbyName(roleName);
				part = new Part(role, null, null);
			}
		} else {
			part = ALL_PARTS;
		}

		if (isMetadata()) {
			searchParts(part, synBioHub, filterSelection.getText());
		} else if(objectType == "Variant"){
			List<TopLevel> topLevels = searchForPotentialVariants(part);
			//topLevels = SBOLUtils.getTopLevelOfType(topLevels, (Types) typeSelection.getSelectedItem());
			TopLevelTableModel tableModel = new TopLevelTableModel(topLevels);
			table = new JTable(tableModel);
			tableLabel.setText("Matching parts (" + topLevels.size() + ")");
			refreshSearch = false;
			TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel);
			table.setRowSorter(sorter);
			setWidthAsPercentages(table, tableModel.getWidths());
		}else {
			List<ComponentDefinition> components = searchParts(part);
			components = SBOLUtils.getCDOfType(components, (Types) typeSelection.getSelectedItem());
			ComponentDefinitionTableModel tableModel = new ComponentDefinitionTableModel(components);
			table = new JTable(tableModel);
			tableLabel.setText("Matching parts (" + components.size() + ")");
			refreshSearch = false;
			TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel);
			table.setRowSorter(sorter);
			setWidthAsPercentages(table, tableModel.getWidths());
		}
		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent event) {
				setSelectAllowed(table.getSelectedRow() >= 0);
			}
		});
		table.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2 && table.getSelectedRow() >= 0) {
					handleTableSelection(false);
				}
			}
		});
		scroller.setViewportView(table);
	}

	@Override
	protected void handleTableSelection(boolean select) {
		// handle collection selected
		if (isMetadata()) {
			int row = table.convertRowIndexToModel(table.getSelectedRow());
			TableMetadata meta = ((TableMetadataTableModel) table.getModel()).getElement(row);
			if (meta.isCollection && (!select || !allowCollectionSelection)) {
				updateCollectionSelection(false, meta.identified);
				updateTable();
				return;
			}
		}
		// otherwise a part was selected
		canceled = false;
		setVisible(false);
	}

	private void updateFilter(String filterText) {
		filterText = "(?i)" + filterText;
		if (isMetadata()) {
			TableRowSorter<TableMetadataTableModel> sorter = (TableRowSorter) table.getRowSorter();
			if (filterText.length() == 0) {
				sorter.setRowFilter(null);
			} else {
				try {
					RowFilter<TableMetadataTableModel, Object> rf = RowFilter.regexFilter(filterText, 0, 1, 2, 4);
					sorter.setRowFilter(rf);
				} catch (PatternSyntaxException e) {
					sorter.setRowFilter(null);
				}
			}
			tableLabel.setText("Matching parts (" + sorter.getViewRowCount() + ")");
		} else {
			TableRowSorter<TopLevelTableModel> sorter = (TableRowSorter) table.getRowSorter();
			if (filterText.length() == 0) {
				sorter.setRowFilter(null);
			} else {
				try {
					RowFilter<TopLevelTableModel, Object> rf = RowFilter.regexFilter(filterText, 0, 1, 2, 4);
					sorter.setRowFilter(rf);
				} catch (PatternSyntaxException e) {
					sorter.setRowFilter(null);
				}
			}
			tableLabel.setText("Matching parts (" + sorter.getViewRowCount() + ")");
		}
	}

	/**
	 * Updates the table using the provided components. This lets the
	 * SBOLStackQuery thread update the table.
	 */
	public class TableUpdater {
		public void updateTable(ArrayList<TableMetadata> identified, String filterText) {
			if (!filterSelection.getText().equals(filterText)) {
				// don't update if the filterSelection text has changed.
				return;
			}

			TableMetadataTableModel tableModel = new TableMetadataTableModel(identified);
			table = new JTable(tableModel);
			tableLabel.setText("Matching parts (" + identified.size() + ")");

			refreshSearch = identified.size() >= SynBioHubQuery.QUERY_LIMIT;
			if (filterText != null && !refreshSearch) {
				cacheKey = filterText;
			}
			/*
			 * System.out.println(); System.out.println("TableUpdater");
			 * System.out.println("refreshSearch: " + refreshSearch);
			 * System.out.println("cacheKey: " + cacheKey); System.out.println(
			 * "filter: " + filterSelection.getText());
			 */

			TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel);
			table.setRowSorter(sorter);
			setWidthAsPercentages(table, tableModel.getWidths());

			table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
			table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
				@Override
				public void valueChanged(ListSelectionEvent event) {
					setSelectAllowed(table.getSelectedRow() >= 0);
				}
			});

			table.addMouseListener(new MouseAdapter() {
				public void mouseClicked(MouseEvent e) {
					if (e.getClickCount() == 2 && table.getSelectedRow() >= 0) {
						handleTableSelection(false);
					}
				}
			});

			scroller.setViewportView(table);
		}
	}

	/**
	 * Wraps SynBioHubFrontend creation so legacy locations can be used.
	 */
	private SynBioHubFrontend createSynBioHubFrontend(String location, String uriPrefix) {
		// update location and SynBioHub location if not using https
		if (location == "http://synbiohub.org") {
			location = "https://synbiohub.org";

			// This isn't elegant, but should work
			ArrayList<Registry> oldRegistries = new ArrayList<Registry>();
			for (int i = 3; i < Registries.get().size(); i++) {
				oldRegistries.add(Registries.get().get(i));
			}

			Registries.get().restoreDefaults();

			for (Registry r : oldRegistries) {
				Registries.get().add(r);
			}

			Registries.get().save();
		}

		// get logged in SynBioHubFrontend if possible
		SynBioHubFrontends frontends = new SynBioHubFrontends();
		if (frontends.hasFrontend(location)) {
			return frontends.getFrontend(location);
		}

		return new SynBioHubFrontend(location, uriPrefix);
	}
}
 public class RegistryLoginDialog extends JDialog implements ActionListener {
	private String backendUrl;
	private String uriPrefix;
	private Component parent;
	private SynBioHubFrontend frontend = null;

	private final JButton loginButton = new JButton("Login");
	private final JButton cancelButton = new JButton("Cancel");
	private final JTextField username = new JTextField("");
	private final JPasswordField password = new JPasswordField("");

	public RegistryLoginDialog(Component parent, String backendUrl, String uriPrefix) {
		super(JOptionPane.getFrameForComponent(parent), "Login to " + backendUrl, true);
		this.backendUrl = backendUrl;
		this.uriPrefix = uriPrefix;
		this.parent = parent;

		PersonInfo userInfo = SBOLEditorPreferences.INSTANCE.getUserInfo();
		String email = userInfo == null || userInfo.getEmail() == null ? null : userInfo.getEmail().getLocalName();
		username.setText(email);
		password.setEchoChar('*');

		cancelButton.registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
				JComponent.WHEN_IN_FOCUSED_WINDOW);
		cancelButton.addActionListener(this);

		loginButton.addActionListener(this);
		getRootPane().setDefaultButton(loginButton);

		JPanel buttonPane = new JPanel();
		buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
		buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		buttonPane.add(Box.createHorizontalStrut(100));
		buttonPane.add(Box.createHorizontalGlue());
		buttonPane.add(cancelButton);
		buttonPane.add(loginButton);

		FormBuilder builder = new FormBuilder();
		builder.add("Username", username);
		builder.add("Password", password);
		JPanel mainPanel = builder.build();
		mainPanel.setAlignmentX(LEFT_ALIGNMENT);

		JLabel infoLabel = new JLabel(
				"Login to SynBioHub account.  This enables you to upload parts and access your private constructs.");

		Container contentPane = getContentPane();
		contentPane.add(infoLabel, BorderLayout.PAGE_START);
		contentPane.add(mainPanel, BorderLayout.CENTER);
		contentPane.add(buttonPane, BorderLayout.PAGE_END);
		((JComponent) contentPane).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

		pack();
		setLocationRelativeTo(parent);
		setVisible(true);
	}

	public SynBioHubFrontend getSynBioHubFrontend() {
		return frontend;
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == cancelButton) {
			setVisible(false);
			return;
		}

		if (e.getSource() == loginButton) {
			frontend = new SynBioHubFrontend(backendUrl, uriPrefix);
			try {
				frontend.login(username.getText(), new String(password.getPassword()));
				JOptionPane.showMessageDialog(parent, "Login successful!");
			} catch (SynBioHubException e1) {
				setVisible(false);
				MessageDialog.showMessage(parent, "Login failed", Arrays.asList(e1.getMessage().split("\"|,")));
				frontend = null;
				e1.printStackTrace();
				return;
			}
			setVisible(false);
			return;
		}
	}
}
 public class RootInputDialog extends InputDialog<SBOLDocument> {
	private static final String TITLE = "Select a root design to open";

	private JTable table;
	private JLabel tableLabel;

	private JComboBox<Part> roleSelection;
	private JComboBox<String> roleRefinement;
	private JComboBox<Types> typeSelection;
	private JCheckBox onlyShowRootCDs;
	private JButton deleteCD;
	private static final Part ALL_PARTS = new Part("All parts", "All");

	private SBOLDocument doc;

	private ComponentDefinitionBox root;

	/**
	 * this.getInput() returns an SBOLDocument with a single rootCD selected
	 * from the rootCDs in doc.
	 * 
	 * Root will reference the root CD that was selected.
	 */
	public RootInputDialog(final Component parent, SBOLDocument doc, ComponentDefinitionBox root) {
		super(parent, TITLE);

		this.doc = doc;
		this.root = root;
	}

	@Override
	protected String initMessage() {
		return "There are multiple designs.  Which would you like to load?  (You will be editing a new partial design)";
	}

	@Override
	public void initFormPanel(FormBuilder builder) {
		typeSelection = new JComboBox<Types>(Types.values());
		typeSelection.setSelectedItem(Types.DNA);
		typeSelection.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				updateTable();
			}
		});
		builder.add("Part type", typeSelection);

		List<Part> parts = Lists.newArrayList(Parts.sorted());
		parts.add(0, ALL_PARTS);
		roleSelection = new JComboBox<Part>(parts.toArray(new Part[0]));
		roleSelection.setRenderer(new PartCellRenderer());
		roleSelection.setSelectedItem(ALL_PARTS);
		roleSelection.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				updateRoleRefinement();
				updateTable();
			}
		});
		builder.add("Part role", roleSelection);

		// set up the JComboBox for role refinement
		roleRefinement = new JComboBox<String>();
		updateRoleRefinement();
		roleRefinement.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				updateTable();
			}
		});
		builder.add("Role refinement", roleRefinement);

		onlyShowRootCDs = new JCheckBox("Only show root ComponentDefinitions");
		onlyShowRootCDs.setSelected(true);
		onlyShowRootCDs.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent event) {
				updateTable();
			}
		});
		builder.add("", onlyShowRootCDs);

		deleteCD = new JButton("Delete selected part(s). (This will resave the file)");
		deleteCD.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				try {
					int[] rows = table.getSelectedRows();
					for (int row : rows) {
						row = table.convertRowIndexToModel(row);
						ComponentDefinition comp = ((ComponentDefinitionTableModel) table.getModel()).getElement(row);
						doc.removeComponentDefinition(comp);
					}
					File file = SBOLUtils.setupFile();
					SBOLWriter.write(doc, new FileOutputStream(file));
					updateTable();
				} catch (Exception e1) {
					MessageDialog.showMessage(rootPane, "Failed to delete CD: ", e1.getMessage());
					e1.printStackTrace();
				}
			}
		});
		builder.add("", deleteCD);

		final JTextField filterSelection = new JTextField();
		filterSelection.getDocument().addDocumentListener(new DocumentListener() {
			@Override
			public void removeUpdate(DocumentEvent paramDocumentEvent) {
				updateFilter(filterSelection.getText());
			}

			@Override
			public void insertUpdate(DocumentEvent paramDocumentEvent) {
				updateFilter(filterSelection.getText());
			}

			@Override
			public void changedUpdate(DocumentEvent paramDocumentEvent) {
				updateFilter(filterSelection.getText());
			}
		});

		builder.add("Filter parts", filterSelection);
	}

	@Override
	protected JPanel initMainPanel() {
		List<ComponentDefinition> components = new ArrayList<ComponentDefinition>();
		if (onlyShowRootCDs.isSelected()) {
			components.addAll(doc.getRootComponentDefinitions());
		} else {
			components.addAll(doc.getComponentDefinitions());
		}

		ComponentDefinitionTableModel tableModel = new ComponentDefinitionTableModel(components);
		JPanel panel = createTablePanel(tableModel, "Matching parts (" + tableModel.getRowCount() + ")");
		table = (JTable) panel.getClientProperty("table");
		table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		tableLabel = (JLabel) panel.getClientProperty("label");

		updateTable();

		return panel;
	}

	@Override
	protected SBOLDocument getSelection() {
		try {
			int row = table.convertRowIndexToModel(table.getSelectedRow());
			ComponentDefinition comp = ((ComponentDefinitionTableModel) table.getModel()).getElement(row);
			SBOLDocument newDoc = doc.createRecursiveCopy(comp);
			SBOLUtils.copyReferencedCombinatorialDerivations(newDoc, doc);
			root.cd = newDoc.getComponentDefinition(comp.getIdentity());
			return newDoc;
		} catch (SBOLValidationException e) {
			MessageDialog.showMessage(null, "This ComponentDefinition cannot be imported: ", e.getMessage());
			e.printStackTrace();
			return null;
		}
	}

	private void updateRoleRefinement() {
		roleRefinement.removeAllItems();
		for (String s : SBOLUtils.createRefinements((Part) roleSelection.getSelectedItem())) {
			roleRefinement.addItem(s);
		}
	}

	private void updateTable() {
		Part part;
		String roleName = (String) roleRefinement.getSelectedItem();
		if (roleName == null || roleName.equals("None")) {
			part = isRoleSelection() ? (Part) roleSelection.getSelectedItem() : ALL_PARTS;
		} else {
			SequenceOntology so = new SequenceOntology();
			URI role = so.getURIbyName(roleName);
			part = new Part(role, null, null);
		}

		Set<ComponentDefinition> CDsToDisplay;
		if (onlyShowRootCDs.isSelected()) {
			CDsToDisplay = doc.getRootComponentDefinitions();
		} else {
			CDsToDisplay = doc.getComponentDefinitions();
		}

		List<ComponentDefinition> components = SBOLUtils.getCDOfRole(CDsToDisplay, part);
		components = SBOLUtils.getCDOfType(components, (Types) typeSelection.getSelectedItem());
		((ComponentDefinitionTableModel) table.getModel()).setElements(components);
		tableLabel.setText("Matching parts (" + components.size() + ")");
	}

	private boolean isRoleSelection() {
		return roleSelection != null;
	}

	private void updateFilter(String filterText) {
		filterText = "(?i)" + filterText;
		@SuppressWarnings({ "rawtypes", "unchecked" })
		TableRowSorter<ComponentDefinitionTableModel> sorter = (TableRowSorter) table.getRowSorter();
		if (filterText.length() == 0) {
			sorter.setRowFilter(null);
		} else {
			try {
				RowFilter<ComponentDefinitionTableModel, Object> rf = RowFilter.regexFilter(filterText, 0, 1);
				sorter.setRowFilter(rf);
			} catch (java.util.regex.PatternSyntaxException e) {
				sorter.setRowFilter(null);
			}
		}

		tableLabel.setText("Matching parts (" + sorter.getViewRowCount() + ")");
	}
}
 public class UploadExistingDialog extends JDialog implements ActionListener, ListSelectionListener {
	private static final String TITLE = "Upload Design: ";

	private static String title(Registry registry) {
		String title = "";
		if (registry.getName() != null) {
			title = title + registry.getName();
		} else if (registry.getLocation() != null) {
			title = title + registry.getLocation();
		}
		return CharSequenceUtil.shorten(title, 20).toString();
	}

	private Component parent;
	private Registry registry;
	private SBOLDocument toBeUploaded;
	private File toBeUploadedFile;

	private final JLabel info = new JLabel(
			"Select an existing collection(s) to upload the design into.  If Overwrite is selected, the uploaded part will overwrite any parts that have the same URI in the selected collection.");
	private final JButton uploadButton = new JButton("Upload");
	private final JButton cancelButton = new JButton("Cancel");
	private final JCheckBox overwrite = new JCheckBox("");
	private JList<IdentifiedMetadata> collections = null;

	public UploadExistingDialog(final Component parent, Registry registry, SBOLDocument uploadDoc) {
		super(JOptionPane.getFrameForComponent(parent), TITLE + title(registry), true);
		CreateUploadExistingDialog(parent, registry, uploadDoc, null);
	}

	public UploadExistingDialog(final Component parent, Registry registry, File uploadFile) {
		super(JOptionPane.getFrameForComponent(parent), TITLE + title(registry), true);
		CreateUploadExistingDialog(parent, registry, null, uploadFile);
	}

	private void CreateUploadExistingDialog(final Component parent, Registry registry, SBOLDocument uploadDoc,
			File uploadFile) {
		this.parent = parent;
		this.registry = registry;
		this.toBeUploaded = uploadDoc;
		this.toBeUploadedFile = uploadFile;

		cancelButton.registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
				JComponent.WHEN_IN_FOCUSED_WINDOW);
		cancelButton.addActionListener(this);

		uploadButton.addActionListener(this);
		uploadButton.setEnabled(false);
		getRootPane().setDefaultButton(uploadButton);

		JPanel buttonPane = new JPanel();
		buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
		buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		buttonPane.add(Box.createHorizontalStrut(100));
		buttonPane.add(Box.createHorizontalGlue());
		buttonPane.add(cancelButton);
		buttonPane.add(uploadButton);

		// setup collections
		collections = new JList<IdentifiedMetadata>(setupListModel());
		collections.addListSelectionListener(this);
		collections.setCellRenderer(new MyListCellRenderer());
		collections.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		collections.setLayoutOrientation(JList.VERTICAL);
		collections.setVisibleRowCount(5);
		JScrollPane collectionsScroller = new JScrollPane(collections);
		collectionsScroller.setPreferredSize(new Dimension(50, 200));
		collectionsScroller.setAlignmentX(LEFT_ALIGNMENT);

		JPanel mainPanel = new JPanel();
		mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));
		mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		mainPanel.add(new JLabel("Collections"));
		mainPanel.add(collectionsScroller);
		mainPanel.add(new JLabel("Overwrite"));
		mainPanel.add(overwrite);

		Container contentPane = getContentPane();
		contentPane.add(info, BorderLayout.PAGE_START);
		contentPane.add(mainPanel, BorderLayout.CENTER);
		contentPane.add(buttonPane, BorderLayout.PAGE_END);
		((JComponent) contentPane).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

		pack();
		setLocationRelativeTo(parent);
		setVisible(true);
	}

	private class MyListCellRenderer extends DefaultListCellRenderer {
		@Override
		public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
				boolean cellHasFocus) {
			JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
			label.setOpaque(isSelected); // Highlight only when selected
			label.setText(((IdentifiedMetadata) value).getName());
			return label;
		}
	}

	private ListModel<IdentifiedMetadata> setupListModel() {
		SynBioHubFrontends frontends = new SynBioHubFrontends();
		SynBioHubFrontend frontend = null;
		if (frontends.hasFrontend(registry.getLocation())) {
			frontend = frontends.getFrontend(registry.getLocation());
		} else {
			frontend = toBeUploaded.addRegistry(registry.getLocation(), registry.getUriPrefix());
		}

		SearchQuery query = new SearchQuery();
		SearchCriteria crit = new SearchCriteria();
		crit.setKey("objectType");
		crit.setValue("Collection");
		query.addCriteria(crit);
		query.setLimit(10000);
		query.setOffset(0);
		List<IdentifiedMetadata> results;
		DefaultListModel<IdentifiedMetadata> model = new DefaultListModel<IdentifiedMetadata>();
		try {
			results = frontend.search(query);
		} catch (SynBioHubException e) {
			return model;
		}

		if (results.size() == 0) {
			return model;
		}

		for (IdentifiedMetadata collection : results) {
			// don't add collections that have "/public" in the URI.
			if (!collection.getUri().contains("/public/")) {
				model.addElement(collection);
			}
		}
		return model;
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == cancelButton) {
			setVisible(false);
			return;
		}

		if (e.getSource() == uploadButton) {
			try {
				uploadDesign();
				setVisible(false);
				return;
			} catch (SynBioHubException | IOException e1) {
				MessageDialog.showMessage(parent, "Uploading failed", Arrays.asList(e1.getMessage().split("\"|,")));
				if (toBeUploaded != null) {
					toBeUploaded.clearRegistries();
				}
			}
		}
	}

	private void uploadDesign() throws SynBioHubException, IOException {
		SynBioHubFrontends frontends = new SynBioHubFrontends();
		if (!frontends.hasFrontend(registry.getLocation())) {
			JOptionPane.showMessageDialog(parent,
					"Please login to " + registry.getLocation() + " in the Registry preferences menu.");
			return;
		}
		SynBioHubFrontend frontend = frontends.getFrontend(registry.getLocation());

		IdentifiedMetadata selectedCollection = collections.getSelectedValue();

		if (toBeUploaded != null) {
			frontend.addToCollection(URI.create(selectedCollection.getUri()), overwrite.isSelected(), toBeUploaded);
		} else {
			frontend.addToCollection(URI.create(selectedCollection.getUri()), overwrite.isSelected(), toBeUploadedFile);
		}

		JOptionPane.showMessageDialog(parent, "Upload successful!");
	}

	@Override
	public void valueChanged(ListSelectionEvent e) {
		uploadButton.setEnabled(!collections.isSelectionEmpty());
	}
}
 public class UploadNewDialog extends JDialog implements ActionListener, DocumentListener {
	private static final String TITLE = "Upload Design: ";

	private static String title(Registry registry) {
		String title = "";
		if (registry.getName() != null) {
			title = title + registry.getName();
		} else if (registry.getLocation() != null) {
			title = title + registry.getLocation();
		}
		return CharSequenceUtil.shorten(title, 20).toString();
	}

	private Component parent;
	private Registry registry;
	private SBOLDocument toBeUploaded;
	private File toBeUploadedFile;

	private final JTextArea info = new JTextArea(
			"SynBioHub organizes your uploads into collections. You can upload as many parts as you want into one collection, and then conveniently download them all together or share the collection as a whole with other people. If you've already made a collection, try again and select the Existing Collection option. If you haven't, fill out the form below to create a new one. If you're submitting the same thing twice because you've changed things, tick the overwrite box below to overwrite previous versions with what you're uploading now. If this is the first time you have made this submission, you can ignore this. (*) indicates a required field");
	private final JButton uploadButton = new JButton("Upload");
	private final JButton cancelButton = new JButton("Cancel");
	private final JTextField submissionId = new JTextField("");
	private final JTextField version = new JTextField("");
	private final JTextField name = new JTextField("");
	private final JTextField description = new JTextField("");
	private final JTextField citations = new JTextField("");
	private final JCheckBox overwrite = new JCheckBox("");

	public UploadNewDialog(final Component parent, Registry registry, File toBeUploadedFile) {
		super(JOptionPane.getFrameForComponent(parent), TITLE + title(registry), true);
		CreateUploadNewDialog(parent, registry, null, toBeUploadedFile);
	}

	public UploadNewDialog(final Component parent, Registry registry, SBOLDocument toBeUploaded) {
		super(JOptionPane.getFrameForComponent(parent), TITLE + title(registry), true);
		CreateUploadNewDialog(parent, registry, toBeUploaded, null);
	}

	private void CreateUploadNewDialog(final Component parent, Registry registry, SBOLDocument toBeUploaded,
			File toBeUploadedFile) {
		this.parent = parent;
		this.registry = registry;
		this.toBeUploaded = toBeUploaded;
		this.toBeUploadedFile = toBeUploadedFile;

		this.setMinimumSize(new Dimension(800, 500));

		// set default values
		PersonInfo userInfo = SBOLEditorPreferences.INSTANCE.getUserInfo();
		String email = userInfo == null || userInfo.getEmail() == null ? null : userInfo.getEmail().getLocalName();
		String uri = userInfo == null ? null : userInfo.getURI().stringValue();
		if (email == null || email.equals("") || uri == null) {
			JOptionPane.showMessageDialog(parent,
					"Make sure your email and URI/namespace are both set and valid in preferences.", "Upload failed",
					JOptionPane.ERROR_MESSAGE);
			return;
		}

		info.setWrapStyleWord(true);
		info.setLineWrap(true);
		info.setEditable(false);
		info.setOpaque(false);
		info.setHighlighter(null);
		if (toBeUploaded != null) {
			ComponentDefinition root = toBeUploaded.getRootComponentDefinitions().iterator().next();
			submissionId.setText(root.getDisplayId());
			name.setText(root.isSetName() ? root.getName() : root.getDisplayId());
			description.setText(root.isSetDescription() ? root.getDescription() : "");
		}
		version.setText("1");
		overwrite.setSelected(false);

		cancelButton.registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
				JComponent.WHEN_IN_FOCUSED_WINDOW);
		cancelButton.addActionListener(this);

		uploadButton.addActionListener(this);
		uploadButton.setEnabled(false);
		getRootPane().setDefaultButton(uploadButton);

		JPanel buttonPane = new JPanel();
		buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
		buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		buttonPane.add(Box.createHorizontalStrut(100));
		buttonPane.add(Box.createHorizontalGlue());
		buttonPane.add(cancelButton);
		buttonPane.add(uploadButton);

		JPanel panel = initMainPanel();

		Container contentPane = getContentPane();
		contentPane.add(info, BorderLayout.PAGE_START);
		contentPane.add(panel, BorderLayout.CENTER);
		contentPane.add(buttonPane, BorderLayout.PAGE_END);
		((JComponent) contentPane).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

		enableUpload();

		pack();
		setLocationRelativeTo(parent);
		setVisible(true);
	}

	private JPanel initMainPanel() {
		submissionId.getDocument().addDocumentListener(this);
		version.getDocument().addDocumentListener(this);
		name.getDocument().addDocumentListener(this);
		description.getDocument().addDocumentListener(this);
		citations.getDocument().addDocumentListener(this);

		FormBuilder builder = new FormBuilder();
		builder.add("", new JLabel(" "));
		builder.add("Collection ID *", submissionId);
		builder.add("Collection Version *", version);
		builder.add("Collection Name *", name);
		builder.add("Collection Description *", description);
		builder.add("Citations (optional)", citations);
		builder.add("Overwrite", overwrite);
		JPanel panel = builder.build();
		panel.setAlignmentX(LEFT_ALIGNMENT);

		JPanel mainPanel = new JPanel();
		mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.PAGE_AXIS));
		mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		mainPanel.add(panel);

		return mainPanel;
	}

	@Override
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == cancelButton) {
			setVisible(false);
			return;
		}

		if (e.getSource() == uploadButton) {
			try {
				uploadDesign();
				setVisible(false);
				return;
			} catch (SynBioHubException | IOException e1) {
				MessageDialog.showMessage(parent, "Uploading failed", Arrays.asList(e1.getMessage().split("\"|,")));
				if (toBeUploaded != null) {
					toBeUploaded.clearRegistries();
				}
			}
		}
	}

	private void uploadDesign() throws SynBioHubException, IOException {
		SynBioHubFrontends frontends = new SynBioHubFrontends();
		if (!frontends.hasFrontend(registry.getLocation())) {
			JOptionPane.showMessageDialog(parent,
					"Please login to " + registry.getLocation() + " in the Registry preferences menu.");
			return;
		}
		SynBioHubFrontend frontend = frontends.getFrontend(registry.getLocation());

		if (toBeUploaded != null) {
			frontend.createCollection(submissionId.getText(), version.getText(), name.getText(), description.getText(),
					citations.getText(), overwrite.isSelected(), toBeUploaded);
		} else {
			frontend.createCollection(submissionId.getText(), version.getText(), name.getText(), description.getText(),
					citations.getText(), overwrite.isSelected(), toBeUploadedFile);
		}

		JOptionPane.showMessageDialog(parent, "Upload successful!");
	}

	@Override
	public void insertUpdate(DocumentEvent e) {
		enableUpload();
	}

	@Override
	public void removeUpdate(DocumentEvent e) {
		enableUpload();
	}

	@Override
	public void changedUpdate(DocumentEvent e) {
		enableUpload();
	}

	private void enableUpload() {
		boolean shouldEnable = !submissionId.getText().equals("") && !version.getText().equals("")
				&& !name.getText().equals("") && !description.getText().equals("");
		uploadButton.setEnabled(shouldEnable);
	}
} public class VariantEditor extends JDialog implements ActionListener {
	private static final String TITLE = "Combinatorial Design Variants: ";

	private ComponentDefinition derivationCD;
	private ComponentDefinition variableCD;
	private SBOLDocument design;
	private Component parent;
	private final JComboBox<OperatorType> operatorSelection = new JComboBox<>(OperatorType.values());
	private final JComboBox<Strategy> strategySelection = new JComboBox<>(getStrategies());
	private final JButton addButton = new JButton("Add Variant");
	private final JButton removeButton = new JButton("Remove");
	private final JButton newButton = new JButton("Add new Combinatorial Derivation");
	private final JButton saveButton = new JButton("Save");
	private final JTextField displayId = new JTextField();
	private final JTextField name = new JTextField();
	private final JTextField description = new JTextField();
	private JTable table;
	private JLabel tableLabel;
	private JScrollPane scroller;

	private class Strategy {
		StrategyType type;

		public Strategy(StrategyType type) {
			this.type = type;
		}

		@Override
		public String toString() {
			if (type == null) {
				return "None";
			}

			return type.toString();
		}
	}

	private Strategy[] getStrategies() {
		StrategyType[] values = StrategyType.values();

		Strategy[] a = new Strategy[values.length + 1];
		a[0] = new Strategy(null);

		for (int i = 0; i < values.length; i++) {
			a[i + 1] = new Strategy(values[i]);
		}

		return a;
	}

	private static String title(Identified comp) {
		String title = comp.getDisplayId();
		if (title == null) {
			title = comp.getName();
		}
		if (title == null) {
			URI uri = comp.getIdentity();
			title = (uri == null) ? null : uri.toString();
		}

		return (title == null) ? "" : CharSequenceUtil.shorten(title, 20).toString();
	}

	public VariantEditor(Component parent, ComponentDefinition derivationCD, ComponentDefinition variableCD,
			SBOLDocument design) {
		super(JOptionPane.getFrameForComponent(parent), TITLE + title(variableCD), true);
		this.derivationCD = derivationCD;
		this.variableCD = variableCD;
		this.parent = parent;
		this.design = design;

		try {
			CombinatorialDerivation derivation = getCombinatorialDerivation(derivationCD);
			if (derivation == null) {
				setVisible(false);
				return;
			}

			operatorSelection.setSelectedItem(getOperator());
			operatorSelection.addActionListener(this);

			strategySelection
					.setSelectedIndex(getStrategy() == null ? 0 : getStrategy() == StrategyType.ENUMERATE ? 1 : 2);
			strategySelection.addActionListener(this);

			displayId.setText(derivation.getDisplayId());
			name.setText(derivation.getName());
			description.setText(derivation.getDescription());
			displayId.setEditable(false);

			FormBuilder builder = new FormBuilder();
			builder.add("Variant operator", operatorSelection);
			builder.add("Derivation strategy", strategySelection);
			builder.add("Derivation display ID", displayId);
			builder.add("Derivation name", name);
			builder.add("Derivation description", description);
			JPanel optionPane = builder.build();

			addButton.addActionListener(this);
			addButton.setEnabled(true);
			removeButton.addActionListener(this);
			removeButton.setEnabled(false);
			newButton.addActionListener(this);
			newButton.setEnabled(true);
			saveButton.registerKeyboardAction(this, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
					JComponent.WHEN_IN_FOCUSED_WINDOW);
			saveButton.addActionListener(this);
			getRootPane().setDefaultButton(saveButton);

			JPanel buttonPane = new JPanel();
			buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
			buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
			buttonPane.add(addButton);
			buttonPane.add(removeButton);
			buttonPane.add(newButton);
			buttonPane.add(Box.createHorizontalStrut(100));
			buttonPane.add(Box.createHorizontalGlue());
			buttonPane.add(saveButton);

			JPanel tablePane = initMainPanel();

			Container contentPane = getContentPane();
			contentPane.add(optionPane, BorderLayout.PAGE_START);
			contentPane.add(tablePane, BorderLayout.CENTER);
			contentPane.add(buttonPane, BorderLayout.PAGE_END);
			((JComponent) contentPane).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

			pack();
			setLocationRelativeTo(parent);
			this.setVisible(true);
		} catch (SBOLValidationException e) {
			e.printStackTrace();
		}
	}

	private StrategyType getStrategy() throws SBOLValidationException {
		CombinatorialDerivation derivation = getCombinatorialDerivation(derivationCD);
		if (derivation == null || !derivation.isSetStrategy()) {
			return null;
		}

		return derivation.getStrategy();
	}

	private OperatorType getOperator() throws SBOLValidationException {
		VariableComponent variable = getVariableComponent();
		if (variable == null) {
			return OperatorType.ONE;
		}

		return variable.getOperator();
	}

	protected JPanel initMainPanel() throws SBOLValidationException {
		//ComponentDefinitionTableModel tableModel = new ComponentDefinitionTableModel(getVariants());
		TopLevelTableModel tableModel = new TopLevelTableModel(getVariantsCollectionsAndDerivations());
		JPanel panel = createTablePanel(tableModel, "Variant count (" + tableModel.getRowCount() + ")");

		table = (JTable) panel.getClientProperty("table");
		tableLabel = (JLabel) panel.getClientProperty("label");
		scroller = (JScrollPane) panel.getClientProperty("scroller");

		return panel;
	}

	protected JPanel createTablePanel(AbstractListTableModel<?> tableModel, String title) {
		final JTable table = new JTable(tableModel);
		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			@Override
			public void valueChanged(ListSelectionEvent event) {
				removeButton.setEnabled(table.getSelectedRow() >= 0);
			}
		});

		setWidthAsPercentages(table, tableModel.getWidths());

		TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel);
		table.setRowSorter(sorter);

		table.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				handleTableClick(e);
			}
		});

		JScrollPane tableScroller = new JScrollPane(table);
		tableScroller.setPreferredSize(new Dimension(850, 200));
		tableScroller.setAlignmentX(LEFT_ALIGNMENT);

		JLabel tableLabel = new JLabel(title);
		tableLabel.setLabelFor(table);

		JPanel tablePane = new JPanel();
		tablePane.setLayout(new BoxLayout(tablePane, BoxLayout.PAGE_AXIS));
		tablePane.add(tableLabel);
		tablePane.add(Box.createRigidArea(new Dimension(0, 5)));
		tablePane.add(tableScroller);

		tablePane.putClientProperty("table", table);
		tablePane.putClientProperty("scroller", tableScroller);
		tablePane.putClientProperty("label", tableLabel);

		return tablePane;
	}

	protected static void setWidthAsPercentages(JTable table, double... percentages) {
		final double factor = 10000;

		TableColumnModel model = table.getColumnModel();
		for (int columnIndex = 0; columnIndex < percentages.length; columnIndex++) {
			TableColumn column = model.getColumn(columnIndex);
			column.setPreferredWidth((int) (percentages[columnIndex] * factor));
		}
	}

	private void handleTableClick(MouseEvent e) {
		if (e.getClickCount() == 2 && table.getSelectedRow() >= 0) {
			int row = table.convertRowIndexToModel(table.getSelectedRow());
			ComponentDefinition variant = ((ComponentDefinitionTableModel) table.getModel()).getElement(row);

			try {
				Desktop.getDesktop().browse(variant.getIdentity());
			} catch (IOException e1) {
				MessageDialog.showMessage(parent, "The URI could not be opened: ", e1.getMessage());
			}
		}
	}

	private CombinatorialDerivation chosenDerivation = null;

	private CombinatorialDerivation getCombinatorialDerivation(ComponentDefinition derivationCD)
			throws SBOLValidationException {
		if (chosenDerivation == null) {
			chosenDerivation = CombinatorialDerivationInputDialog.pickCombinatorialDerivation(parent, design, derivationCD);
			//chosenDerivation = new ComboDerivDesign();
			if (chosenDerivation == null) {
				String id = JOptionPane.showInputDialog("What would you like to call this combinatorial derivation?",
						derivationCD.isSetDisplayId() ? derivationCD.getDisplayId() + "_CombinatorialDerivation" : "");
				if (id == null) {
					return null;
				}
				chosenDerivation = createCombinatorialDerivation(derivationCD, id);
			}
		}

		return chosenDerivation;
	}

	private VariableComponent getVariableComponent(OperatorType operator) throws Exception {
		CombinatorialDerivation derivation = getCombinatorialDerivation(derivationCD);
		if (derivation == null) {
			return null;
		}

		org.sbolstandard.core2.Component link = getComponentLink(derivationCD, variableCD);
		if (link == null) {
			throw new Exception("derivationCD does not have a component to variableCD");
		}

		VariableComponent variable = getVariableComponent(derivation, link);
		if (variable == null) {
			variable = createVariableComponent(derivation, operator, link);
		}

		return variable;
	}

	private VariableComponent getVariableComponent() throws SBOLValidationException {
		CombinatorialDerivation derivation = getCombinatorialDerivation(derivationCD);
		if (derivation == null) {
			return null;
		}

		org.sbolstandard.core2.Component link = getComponentLink(derivationCD, variableCD);
		if (link == null) {
			return null;
		}

		VariableComponent variable = getVariableComponent(derivation, link);
		if (variable == null) {
			return null;
		}

		return variable;
	}

	private VariableComponent getVariableComponent(CombinatorialDerivation derivation,
			org.sbolstandard.core2.Component link) {
		for (VariableComponent variable : derivation.getVariableComponents()) {
			if (variable.getVariable().equals(link)) {
				return variable;
			}
		}

		return null;
	}

	private org.sbolstandard.core2.Component getComponentLink(ComponentDefinition derivationCD,
			ComponentDefinition variableCD) {
		for (org.sbolstandard.core2.Component link : derivationCD.getComponents()) {
			if (link.getDefinitionURI().equals(variableCD.getIdentity())) {
				return link;
			}
		}

		return null;
	}
	
	private List<TopLevel> getVariantsCollectionsAndDerivations() throws SBOLValidationException {
		ArrayList<TopLevel> variants = new ArrayList<>();

		VariableComponent variable = getVariableComponent();
		if (variable == null) {
			return variants;
		}

		//Get variants
		for (URI cd : variable.getVariantURIs()) {
			variants.add(design.getComponentDefinition(cd));
		}
		
		//Get variantCollections
		for (URI col : variable.getVariantCollectionURIs()) {
			variants.add(design.getCollection(col));
		}
		
		//Get combinatoralDerivations
		for (URI der : variable.getVariantDerivationURIs()) {
			variants.add(design.getCombinatorialDerivation(der));
		}

		return variants;
	}

	private void addVariant(ComponentDefinition variant) throws Exception {
		VariableComponent variable = getVariableComponent((OperatorType) operatorSelection.getSelectedItem());
		variable.addVariant(variant.getIdentity());
	}
	
	private void addCollection(Collection collection) throws Exception {
		VariableComponent variable = getVariableComponent((OperatorType) operatorSelection.getSelectedItem());
		variable.addVariantCollection(collection.getIdentity());
	}
	
	private void addDerivation(CombinatorialDerivation derivation) throws Exception {
		VariableComponent variable = getVariableComponent((OperatorType) operatorSelection.getSelectedItem());
		variable.addVariantDerivation(derivation.getIdentity());
	}

	private VariableComponent createVariableComponent(CombinatorialDerivation derivation, OperatorType operator,
			org.sbolstandard.core2.Component link) throws SBOLValidationException {
		String uniqueId = SBOLUtils.getUniqueDisplayId(null, derivation, link.getDisplayId() + "_VariableComponent",
				null, "VariableComponent", design);
		return derivation.createVariableComponent(uniqueId, operator, link.getIdentity());
	}

	private CombinatorialDerivation createCombinatorialDerivation(ComponentDefinition derivationCD, String displayId)
			throws SBOLValidationException {
		String uniqueId = SBOLUtils.getUniqueDisplayId(null, null, displayId, derivationCD.getVersion(),
				"CombinatorialDerivation", design);
		CombinatorialDerivation derivation = design.createCombinatorialDerivation(uniqueId, "1",
				derivationCD.getIdentity());

		StrategyType strategy = ((Strategy) strategySelection.getSelectedItem()).type;
		if (strategy != null) {
			derivation.setStrategy(strategy);
		}

		addAsNestedDerivation(derivationCD, derivation);
		addNestedDerivations(variableCD);

		this.displayId.setText(displayId);

		chosenDerivation = derivation;

		return derivation;
	}

	private void addAsNestedDerivation(ComponentDefinition derivationCD, CombinatorialDerivation derivation)
			throws SBOLValidationException {
		for (ComponentDefinition parentCD : design.getComponentDefinitions()) {
			for (org.sbolstandard.core2.Component link : parentCD.getComponents()) {
				if (link.getDefinition().equals(derivationCD)) {
					CombinatorialDerivation parentDerivation = getCombinatorialDerivation(parentCD);

					if (parentDerivation != null) {
						VariableComponent variable = getVariableComponent(parentDerivation, link);

						if (variable == null) {
							variable = createVariableComponent(parentDerivation, OperatorType.ONE, link);
						}

						if (!variable.getVariantDerivations().contains(derivation)) {
							variable.addVariantDerivation(derivation.getIdentity());
						}
					}
				}
			}
		}
	}

	private void addNestedDerivations(ComponentDefinition variableCD) throws SBOLValidationException {
		VariableComponent variable = getVariableComponent();
		if (variable == null) {
			return;
		}

		for (CombinatorialDerivation childDerivation : design.getCombinatorialDerivations()) {
			if (childDerivation.getTemplate().equals(variableCD)
					&& !variable.getVariantDerivations().contains(childDerivation)) {
				variable.addVariantDerivation(childDerivation.getIdentity());
			}
		}
	}

	private void removeVariant(TopLevel top) throws Exception {
		VariableComponent variable = getVariableComponent();
		if (variable == null) {
			return;
		}
		if(top instanceof ComponentDefinition)
		{
			ComponentDefinition variant = (ComponentDefinition) top;
			variable.removeVariant(variant);

			// TODO: pull into a function and call on all three types of removes
			if (variable.getVariants().isEmpty()) { // TODO: should check variantCollections / variantDerivations
				// TODO: this call should not create when it cannot find
				// also need to see why it cannot find it
				CombinatorialDerivation derivation = getCombinatorialDerivation(derivationCD);
				if (derivation == null) {
					return;
				}

				derivation.removeVariableComponent(variable);

//				if (derivation.getVariableComponents().isEmpty()) {
//					design.removeCombinatorialDerivation(derivation);
//					chosenDerivation = null;
//				}
			}
		}else if (top instanceof CombinatorialDerivation)
		{
			variable.removeVariantDerivation((CombinatorialDerivation)top);
			// TODO: remove variableComponent if last variant
		}else if(top instanceof Collection)
		{
			variable.removeVariantCollection((Collection) top);
			// TODO: remove variableComponent if last variant
		}

	}

	@Override
	public void actionPerformed(ActionEvent e) {
		try {
			if (e.getSource() == operatorSelection) {
				setOperator((OperatorType) operatorSelection.getSelectedItem());
				return;
			}

			if (e.getSource() == strategySelection) {
				setStrategy(((Strategy) strategySelection.getSelectedItem()).type);
				return;
			}

			if (e.getSource() == saveButton) {
				if (chosenDerivation != null) {
					if (!name.getText().equals("")) {
						chosenDerivation.setName(name.getText());
					}
					if (!description.getText().equals("")) {
						chosenDerivation.setDescription(description.getText());
					}
				}

				setVisible(false);
				return;
			}

			if (e.getSource() == addButton) {
				ComponentDefinitionBox root = new ComponentDefinitionBox();
				RegistryInputDialog dialog = new RegistryInputDialog(parent, root, Parts.forIdentified(variableCD),
						SBOLUtils.Types.DNA, null, null);
				dialog.allowCollectionSelection();
				dialog.setObjectType("Variant");
				SBOLDocument selection = dialog.getInput();
				if (selection == null) {
					return;
				}
				URI selectedURI = dialog.getSelectedURI();
				
				
				TopLevel selectedTopLevel = selection.getTopLevel(selectedURI);
				SBOLUtils.insertTopLevels(selection, design);
				Collection col;
				if(selectedTopLevel instanceof Collection) 
				{
					col = selection.getCollections().iterator().next();
					boolean cont = true;
					while(cont)
					{
						cont = false;
						for(Collection c : selection.getCollections())
						{
							if(col != c)
							{
								if(c.containsMember(col.getIdentity()))
								{
									col = c;
									cont = true;
								}
							}
						}
					}
					addCollection(col);
				} else if(selectedTopLevel instanceof CombinatorialDerivation) {
					addDerivation(selection.getCombinatorialDerivations().iterator().next());
				} else if(selectedTopLevel instanceof ComponentDefinition) {
					addVariant(root.cd);
				} else {
					System.err.println("ERROR: Variant is not a valid type (" + selectedTopLevel.getClass() + ")");
				}

				updateTable();
				return;
			}

			if (e.getSource() == removeButton) {
				int row = table.convertRowIndexToModel(table.getSelectedRow());
				TopLevelTableModel model = (TopLevelTableModel) table.getModel();
				TopLevel top = model.getElement(row);
				removeVariant(top);
				updateTable();
				return;
			}

			if (e.getSource() == newButton) {
				addCombinatorialDerivation();
				return;
			}
		} catch (Exception e1) {
			e1.printStackTrace();
		}
	}

	private void setStrategy(StrategyType strategy) {
		try {
			CombinatorialDerivation derivation = getCombinatorialDerivation(derivationCD);
			if (derivation == null) {
				return;
			}

			if (strategy == null) {
				derivation.unsetStrategy();
			} else {
				derivation.setStrategy(strategy);
			}
		} catch (SBOLValidationException e) {
			JOptionPane.showMessageDialog(parent, "Strategy cannot be enumerate when operators are *orMore");
			e.printStackTrace();
			setVisible(false);
		}
	}

	private void setOperator(OperatorType operator) {
		try {
			VariableComponent variable = getVariableComponent(operator);
			variable.setOperator(operator);
		} catch (Exception e) {
			JOptionPane.showMessageDialog(parent, "Operator cannot be *orMore when strategy is enumerate");
			e.printStackTrace();
			setVisible(false);
		}
	}

	private void addCombinatorialDerivation() throws SBOLValidationException {
		String displayId = JOptionPane.showInputDialog("What would you like the Display ID to be?");
		if (displayId == null) {
			return;
		}

		CombinatorialDerivation newDerivation = createCombinatorialDerivation(derivationCD, displayId);

		JOptionPane.showMessageDialog(parent,
				"A new CombinatorialDerivation was created: " + newDerivation.getDisplayId());

		setVisible(false);
	}

	private void updateTable() throws SBOLValidationException {
		TopLevelTableModel tableModel = new TopLevelTableModel(getVariantsCollectionsAndDerivations());
		table.setModel(tableModel);
		setWidthAsPercentages(table, tableModel.getWidths());
		TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel);
		table.setRowSorter(sorter);
	}

}
 public class DesignChangedEvent {
	private final SBOLDesign design;

	public DesignChangedEvent(SBOLDesign design) {
		this.design = design;
	}

	public SBOLDesign getDesign() {
		return design;
	}
}
 public class DesignLoadedEvent {
	private final SBOLDesign design;

	public DesignLoadedEvent(SBOLDesign design) {
		this.design = design;
	}

	public SBOLDesign getDesign() {
		return design;
	}
}
 public class FocusInEvent {
	private final SBOLDesign design;
	private final ComponentDefinition component;
	private final BufferedImage snapshot;

	public FocusInEvent(SBOLDesign design, ComponentDefinition component, BufferedImage snapshot) {
		this.design = design;
		this.component = component;
		this.snapshot = snapshot;
	}

	public SBOLDesign getDesign() {
		return design;
	}
	
	public ComponentDefinition getComponent() {
		return component;
	}

	public BufferedImage getSnapshot() {
		return snapshot;
	}
}
 public class FocusOutEvent {
	private final SBOLDesign design;
	private final ComponentDefinition component;

	public FocusOutEvent(SBOLDesign design, ComponentDefinition component) {
		this.design = design;
		this.component = component;
	}

	public SBOLDesign getDesign() {
		return design;
	}
	
	public ComponentDefinition getComponent() {
		return component;
	}
}
 public class PartVisibilityChangedEvent {
	private final Part part;
	private final boolean isVisible;

	public PartVisibilityChangedEvent(Part part, boolean isVisible) {
		super();
		this.part = part;
		this.isVisible = isVisible;
	}

	public Part getPart() {
		return part;
	}

	public boolean isVisible() {
		return isVisible;
	}
}
 public class SelectionChangedEvent {
	private final ComponentDefinition component;

	public SelectionChangedEvent(ComponentDefinition component) {
		this.component = component;
	}

	public ComponentDefinition getSelected() {
		return component;
	}
}

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceAdapter;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import org.apache.commons.httpclient.URIException;
import org.sbolstandard.core2.AccessType;
import org.sbolstandard.core2.CombinatorialDerivation;
import org.sbolstandard.core2.ComponentDefinition;
import org.sbolstandard.core2.Cut;
import org.sbolstandard.core2.Identified;
import org.sbolstandard.core2.Location;
import org.sbolstandard.core2.OrientationType;
import org.sbolstandard.core2.Range;
import org.sbolstandard.core2.RestrictionType;
import org.sbolstandard.core2.SBOLConversionException;
import org.sbolstandard.core2.SBOLDocument;
import org.sbolstandard.core2.SBOLValidate;
import org.sbolstandard.core2.SBOLValidationException;
import org.sbolstandard.core2.SBOLWriter;
import org.sbolstandard.core2.Sequence;
import org.sbolstandard.core2.SequenceAnnotation;
import org.sbolstandard.core2.SequenceOntology;
import org.sbolstandard.core2.TopLevel;
import org.sbolstandard.core2.VariableComponent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.synbiohub.frontend.SynBioHubException;
import org.synbiohub.frontend.SynBioHubFrontend;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
import edu.utah.ece.async.sboldesigner.sbol.CombinatorialExpansionUtil;
import edu.utah.ece.async.sboldesigner.sbol.ProvenanceUtil;
import edu.utah.ece.async.sboldesigner.sbol.SBOLUtils;
import edu.utah.ece.async.sboldesigner.sbol.SBOLUtils.Types;
import edu.utah.ece.async.sboldesigner.sbol.editor.dialog.ComponentDefinitionBox;
import edu.utah.ece.async.sboldesigner.sbol.editor.dialog.MessageDialog;
import edu.utah.ece.async.sboldesigner.sbol.editor.dialog.PartEditDialog;
import edu.utah.ece.async.sboldesigner.sbol.editor.dialog.RegistryInputDialog;
import edu.utah.ece.async.sboldesigner.sbol.editor.dialog.RegistryLoginDialog;
import edu.utah.ece.async.sboldesigner.sbol.editor.dialog.RootInputDialog;
import edu.utah.ece.async.sboldesigner.sbol.editor.dialog.UploadExistingDialog;
import edu.utah.ece.async.sboldesigner.sbol.editor.dialog.UploadNewDialog;
import edu.utah.ece.async.sboldesigner.sbol.editor.dialog.VariantEditor;
import edu.utah.ece.async.sboldesigner.sbol.editor.event.DesignChangedEvent;
import edu.utah.ece.async.sboldesigner.sbol.editor.event.DesignLoadedEvent;
import edu.utah.ece.async.sboldesigner.sbol.editor.event.FocusInEvent;
import edu.utah.ece.async.sboldesigner.sbol.editor.event.FocusOutEvent;
import edu.utah.ece.async.sboldesigner.sbol.editor.event.PartVisibilityChangedEvent;
import edu.utah.ece.async.sboldesigner.sbol.editor.event.SelectionChangedEvent;

		setPartVisible(Parts.SCAR, true);
	}

	public void editCanvasCD() throws SBOLValidationException {
		confirmEditable();
		if(!features.isEmpty()) {
			PartEditDialog.editPart(panel.getParent(), parentCDs.peekFirst(), getCanvasCD(), false, false, design, false);
		}else {
			ComponentDefinition comp = getCanvasCD();
			URI originalIdentity = comp.getIdentity();
			comp = PartEditDialog.editPart(panel.getParent(), parentCDs.peekFirst(), comp, false, true, design, false);
			if (comp != null) {
				if (!originalIdentity.equals(comp.getIdentity())) {
					try {
						updateComponentReferences(originalIdentity, comp.getIdentity(), null);
					} catch (URISyntaxException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				load(comp);
				fireDesignChangedEvent(true);
			}
		}
	}

	/**
	 * Looks through all the components and updates all references from
	 * originalIdentity to identity
	 * @throws URISyntaxException 
	 */
	private void updateComponentReferences(URI originalIdentity, URI newIdentity, ComponentDefinition original) throws SBOLValidationException, URISyntaxException {
		ComponentDefinition CD = getParentCD();
		if(CD == null) {
			return;
		}
		ComponentDefinition newCD = CD;
		if(SBOLUtils.notInNamespace(CD)) {
			newCD = (ComponentDefinition) design.rename(CD, SBOLEditorPreferences.INSTANCE.getUserInfo().getURI().toString(), CD.getDisplayId(), CD.getVersion());
		}
		for (org.sbolstandard.core2.Component comp : newCD.getComponents()) {		
 			if (comp.getDefinitionIdentity().equals(originalIdentity)) {		
 				comp.setDefinition(newIdentity);		
 			}		
 		}
		if(SBOLUtils.notInNamespace(CD)) {
			parentCDs.pop();
			updateComponentReferences(CD.getIdentity(), newCD.getIdentity(), null);
			parentCDs.push(newCD);
		}
		
	}
	
	/**
	 * This Is the method that should be called to auto update the component references for the user when editing a part that is not owned. 
	 * @param originalIdentity
	 * @param newIdentity
	 * @throws SBOLValidationException
	 * @throws URISyntaxException
	 */
	private URI autoUpdateComponentReferences(Component parent, ComponentDefinition parentCD, 
			ComponentDefinition CD, boolean openEditor) throws SBOLValidationException, URISyntaxException{
		URI originalIdentity = CD.getIdentity();
		URI newIdentity;
		if (openEditor) {
			newIdentity = PartEditDialog.editPart(parent, parentCD, CD, false, true, design, false).getIdentity();
		}else {
			ComponentDefinition cd = (ComponentDefinition) design.rename(CD,
					SBOLEditorPreferences.INSTANCE.getUserInfo().getURI().toString(), CD.getDisplayId(),
					CD.getVersion());
			newIdentity = cd.getIdentity();
		}
		if (CD != null) {
			try {
				updateComponentReferences(originalIdentity, newIdentity, CD);
			} catch (URISyntaxException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			ComponentDefinition newParentCD = getParentCD();
			load(newParentCD);
			fireDesignChangedEvent(false);
			return newIdentity;
		}
		return null;
	}

	public void editSelectedCD() throws SBOLValidationException, URISyntaxException {
		if(selectedElement.isFeature()) {
			PartEditDialog.editPart(panel.getParent(), parentCDs.peekFirst(), getCanvasCD(), false, false, design, false);
		}else {
			focusIn();
			editCanvasCD();
			focusOut();
			fireDesignChangedEvent(true);
		}
	}

	public void findPartForSelectedCD() throws Exception {
		Part part = selectedElement.getPart();
		ComponentDefinition selectedCD = selectedElement.getCD();
		URI role = selectedCD.getRoles().iterator().next();
		Types type = SBOLUtils
				.convertURIsToType(new HashSet<URI>(Arrays.asList(selectedCD.getTypes().iterator().next())));

		ComponentDefinitionBox root = new ComponentDefinitionBox();
		SBOLDocument selection = new RegistryInputDialog(panel.getParent(), root, part, type, role).getInput();

		if (selection != null) {
			SBOLUtils.insertTopLevels(selection, design);
			if (!confirmEditable()) {
				return;
			}
			replaceCD(selectedElement.getCD(), root.cd);
		}
	}

	private void editVariants() throws SBOLValidationException {
		int index = getElementIndex(getSelectedCD());
		new VariantEditor(panel, getCanvasCD(), getSelectedCD(), design);