changeset 85:fd7beb701724

working on "merge reference" feature.
author casties
date Fri, 30 Sep 2016 14:30:33 +0200
parents e2f6ccc4d322
children 017f8ff46e50
files src/main/java/de/mpiwg/itgroup/ismi/entry/beans/SessionBean.java src/main/java/de/mpiwg/itgroup/ismi/merge/ReferenceMerge.java src/main/webapp/merge/referenceMerge.xhtml src/main/webapp/templates/main_template.xhtml
diffstat 4 files changed, 1017 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/src/main/java/de/mpiwg/itgroup/ismi/entry/beans/SessionBean.java	Wed Sep 28 10:47:39 2016 +0200
+++ b/src/main/java/de/mpiwg/itgroup/ismi/entry/beans/SessionBean.java	Fri Sep 30 14:30:33 2016 +0200
@@ -27,6 +27,7 @@
 import de.mpiwg.itgroup.ismi.event.beans.StudyEvent;
 import de.mpiwg.itgroup.ismi.event.beans.TransferEvent;
 import de.mpiwg.itgroup.ismi.merge.GeneralMerge;
+import de.mpiwg.itgroup.ismi.merge.ReferenceMerge;
 import de.mpiwg.itgroup.ismi.publicView.DynamicPageEditor;
 import de.mpiwg.itgroup.ismi.publicView.PublicCodexBean;
 import de.mpiwg.itgroup.ismi.publicView.PublicCodexView;
@@ -107,6 +108,7 @@
 	//private Entity currentEntity;
 
 	private GeneralMerge generalMerge;
+	private ReferenceMerge referenceMerge;
 
 	//private String last_action;
 	//private Date time_of_lastAction;
@@ -239,6 +241,7 @@
 			logger.info("login " + username + " [remoteAddr=" + remoteAddr + "]");
 			this.setUser(user);
 			this.generalMerge = new GeneralMerge();
+            this.referenceMerge = new ReferenceMerge();
 			this.defForm = new DefinitionForm();
 			// refresh the editor of Dirk
 			addSessionBean("CurrentWitness", new CurrentWitnessBean());
@@ -264,6 +267,7 @@
 		this.password = "";
 		this.user = null;
 		this.generalMerge = null;
+        this.referenceMerge = null;
 		this.defForm = null;
 		return PAGE_PUBLIC_CODICES;
 	}
@@ -928,6 +932,14 @@
 		this.generalMerge = generalMerge;
 	}
 
