// // Merger.java // /* OME Metadata Editor application for exploration and editing of OME-XML and OME-TIFF metadata. Copyright (C) 2006-@year@ Christopher Peterson. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package loci.ome.editor; import java.io.File; import java.util.Vector; import javax.swing.JComponent; import javax.swing.JOptionPane; import javax.xml.parsers.DocumentBuilderFactory; import ome.xml.OMEXMLNode; import org.openmicroscopy.xml.OMENode; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * <dl><dt><b>Source code:</b></dt> * <dd><a href="http://trac.openmicroscopy.org.uk/ome/browser/bioformats.git/components/legacy/ome-editor/src/loci/ome/editor/Merger.java">Trac</a>, * <a href="http://git.openmicroscopy.org/?p=bioformats.git;a=blob;f=components/legacy/ome-editor/src/loci/ome/editor/Merger.java;hb=HEAD">Gitweb</a></dd></dl> * * @author Christopher Peterson crpeterson2 at wisc.edu */ public class Merger { // --Constants-- /** Factory for generating document builders. */ public static final DocumentBuilderFactory DOC_FACT = DocumentBuilderFactory.newInstance(); /** Different mode constants.*/ public static final int ALL_ORIGINAL = 0x01; public static final int ALL_COMPANION = 0x02; public static final int ORIGINAL_OVER = 0x04; public static final int COMPANION_OVER = 0x08; // --Members-- protected OMENode ome, compOme, finalOme; protected int mode; private JComponent comp; public Merger(OMENode originalOme, File compFile, JComponent c) { ome = originalOme; comp = c; try { compOme = new OMENode(compFile); } catch (Exception exc) { exc.printStackTrace();} prompt(); merge(); } public OMENode getRoot() { return finalOme; } private void merge() { if(mode == ALL_ORIGINAL) finalOme = ome; else if(mode == ALL_COMPANION) finalOme = compOme; else if(mode == ORIGINAL_OVER) { finalOme = merge(ome,compOme); } else if(mode == COMPANION_OVER) { finalOme = merge(compOme,ome); } } /** * Merge two OME-XML Trees, when a conflict arrises, use the * "over" tree's node instead of the "under" tree's. NB: the * trees should be in OMECA (flattened) format before passing * them to this method. * @param over The OMENode that by default has higher priority. * @param under The OMENode that by default has lower priority. */ public static OMENode merge(OMENode over, OMENode under) { OMENode result; OMEXMLNode tempNode = merge((OMEXMLNode)over,(OMEXMLNode)under); if (tempNode instanceof OMENode) result = (OMENode) tempNode; else result = null; return result; } private static OMEXMLNode merge(OMEXMLNode over, OMEXMLNode under) { OMEXMLNode result = over; Vector overList = result.getChildNodes(); Vector underList = under.getChildNodes(); Vector idList = new Vector(); boolean isOverCustom = false; boolean isUnderCustom = false; boolean addedCustom = false; for(int i = 0;i<overList.size();i++) { OMEXMLNode overNode = (OMEXMLNode)(overList.get(i)); String overID = overNode.getAttribute("ID"); if (overID == null) isOverCustom = true; for(int j = 0;j<underList.size();j++) { OMEXMLNode underNode = (OMEXMLNode)(underList.get(j)); String underID = underNode.getAttribute("ID"); if (underID == null) isUnderCustom = true; if(isOverCustom && !isUnderCustom) { //do nothing to alter custom tree isOverCustom = false; } else if(!isOverCustom && isUnderCustom && !addedCustom) { result.getDOMElement().appendChild(createClone( underNode.getDOMElement(),overNode.getDOMElement(). getOwnerDocument())); addedCustom = true; isUnderCustom = false; } else if (!isOverCustom && !isUnderCustom) { if (underID.equals(overID)) merge(overNode,underNode); else { if(idList.indexOf(underID) > -1) { result.getDOMElement().appendChild(createClone( underNode.getDOMElement(),overNode.getDOMElement(). getOwnerDocument())); idList.add(underID); } } } } } return result; } public static Element createClone(Element el, Document doc) { String tagName = el.getTagName(); Element clone = doc.createElement(tagName); if(el.hasAttributes()) { NamedNodeMap map = el.getAttributes(); for(int i = 0;i<map.getLength();i++) { Node thisAttr = map.item(i); String attrName = thisAttr.getNodeName(); String attrValue = thisAttr.getNodeValue(); clone.setAttribute(attrName,attrValue); } } if(el.hasChildNodes()) { NodeList nodes = el.getChildNodes(); for(int i = 0;i<nodes.getLength();i++) { Node thisNode = nodes.item(i); if(thisNode instanceof Element) { Element origChild = (Element) thisNode; Element cloneChild = createClone(origChild,doc); clone.appendChild(cloneChild); } } } return clone; } private void prompt() { Object[] possibilities = {"Just use the original file", "Just use the companion file", "Merge, original file takes precedence", "Merge, companion file takes precedence"}; String s = (String)JOptionPane.showInputDialog( comp.getTopLevelAncestor(), "How would you like to merge the companion file " + "with the original file?\nIf you choose to merge, " + "you must specify which file takes\nprecedence should " + "a conflict in the metadata arise.", "Merge Mode Selection", JOptionPane.QUESTION_MESSAGE, (javax.swing.Icon)null, possibilities, possibilities[1]); if ((s != null) && (s.length() > 0)) { if(s.equals(possibilities[0])) mode = ALL_ORIGINAL; else if (s.equals(possibilities[1])) mode = ALL_COMPANION; else if (s.equals(possibilities[2])) mode = ORIGINAL_OVER; else if (s.equals(possibilities[3])) mode = COMPANION_OVER; } else mode = ALL_ORIGINAL; } }