+    public ReferenceMerge getReferenceMerge() {
+        return referenceMerge;
+    }
+
+    public void setReferenceMerge(ReferenceMerge referenceMerge) {
+        this.referenceMerge = referenceMerge;
+    }
+
 	public DefinitionForm getDefForm() {
 		return defForm;
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/java/de/mpiwg/itgroup/ismi/merge/ReferenceMerge.java	Fri Sep 30 14:30:33 2016 +0200
@@ -0,0 +1,622 @@
+package de.mpiwg.itgroup.ismi.merge;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.event.ActionEvent;
+import javax.faces.model.SelectItem;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.mpi.openmind.repository.bo.Attribute;
+import org.mpi.openmind.repository.bo.Entity;
+import org.mpi.openmind.repository.bo.Relation;
+import org.mpi.openmind.repository.services.utils.AttributeFilter;
+
+import de.mpiwg.itgroup.ismi.entry.beans.AbstractISMIBean;
+
+public class ReferenceMerge extends AbstractISMIBean implements Serializable{
+	
+	private static Logger logger = Logger.getLogger(ReferenceMerge.class);
+	
+	private static final long serialVersionUID = 1L;
+	public static String FIRST_VALUE = "first value";
+	public static String SECOND_VALUE = "second value";
+	public static String IGNORE = "ignore";
+	public static String TAKE = "take";	
+
+	private boolean showAttributeMapping = false;
+	private boolean showSrcRelationMapping = false;
+	private boolean showTarRelationMapping = false;
+
+	private boolean entitiesLoaded = false;
+
+	private Map<String, String> firstAttMap = new HashMap<String, String>();
+	private Map<String, String> secondAttMap = new HashMap<String, String>();
+
+	private Entity firstEntity;
+	private String firstBibId;
+	private Entity secondEntity;
+    private String secondBibId;
+	
+	private Map<Entity, Attribute> firstEntityMap;
+    private Map<Entity, Attribute> secondEntityMap;
+	
+	private String firstId;
+	private String secondId;
+	
+	private Entity entResult;
+	private List<Attribute> resultAtts;
+	private List<Relation> resultSrcRels;
+	private List<Relation> resultTarRels;
+	
+	private List<String> attLabels;
+	private Map<String, String> selectedAtts;
+
+	Map<Long, String> selectedFirstSrcRelations;
+	Map<Long, String> selectedSecondSrcRelations;
+	Map<Long, String> selectedFirstTarRelations;
+	Map<Long, String> selectedSecondTarRelations;
+	
+	public ReferenceMerge(){}
+	
+	public void loadFirstEntity(ActionEvent event){
+		reset();
+		try{
+			Long id = new Long(this.firstId);
+			List<AttributeFilter> filterList = new ArrayList<AttributeFilter>();
+			// search using regexp match in endnote-id attribute
+			filterList.add(new AttributeFilter("endnote-id", "#" + id + "[[:>:]]", REFERENCE, true));
+			firstEntityMap = getWrapper().searchEntityByAttributeFilter(filterList, 1000);
+			if(this.firstEntityMap.isEmpty()){
+				addErrorMsg("No references found.");
+			} else {
+	             // show first entity as example
+                Entity ent = firstEntityMap.keySet().iterator().next();
+                this.firstEntity = ent;
+                Attribute att = firstEntityMap.get(ent);
+                this.firstBibId = att.getOwnValue();
+                
+                if(this.secondEntityMap != null && ! this.secondEntityMap.isEmpty()){
+                    this.deployDifferences();
+                }
+			}
+		} catch(Exception e) {
+			addErrorMsg("The first entity could no be loaded.");
+		}
+	}
+	
+	public void loadSecondEntity(ActionEvent event){
+		reset();
+		try{
+			Long id = new Long(this.secondId);
+            List<AttributeFilter> filterList = new ArrayList<AttributeFilter>();
+            // search using regexp match in endnote-id attribute
+            filterList.add(new AttributeFilter("endnote-id", "#" + id + "[[:>:]]", REFERENCE, true));
+            secondEntityMap = getWrapper().searchEntityByAttributeFilter(filterList, 1000);
+            if(this.secondEntityMap.isEmpty()){
+                addErrorMsg("No references found.");
+            } else {
+                // show first entity as example
+                Entity ent = secondEntityMap.keySet().iterator().next();
+                this.secondEntity = ent;
+                Attribute att = secondEntityMap.get(ent);
+                this.secondBibId = att.getOwnValue();
+
+                if(! this.firstEntityMap.isEmpty()){
+                    this.deployDifferences();
+                }
+            }
+		}catch(Exception e){
+			addErrorMsg( "The second entity could no be loaded.");
+		}
+	}
+	
+	@Override
+	public void reset(){
+		this.attLabels = new ArrayList<String>();
+		this.selectedAtts = new HashMap<String, String>();
+		this.selectedFirstSrcRelations = new HashMap<Long, String>();
+		this.selectedSecondSrcRelations = new HashMap<Long, String>();
+		this.selectedFirstTarRelations = new HashMap<Long, String>();
+		this.selectedSecondTarRelations = new HashMap<Long, String>();
+		this.entResult = null;
+		
+		this.entitiesLoaded = false;
+		this.showAttributeMapping = false;
+		this.showSrcRelationMapping = false;
+		this.showTarRelationMapping = false;
+	}
+
+	public void listenerExecuteMerge(){
+		this.executeMerge();
+		getAppBean().getSimpleSearchCache().setMapDirty(true);
+	}
+	
+	private void deployDifferences(){
+		this.showAttributeMapping = true;
+		this.showSrcRelationMapping = true;
+		this.showTarRelationMapping = true;
+		this.entitiesLoaded = true;
+		if(this.firstEntity != null && this.secondEntity != null){
+		    
+            this.attLabels = new ArrayList<String>();
+
+            for (Entity ent : this.secondEntityMap.keySet()) {
+
+                if (ent.isLightweight()) {
+                    ent = getWrapper().getEntityContent(ent);
+                }
+                attLabels.add(ent.getId().toString());
+                
+            }
+            
+            /*
+			if(secondEntity.isLightweight()){
+				this.secondEntity = getWrapper().getEntityContent(this.secondEntity);
+			}			
+			
+			this.firstEntity = (Entity)firstEntity.clone();
+			this.secondEntity = (Entity) secondEntity.clone();
+			
+			//attributes
+			this.attLabels = new ArrayList<String>();
+			this.selectedAtts = new HashMap<String, String>();
+			this.firstAttMap = new HashMap<String, String>();
+			this.secondAttMap = new HashMap<String, String>();
+			
+			for(Attribute att : this.firstEntity.getAttributes()){
+				firstAttMap.put(att.getName(), att.getValue());
+				if(!attLabels.contains(att.getName())){
+					attLabels.add(att.getName());
+					selectedAtts.put(att.getName(), FIRST_VALUE);
+				}
+			}
+			
+			for(Attribute att : this.secondEntity.getAttributes()){
+				secondAttMap.put(att.getName(), att.getValue());
+				if(!attLabels.contains(att.getName())){
+					attLabels.add(att.getName());
+					selectedAtts.put(att.getName(), FIRST_VALUE);
+				}
+			}			
+			
+			//source relations
+			this.selectedFirstSrcRelations = new HashMap<Long, String>();
+			this.selectedSecondSrcRelations = new HashMap<Long, String>();
+			
+			for(Relation rel : this.firstEntity.getSourceRelations()){
+				rel.setTarget(getWrapper().getEntityById(rel.getTargetId()));
+				selectedFirstSrcRelations.put(rel.getId(), TAKE);
+			}
+			
+			
+			for(Relation rel : this.secondEntity.getSourceRelations()){
+				rel.setTarget(getWrapper().getEntityById(rel.getTargetId()));
+				selectedSecondSrcRelations.put(rel.getId(), TAKE);
+			}
+			
+			//target relations
+			this.selectedFirstTarRelations = new HashMap<Long, String>();
+			this.selectedSecondTarRelations = new HashMap<Long, String>();
+			
+			for(Relation rel : this.firstEntity.getTargetRelations()){
+				rel.setSource(getWrapper().getEntityById(rel.getSourceId()));
+				selectedFirstTarRelations.put(rel.getId(), TAKE);
+			}
+			
+			for(Relation rel : this.secondEntity.getTargetRelations()){
+				rel.setSource(getWrapper().getEntityById(rel.getSourceId()));
+				selectedSecondTarRelations.put(rel.getId(), TAKE);
+			}
+			
+			*/
+		}
+	}
+	
+	public void preview(ActionEvent event){
+		this.generateResultEntity();
+	}
+	
+	private void executeMerge(){
+		
+		logger.info("Starting merge execution " + firstEntity.getObjectClass() 
+				+ " ["+ getUserName() +"]"
+				+ "[firstEntity=" + firstEntity.getId() 
+				+ ", secondEntity=" + secondEntity.getId() + "]");
+		
+		try {
+			this.generateResultEntity();
+			if(this.entResult != null){
+				
+				this.printMergeInfo(entResult);
+				
+				this.getWrapper().saveEntity(this.entResult, getSessionUser().getEmail() + "_merge");
+				
+				this.getWrapper().removeCurrentVersionEntity(this.firstEntity);
+				this.getWrapper().removeCurrentVersionEntity(this.secondEntity);
+				
+				//the old relations should be removed, before...
+				this.generateSecundaryOW(this.entResult, getSessionUser().getEmail() + "_merge");
+				
+				this.printMergeInfo(entResult);
+				
+				logger.info("Merge execution 'successful' "  + 
+						firstEntity.getObjectClass() + 
+						" ["+ getUserName() +"]"
+						+ "[firstEntity=" + firstEntity.getId() 
+						+ ", secondEntity=" + secondEntity.getId() 
+						+ ", generatedEntity=" + entResult.getId() + "]");
+				
+				this.firstEntity = null;
+				this.secondEntity = null;
+				
+				addGeneralMsg("The entities were merged successfully");
+				addGeneralMsg("The new entity has the id " + this.entResult.getId());
+				this.reset();
+			}	
+		} catch (Exception e) {
+			printInternalError(e);
+			logger.error("["+ getUserName() +"] " + e.getMessage(), e);			
+		}
+	}
+	
+	private void printMergeInfo(Entity ent){
+		StringBuilder sb = new StringBuilder("\n\n");
+		
+		sb.append("-------------------------------------------");
+		sb.append("-----------------------------------------\n");
+		sb.append("Merging result [" + getUserName() + "]\n");
+		sb.append(ent.toString() + "\n");
+		sb.append("Attributes:\n");
+		for(Attribute att : ent.getAttributes()){
+			sb.append("\t" + att.toString() + "\n");
+		}
+		
+		sb.append("Src Relations:\n");
+		for(Relation src : ent.getSourceRelations()){
+			sb.append("\t" + src.toString() + "\n");
+		}
+		
+		sb.append("Tar Relations:\n");
+		for(Relation tar : ent.getTargetRelations()){
+			sb.append("\t" + tar.toString() + "\n");
+		}
+		
+		sb.append("-------------------------------------------");
+		sb.append("-----------------------------------------\n");
+		logger.info(sb.toString());
+	}
+	
+	public void actionShowTarRelationMapping(ActionEvent event){
+		this.showTarRelationMapping = true;
+	}
+	
+	public void actionHideTarRelationMapping(ActionEvent event){
+		this.showTarRelationMapping = false;
+	}
+	
+	public void actionShowSrcRelationMapping(ActionEvent event){
+		this.showSrcRelationMapping = true;
+	}
+	
+	public void actionHideSrcRelationMapping(ActionEvent event){
+		this.showSrcRelationMapping = false;
+	}
+	
+	public void actionShowAttributeMapping(ActionEvent event){
+		this.showAttributeMapping = true;
+	}
+	
+	public void actionHideAttributeMapping(ActionEvent event){
+		this.showAttributeMapping = false;
+	}
+	
+	private void generateResultEntity(){
+		this.entResult = new Entity();
+		this.entResult.setLightweight(false);
+		this.entResult.setObjectClass(this.firstEntity.getObjectClass());
+		
+		//generating attributes
+		try{
+			for(String attName : this.selectedAtts.keySet()){
+				String selected = this.selectedAtts.get(attName);
+				String value = "";
+				if(selected.equals(FIRST_VALUE)){
+					value = (firstEntity.getAttributeByName(attName) == null) ? "" : firstEntity.getAttributeByName(attName).getOwnValue();
+				}else if(selected.equals(SECOND_VALUE)){
+					value = (secondEntity.getAttributeByName(attName) == null) ? "" : secondEntity.getAttributeByName(attName).getOwnValue();
+				}
+				this.entResult.addAttribute(new Attribute(attName, "text", value));
+			}
+		}catch(Exception e){
+			e.printStackTrace();
+			addErrorMsg("Please inform support of this exception: " + e.getMessage());
+		}
+
+		
+		//generating source relations
+		for(Relation rel : firstEntity.getSourceRelations()){
+			String selectedValue = this.selectedFirstSrcRelations.get(rel.getId());
+			if(StringUtils.isNotEmpty(selectedValue) && selectedValue.equals(TAKE)){
+				if(!this.entResult.containsSourceRelation(rel.getOwnValue(), rel.getTargetId())){
+					this.entResult.addSourceRelation(generateSrcRelation(rel));	
+				}
+			}
+		}
+		
+		for(Relation rel : secondEntity.getSourceRelations()){
+			String selectedValue = this.selectedSecondSrcRelations.get(rel.getId());
+			if(StringUtils.isNotEmpty(selectedValue) && selectedValue.equals(TAKE)){
+				if(!this.entResult.containsSourceRelation(rel.getOwnValue(), rel.getTargetId())){
+					this.entResult.addSourceRelation(generateSrcRelation(rel));
+				}
+			}
+		}
+		
+		//generating target relations
+		for(Relation rel : firstEntity.getTargetRelations()){
+			String selectedValue = this.selectedFirstTarRelations.get(rel.getId());
+			if(StringUtils.isNotEmpty(selectedValue) && selectedValue.equals(TAKE)){
+				//ensuring that there is no two equals relations.
+				if(!this.entResult.containsTargetRelation(rel.getOwnValue(), rel.getSourceId())){
+					this.entResult.addTargetRelation(generateTarRelation(rel));
+				}
+			}
+		}
+		
+		for(Relation rel : secondEntity.getTargetRelations()){
+			String selectedValue = this.selectedSecondTarRelations.get(rel.getId());
+			if(StringUtils.isNotEmpty(selectedValue) && selectedValue.equals(TAKE)){
+				if(!this.entResult.containsTargetRelation(rel.getOwnValue(), rel.getSourceId())){
+					this.entResult.addTargetRelation(generateTarRelation(rel));
+				}
+			}
+		}
+	}
+	
+	private Relation generateSrcRelation(Relation rel){
+		
+		Relation newRel = new Relation();
+		newRel.setOwnValue(rel.getOwnValue());
+		newRel.setTarget(getWrapper().getEntityById(rel.getTargetId()));
+		
+		return newRel;
+	}
+	
+	private Relation generateTarRelation(Relation rel){
+		Relation newRel = new Relation();
+		newRel.setOwnValue(rel.getOwnValue());
+		newRel.setSource(getWrapper().getEntityById(rel.getSourceId()));
+		return newRel;
+	}
+
+    public List<SelectItem> getAttSelectItems() {
+        List<SelectItem> items = new ArrayList<SelectItem>();
+        items.add(new SelectItem(FIRST_VALUE));
+        items.add(new SelectItem(SECOND_VALUE));
+        items.add(new SelectItem(IGNORE));
+        return items;
+    }
+    
+    public List<SelectItem> getRelSelectItems(){
+        List<SelectItem> items = new ArrayList<SelectItem>();
+        items.add(new SelectItem(TAKE));
+        items.add(new SelectItem(IGNORE));
+        return items;
+    }
+    
+	public Entity getEntResult() {
+		return entResult;
+	}
+
+	public void setEntResult(Entity entResult) {
+		this.entResult = entResult;
+	}
+
+	public List<Attribute> getResultAtts() {
+		return resultAtts;
+	}
+
+	public void setResultAtts(List<Attribute> resultAtts) {
+		this.resultAtts = resultAtts;
+	}
+
+	public List<Relation> getResultSrcRels() {
+		return resultSrcRels;
+	}
+
+	public void setResultSrcRels(List<Relation> resultSrcRels) {
+		this.resultSrcRels = resultSrcRels;
+	}
+
+	public List<Relation> getResultTarRels() {
+		return resultTarRels;
+	}
+
+	public void setResultTarRels(List<Relation> resultTarRels) {
+		this.resultTarRels = resultTarRels;
+	}
+	
+	public Entity getFirstEntity() {
+		return firstEntity;
+	}
+
+	public void setFirstEntity(Entity firstEntity) {
+		this.firstEntity = firstEntity;
+	}
+
+	public Entity getSecondEntity() {
+		return secondEntity;
+	}
+
+	public void setSecondEntity(Entity secondEntity) {
+		this.secondEntity = secondEntity;
+	}
+
+	public String getFirstId() {
+		return firstId;
+	}
+
+	public void setFirstId(String firstId) {
+		this.firstId = firstId;
+	}
+
+	public String getSecondId() {
+		return secondId;
+	}
+
+	public void setSecondId(String secondId) {
+		this.secondId = secondId;
+	}
+	
+	public List<String> getAttLabels() {
+		return attLabels;
+	}
+
+	public void setAttLabels(List<String> attLabels) {
+		this.attLabels = attLabels;
+	}
+	
+	public Map<String, String> getFirstAttMap() {
+		return firstAttMap;
+	}
+
+	public void setFirstAttMap(Map<String, String> firstAttMap) {
+		this.firstAttMap = firstAttMap;
+	}
+
+	public Map<String, String> getSecondAttMap() {
+		return secondAttMap;
+	}
+
+	public void setSecondAttMap(Map<String, String> secondAttMap) {
+		this.secondAttMap = secondAttMap;
+	}
+
+	public Map<String, String> getSelectedAtts() {
+		return selectedAtts;
+	}
+
+	public void setSelectedAtts(Map<String, String> selectedAtts) {
+		this.selectedAtts = selectedAtts;
+	}
+	
+	public boolean isShowAttributeMapping() {
+		return showAttributeMapping;
+	}
+
+	public void setShowAttributeMapping(boolean showAttributeMapping) {
+		this.showAttributeMapping = showAttributeMapping;
+	}
+
+	public boolean isEntitiesLoaded() {
+		return entitiesLoaded;
+	}
+
+	public void setEntitiesLoaded(boolean entitiesLoaded) {
+		this.entitiesLoaded = entitiesLoaded;
+	}
+	
+	public Map<Long, String> getSelectedFirstSrcRelations() {
+		return selectedFirstSrcRelations;
+	}
+
+	public void setSelectedFirstSrcRelations(
+			Map<Long, String> selectedFirstSrcRelations) {
+		this.selectedFirstSrcRelations = selectedFirstSrcRelations;
+	}
+
+	public Map<Long, String> getSelectedSecondSrcRelations() {
+		return selectedSecondSrcRelations;
+	}
+
+	public void setSelectedSecondSrcRelations(
+			Map<Long, String> selectedSecondSrcRelations) {
+		this.selectedSecondSrcRelations = selectedSecondSrcRelations;
+	}
+	public boolean isShowSrcRelationMapping() {
+		return showSrcRelationMapping;
+	}
+
+	public void setShowSrcRelationMapping(boolean showSrcRelationMapping) {
+		this.showSrcRelationMapping = showSrcRelationMapping;
+	}
+
+
+	public boolean isShowTarRelationMapping() {
+		return showTarRelationMapping;
+	}
+
+	public void setShowTarRelationMapping(boolean showTarRelationMapping) {
+		this.showTarRelationMapping = showTarRelationMapping;
+	}
+
+	public Map<Long, String> getSelectedFirstTarRelations() {
+		return selectedFirstTarRelations;
+	}
+
+	public void setSelectedFirstTarRelations(
+			Map<Long, String> selectedFirstTarRelations) {
+		this.selectedFirstTarRelations = selectedFirstTarRelations;
+	}
+
+	public Map<Long, String> getSelectedSecondTarRelations() {
+		return selectedSecondTarRelations;
+	}
+
+	public void setSelectedSecondTarRelations(
+			Map<Long, String> selectedSecondTarRelations) {
+		this.selectedSecondTarRelations = selectedSecondTarRelations;
+	}
+	/*
+	public String getMsg() {
+		return msg;
+	}
+
+	public void setMsg(String msg) {
+		this.msg = msg;
+	}
+
+	public boolean isShowMsgPopup() {
+		return showMsgPopup;
+	}
+
+	public void setShowMsgPopup(boolean showMsgPopup) {
+		this.showMsgPopup = showMsgPopup;
+	}
+
+	public String getConfirmMsg() {
+		return confirmMsg;
+	}
+
+	public void setConfirmMsg(String confirmMsg) {
+		this.confirmMsg = confirmMsg;
+	}
+
+	public boolean isShowConfirmPopup() {
+		return showConfirmPopup;
+	}
+
+	public void setShowConfirmPopup(boolean showConfirmPopup) {
+		this.showConfirmPopup = showConfirmPopup;
+	}
+	*/
+
+    /**
+     * @return the firstBibId
+     */
+    public String getFirstBibId() {
+        return firstBibId;
+    }
+
+    /**
+     * @return the secondBibId
+     */
+    public String getSecondBibId() {
+        return secondBibId;
+    }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/webapp/merge/referenceMerge.xhtml	Fri Sep 30 14:30:33 2016 +0200
@@ -0,0 +1,379 @@
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:f="http://java.sun.com/jsf/core"
+	xmlns:ui="http://java.sun.com/jsf/facelets"
+	xmlns:a4j="http://richfaces.org/a4j"
+	xmlns:rich="http://richfaces.org/rich">
+
+<body>
+	<ui:composition template="/templates/privateTemplate.xhtml">
+
+		<ui:define name="privateContent">
+			
+			<div id="pageTitle">
+				<h:outputText value="Join References" />
+			</div>
+
+            <!-- TODO: fix this! -->
+            <script type="text/javascript" src="../imageServer/resources/js/diva4ismi.js"></script>
+			
+			<h:panelGrid rendered="#{Session.user != null}"
+				styleClass="mainPanel" columns="1">
+			
+				
+
+				<h:panelGrid columns="2" styleClass="createPanel"
+					columnClasses="createPanelFirstColumn" id="loadingPanel">
+
+					<h:outputText value="Load Entities" />
+
+					<h:panelGrid columns="4">
+
+						<h:outputText value="Source Biblio ID" 
+							styleClass="textBack"/>
+						<h:inputText value="#{Session.referenceMerge.firstId}" />
+						<a4j:commandButton value="Load"
+							actionListener="#{Session.referenceMerge.loadFirstEntity}"
+							render="loadingPanel,mergingPanel" />
+						<h:column>
+							<h:outputText
+								value="#{Session.referenceMerge.firstEntity.objectClass}= #{Session.referenceMerge.firstEntity.ownValue} [#{Session.referenceMerge.firstEntity.id}]"
+								rendered="#{!empty Session.referenceMerge.firstEntity}" 
+								styleClass="textBack"/>
+						</h:column>
+						
+                        <h:outputText/>
+                        <h:outputText/>
+                        <h:outputText/>
+                        <h:column rendered="#{Session.referenceMerge.firstBibId != null}">
+                          <script>showBibliographyEntryFormatted("#{Session.referenceMerge.firstBibId}", null, "#bibl-entry-1")</script>
+                          <span class="textBack" id="bibl-entry-1">(loading reference...)</span>
+                        </h:column>
+                        <h:outputText rendered="#{Session.referenceMerge.firstBibId == null}"/>
+                        
+						<h:outputText value="Target Biblio ID" 
+							styleClass="textBack"/>
+						<h:inputText value="#{Session.referenceMerge.secondId}" />
+						<a4j:commandButton value="Load"
+							actionListener="#{Session.referenceMerge.loadSecondEntity}"
+							render="loadingPanel,mergingPanel" />
+						<h:column>
+							<h:outputText
+								value="#{Session.referenceMerge.secondEntity.objectClass}= #{Session.referenceMerge.secondEntity.ownValue} [#{Session.referenceMerge.secondEntity.id}]"
+								rendered="#{Session.referenceMerge.secondEntity != null}"
+								styleClass="textBack"/>
+						</h:column>
+
+                        <h:outputText/>
+                        <h:outputText/>
+                        <h:outputText/>
+                        <h:column rendered="#{Session.referenceMerge.secondBibId != null}">
+                          <script>showBibliographyEntryFormatted("#{Session.referenceMerge.secondBibId}", null, "#bibl-entry-2")</script>
+                          <span class="textBack" id="bibl-entry-2">(loading reference...)</span>
+                        </h:column>
+                        <h:outputText rendered="#{Session.referenceMerge.secondBibId == null}"/>
+                        
+					</h:panelGrid>
+
+				</h:panelGrid>
+
+				<h:panelGrid columns="1" id="mergingPanel">
+
+					<h:outputText value="Merge Mapping"
+						rendered="#{Session.referenceMerge.entitiesLoaded}"
+						styleClass="titlePanel"/>
+
+					<h:panelGrid columns="3" styleClass="createPanel"
+						columnClasses="createPanelFirstColumn,createPanelColumn02,createPanelColumn02"
+						rendered="#{Session.referenceMerge.entitiesLoaded}">
+
+
+						<h:outputText value="Attributes" />
+						<rich:dataTable var="attName"
+							value="#{Session.referenceMerge.attLabels}"
+							rendered="#{!empty Session.referenceMerge.attLabels}">
+							<h:column>
+								<f:facet name="header">
+									<h:outputText value="Attribute Name" />
+								</f:facet>
+								<h:outputText value="#{attName}" />
+							</h:column>
+							<h:column>
+								<f:facet name="header">
+									<h:outputText value="Value First Entity" />
+								</f:facet>
+								<h:outputText
+									value="#{Session.referenceMerge.firstAttMap[attName]}" />
+							</h:column>
+							<h:column>
+								<f:facet name="header">
+									<h:outputText value="Value Second Entity" />
+								</f:facet>
+								<h:outputText
+									value="#{Session.referenceMerge.secondAttMap[attName]}" />
+							</h:column>
+							<h:column style="width:300px;">
+								<f:facet name="header">
+									<h:outputText value="Select Value" />
+								</f:facet>
+								<h:selectOneRadio
+									value="#{Session.referenceMerge.selectedAtts[attName]}">
+									<f:selectItems value="#{Session.referenceMerge.attSelectItems}" />
+								</h:selectOneRadio>
+							</h:column>
+						</rich:dataTable>
+						<h:outputText />
+
+						<h:outputText value="Source Relations" />
+
+						<h:panelGrid columns="1">
+							<rich:dataTable var="srcRelation"
+								rendered="#{!empty Session.referenceMerge.firstEntity.sourceRelations}"
+								value="#{Session.referenceMerge.firstEntity.sourceRelations}">
+
+								<h:column>
+									<f:facet name="header">
+										<h:outputText value="Relation Name" />
+									</f:facet>
+									<h:outputText value="#{srcRelation.ownValue}" />
+								</h:column>
+								<h:column>
+									<f:facet name="header">
+										<h:outputText value="linked to this" />
+									</f:facet>
+									<h:outputText
+										value="#{srcRelation.target.ownValue} [#{srcRelation.target.objectClass}-#{srcRelation.targetId}]" />
+								</h:column>
+
+								<h:column>
+									<h:selectOneRadio
+										value="#{Session.referenceMerge.selectedFirstSrcRelations[srcRelation.id]}">
+										<f:selectItems value="#{Session.referenceMerge.relSelectItems}" />
+									</h:selectOneRadio>
+								</h:column>
+
+							</rich:dataTable>
+
+							<h:outputText
+								value="the first entity does not have source relations"
+								rendered="#{empty Session.referenceMerge.firstEntity.sourceRelations}" />
+						</h:panelGrid>
+
+						<h:panelGrid columns="1">
+
+							<rich:dataTable var="srcRelation"
+								rendered="#{!empty Session.referenceMerge.secondEntity.sourceRelations}"
+								value="#{Session.referenceMerge.secondEntity.sourceRelations}">
+								<h:column>
+									<f:facet name="header">
+										<h:outputText value="Relation Name" />
+									</f:facet>
+									<h:outputText value="#{srcRelation.ownValue}" />
+								</h:column>
+								<h:column>
+									<f:facet name="header">
+										<h:outputText value="linked to this" />
+									</f:facet>
+									<h:outputText
+										value="#{srcRelation.target.ownValue} [#{srcRelation.target.objectClass}-#{srcRelation.targetId}]" />
+								</h:column>
+								<h:column>
+									<h:selectOneRadio
+										value="#{Session.referenceMerge.selectedSecondSrcRelations[srcRelation.id]}">
+										<f:selectItems value="#{Session.referenceMerge.relSelectItems}" />
+									</h:selectOneRadio>
+								</h:column>
+							</rich:dataTable>
+							
+							<h:outputText
+								value="The second entity does not have source relations"
+								rendered="#{empty Session.referenceMerge.secondEntity.sourceRelations}" />
+
+						</h:panelGrid>
+
+
+						<h:outputText value="Target Relations" />
+
+						<h:panelGrid columns="1">
+							<rich:dataTable var="tarRelation"
+								rendered="#{!empty Session.referenceMerge.firstEntity.targetRelations}"
+								value="#{Session.referenceMerge.firstEntity.targetRelations}"
+								style="width:60%;">
+
+								<h:column>
+									<f:facet name="header">
+										<h:outputText value="linked from this" />
+									</f:facet>
+									<h:outputText
+										value="#{tarRelation.source.ownValue} [#{tarRelation.source.objectClass}-#{tarRelation.sourceId}]" />
+								</h:column>
+
+								<h:column>
+									<f:facet name="header">
+										<h:outputText value="Relation Name" />
+									</f:facet>
+									<h:outputText value="#{tarRelation.ownValue}" />
+								</h:column>
+
+								<h:column>
+									<h:selectOneRadio
+										value="#{Session.referenceMerge.selectedFirstTarRelations[tarRelation.id]}">
+										<f:selectItems value="#{Session.referenceMerge.relSelectItems}" />
+									</h:selectOneRadio>
+								</h:column>
+							</rich:dataTable>
+							<h:outputText
+								value="the first entity does not have target relations."
+								rendered="#{empty Session.referenceMerge.firstEntity.targetRelations}" />
+						</h:panelGrid>
+
+						<h:panelGrid columns="1">
+
+							<rich:dataTable var="tarRelation"
+								rendered="#{!empty Session.referenceMerge.secondEntity.targetRelations}"
+								value="#{Session.referenceMerge.secondEntity.targetRelations}"
+								style="width:60%;">
+
+								<h:column>
+									<f:facet name="header">
+										<h:outputText value="linked from this" />
+									</f:facet>
+									<h:outputText
+										value="#{tarRelation.source.ownValue} [#{tarRelation.source.objectClass}-#{tarRelation.sourceId}]" />
+								</h:column>
+
+								<h:column>
+									<f:facet name="header">
+										<h:outputText value="Relation Name" />
+									</f:facet>
+									<h:outputText value="#{tarRelation.ownValue}" />
+								</h:column>
+
+								<h:column>
+									<h:selectOneRadio
+										value="#{Session.referenceMerge.selectedSecondTarRelations[tarRelation.id]}">
+										<f:selectItems value="#{Session.referenceMerge.relSelectItems}" />
+									</h:selectOneRadio>
+								</h:column>
+
+							</rich:dataTable>
+
+							<h:outputText
+								value="The second entity does not have target relations"
+								rendered="#{empty Session.referenceMerge.secondEntity.targetRelations}" />
+
+						</h:panelGrid>
+					</h:panelGrid>
+
+					<h:panelGrid columns="2" styleClass="controlPanel"
+						rendered="#{Session.referenceMerge.entitiesLoaded}">
+
+						<a4j:commandButton value="Preview"
+							actionListener="#{Session.referenceMerge.preview}"
+							rendered="#{Session.referenceMerge.entitiesLoaded}"
+							render="mergingPanel" />
+
+						<a4j:commandButton value="Execute Merge"
+							actionListener="#{Session.referenceMerge.listenerExecuteMerge }"
+							rendered="#{Session.referenceMerge.entitiesLoaded}"
+							onclick="#{ApplicationBean1.JSConfirmationMerge}"
+							render="mergingPanel" />
+
+					</h:panelGrid>
+
+
+					<h:outputText value="Entity Preview"
+						rendered="#{!empty Session.referenceMerge.entResult}"
+						styleClass="titlePanel"/>
+					<h:panelGrid columns="2" styleClass="createPanel"
+						columnClasses="createPanelFirstColumn"
+						rendered="#{!empty Session.referenceMerge.entResult}">
+
+
+						<h:outputText value="Attributes" />
+						<rich:dataTable
+							value="#{Session.referenceMerge.entResult.attributes}"
+							var="attribute" border="1">
+							<h:column>
+								<f:facet name="header">
+									<h:outputText value="Name" />
+								</f:facet>
+								<h:outputText value="#{attribute.objectClass}" />
+							</h:column>
+							<h:column>
+								<f:facet name="header">
+									<h:outputText value="Value" />
+								</f:facet>
+								<h:outputText value="#{attribute.ownValue}" />
+							</h:column>
+						</rich:dataTable>
+
+
+						<h:outputText value="Source Relations" />
+						<rich:dataTable
+							value="#{Session.referenceMerge.entResult.sourceRelations}"
+							var="srcRelation" border="1">
+
+							<h:column>
+								<f:facet name="header">
+									<h:outputText value="linked from this" />
+								</f:facet>
+								<h:outputText value="#{'x'}" />
+							</h:column>
+
+							<h:column>
+								<f:facet name="header">
+									<h:outputText value="Relation Name" />
+								</f:facet>
+								<h:outputText value="#{srcRelation.ownValue}" />
+							</h:column>
+							<h:column>
+								<f:facet name="header">
+									<h:outputText value="linked to this" />
+								</f:facet>
+								<h:outputText
+									value="#{srcRelation.target.ownValue} [#{srcRelation.target.objectClass}]" />
+							</h:column>
+						</rich:dataTable>
+
+						<h:outputText value="Target Relations" />
+						<rich:dataTable
+							value="#{Session.referenceMerge.entResult.targetRelations}"
+							var="tarRelation" border="1">
+							<h:column>
+								<f:facet name="header">
+									<h:outputText value="linked from this" />
+								</f:facet>
+								<h:outputText
+									value="#{tarRelation.source.ownValue} [#{tarRelation.source.objectClass}]" />
+							</h:column>
+							<h:column>
+								<f:facet name="header">
+									<h:outputText value="Relation Name" />
+								</f:facet>
+								<h:outputText value="#{tarRelation.ownValue}" />
+							</h:column>
+							<h:column>
+								<f:facet name="header">
+									<h:outputText value="linked to this" />
+								</f:facet>
+								<h:outputText value="#{'x'}" />
+							</h:column>
+						</rich:dataTable>
+
+
+					</h:panelGrid>
+
+
+
+				</h:panelGrid>
+
+
+
+			</h:panelGrid>
+		</ui:define>
+	</ui:composition>
+</body>
+</html>
\ No newline at end of file
--- a/src/main/webapp/templates/main_template.xhtml	Wed Sep 28 10:47:39 2016 +0200
+++ b/src/main/webapp/templates/main_template.xhtml	Fri Sep 30 14:30:33 2016 +0200
@@ -147,9 +147,12 @@
 				</rich:menuItem>
 			</rich:dropDownMenu>
 
-			<h:outputLink rendered="#{Session.canCreate}"
+			<h:outputLink rendered="#{Session.canMerge}"
 				value="#{ApplicationBean1.root}/merge/generalMerge.xhtml">Merge</h:outputLink>
 
+            <h:outputLink rendered="#{Session.canMerge}"
+                value="#{ApplicationBean1.root}/merge/referenceMerge.xhtml">Join Refs</h:outputLink>
+
 			<h:outputLink  rendered="#{Session.canCreate}"
 				value="#{ApplicationBean1.root}/browse/entityRepository.xhtml">Browse Repository</h:outputLink>