/** * Copyright (c) 2011, SOCIETIES Consortium (WATERFORD INSTITUTE OF TECHNOLOGY (TSSG), HERIOT-WATT UNIVERSITY (HWU), SOLUTA.NET * (SN), GERMAN AEROSPACE CENTRE (Deutsches Zentrum fuer Luft- und Raumfahrt e.V.) (DLR), Zavod za varnostne tehnologije * informacijske družbe in elektronsko poslovanje (SETCCE), INSTITUTE OF COMMUNICATION AND COMPUTER SYSTEMS (ICCS), LAKE * COMMUNICATIONS (LAKE), INTEL PERFORMANCE LEARNING SOLUTIONS LTD (INTEL), PORTUGAL TELECOM INOVAÇÃO, SA (PTIN), IBM Corp., * INSTITUT TELECOM (ITSUD), AMITEC DIACHYTI EFYIA PLIROFORIKI KAI EPIKINONIES ETERIA PERIORISMENIS EFTHINIS (AMITEC), TELECOM * ITALIA S.p.a.(TI), TRIALOG (TRIALOG), Stiftelsen SINTEF (SINTEF), NEC EUROPE LTD (NEC)) * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following * conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.societies.personalisation.UserPreferenceManagement.impl.merging; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.societies.api.internal.servicelifecycle.ServiceModelUtils; import org.societies.api.internal.useragent.feedback.IUserFeedback; import org.societies.api.internal.useragent.model.ExpProposalContent; import org.societies.api.internal.useragent.model.ExpProposalType; import org.societies.personalisation.preference.api.UserPreferenceMerging.IUserPreferenceMerging; import org.societies.personalisation.preference.api.model.IPreference; import org.societies.personalisation.preference.api.model.IPreferenceCondition; import org.societies.personalisation.preference.api.model.IPreferenceOutcome; import org.societies.personalisation.preference.api.model.PreferenceOutcome; import org.societies.personalisation.preference.api.model.PreferenceTreeNode; public class PreferenceMerger implements IUserPreferenceMerging{ private String situation; private Logger logging = LoggerFactory.getLogger(this.getClass()); private final IUserFeedback userFeedback; public PreferenceMerger(IUserFeedback userFeedback){ this.userFeedback = userFeedback; //RequestUserConfirmation ruc = new RequestUserConfirmation(); situation = ""; } @Override public IPreference mergeTrees(IPreference oldTree, IPreference newNode, String title){ this.situation = title; if (newNode.isLeaf()){ this.logging.debug("new node does not contain context condition. merging as leaf"); return mergeNewNodeAsLeaf(newNode,oldTree); } if (oldTree.isLeaf()){ this.logging.debug("old node does not contain context condition. merging as leaf"); return mergeNewNodeAsLeaf(oldTree,newNode); } ArrayList<SingleRule> singleRules = this.convertToSingleRules(newNode); IPreference mergedTree = oldTree; for (int i = 0; i< singleRules.size(); i++){ SingleRule sr = singleRules.get(i); logging.debug("Merging new Single Rule: "+sr.toString()); logging.debug("\twith: "+mergedTree.toTreeString()); IPreference temp = merge(mergedTree, sr); if (temp==null){ return null; } mergedTree = temp; //in the MergingManager if this method returns null, it means we have to request a full learning cycle } //DisplayPreferenceTree dpt = new DisplayPreferenceTree(new PreferenceTreeModel(oldTree),"Merged Tree :"+title); return mergedTree; } public ArrayList<SingleRule> convertToSingleRules(IPreference ptn){ ArrayList<SingleRule> singleRules = new ArrayList<SingleRule>(); Enumeration<IPreference> newNodeEnum = ptn.depthFirstEnumeration(); ArrayList<IPreference> leaves = new ArrayList<IPreference>(); while (newNodeEnum.hasMoreElements()){ IPreference temp = newNodeEnum.nextElement(); if (temp.isLeaf()){ leaves.add(temp); } } //we're going to construct SingleRule objects from the new tree to use as input to merge with the old tree for (IPreference leaf : leaves){ SingleRule sr = new SingleRule(); Object[] userObjs = leaf.getUserObjectPath(); for (int i=0; i<userObjs.length; i++){ if (userObjs[i]!=null){ if (userObjs[i] instanceof IPreferenceCondition){ sr.addConditions((IPreferenceCondition) userObjs[i]); }else { sr.setOutcome((IPreferenceOutcome) userObjs[i]); } } } singleRules.add(sr); } for (int i=0; i<singleRules.size(); i++){ logging.debug("::"+singleRules.get(i).toString()); } return singleRules; } private IPreference merge(IPreference oldTree, SingleRule sr){ //IPreference newTree = null; ArrayList<SingleRule> oldRules = this.convertToSingleRules(oldTree); //check if we're in Situation 1 (same conditions different outcomes) ArrayList<SingleRule> temp = this.checkConflicts(oldRules, sr); if (temp.size()>0){ return null; } this.logging.debug("Not in situation 1"); //check if we're in Situation 2 (100% match) temp = this.checkMatches(oldRules, sr); if (temp.size()>0){ //return createTree(temp); return oldTree; } this.logging.debug("Not in Situation 2"); //we're going to find a branch that has the most common conditions with this rule. IPreference commonNode = this.findCommonNode(oldTree, sr); if (null==commonNode){ IPreference root = (IPreference) oldTree.getRoot(); if (null==root.getUserObject()){ return this.addToNode((IPreference) oldTree.getRoot(),sr); } IPreference newRoot = new PreferenceTreeNode(); newRoot.add(root); return this.addToNode(newRoot, sr); } return this.addToNode(commonNode, sr); //ArrayList<SingleRule> sortedRules = sortTree(oldRules); //newTree = createTree(sortedRules); //return newTree; } private ArrayList<SingleRule> checkConflicts(ArrayList<SingleRule> oldRules, SingleRule newRule){ for (int i=0; i< oldRules.size(); i++){ SingleRule sr = oldRules.get(i); if (sr.conflicts(newRule)){ oldRules.set(i, this.resolveConflict(sr, newRule)); return oldRules; } } return new ArrayList<SingleRule>(); } private SingleRule resolveConflict(SingleRule oldRule, SingleRule newRule){ //resolve return oldRule; } private ArrayList<SingleRule> checkMatches(ArrayList<SingleRule> oldRules, SingleRule newRule){ for (int i=0; i< oldRules.size(); i++){ SingleRule sr = oldRules.get(i); if (sr.equals(newRule)){ oldRules.set(i, this.increaseConfidenceLevel(sr)); return oldRules; } } return new ArrayList<SingleRule>(); } private SingleRule increaseConfidenceLevel(SingleRule sr){ //need to increase the confidence level by running the algorithm return sr; } private IPreference findCommonNode(IPreference ptn, SingleRule sr){ CommonNodeCounter cnc = new CommonNodeCounter(); //if it's an empty root, we have to repeat with all its children if (ptn.getUserObject() == null){ this.logging.debug("current node is empty root"); Enumeration<IPreference> e = ptn.children(); while (e.hasMoreElements()){ IPreference p = e.nextElement(); this.logging.debug("processing child :"+p.toString()+" which is child of: "+ptn.toString()); cnc = findCommonNode(p,sr, cnc); } }else{ cnc = findCommonNode(ptn,sr,cnc); } return cnc.getMostCommonNode(); } private CommonNodeCounter findCommonNode(IPreference ptn, SingleRule sr, CommonNodeCounter cnc){ //unlikely if (ptn.isLeaf()){ this.logging.debug("current node is leaf. returning common node counter"); return cnc; } IPreferenceCondition pc = (IPreferenceCondition) ptn.getUserObject(); //if they have a common condition, go to the children, otherwise, return and continue with siblings if (sr.hasCondition(pc)){ this.logging.debug("Single rule: "+sr.toString()+" has common node: "+pc.toString()); cnc.add(ptn, ptn.getLevel()); Enumeration<IPreference> e = ptn.children(); while (e.hasMoreElements()){ cnc = findCommonNode(e.nextElement(),sr,cnc); } } return cnc; } private IPreference addToNode(IPreference ptn, SingleRule sr){ logging.debug(situation+"BEFORE REMOVAL: "+sr.toString()); if (null!=ptn.getUserObject()){ logging.debug(this.situation+" found common node: "+ptn.getUserObject().toString()); //IPreferenceCondition[] cons = new IPreferenceCondition[ptn.getLevel()]; Object[] objs = ptn.getUserObjectPath(); for (int i = 0; i< objs.length; i++){ if (objs[i] instanceof IPreferenceCondition){ IPreferenceCondition con = (IPreferenceCondition) objs[i]; logging.debug(this.situation+" removing conditions"); if (sr.hasCondition(con)){ sr.removeCondition(con); logging.debug(this.situation+" REMOVED "+con.toString()); } } } if (ptn.getUserObject() instanceof IPreferenceCondition){ if (sr.hasCondition((IPreferenceCondition) ptn.getUserObject())){ sr.removeCondition((IPreferenceCondition) ptn.getUserObject()); } } }else{ logging.debug(this.situation+" not found common node"); } logging.debug(situation+"AFTER REMOVAL: "+sr.toString()); IPreference leaf = new PreferenceTreeNode(sr.getOutcome()); for (int i = 0; i< sr.getConditions().size(); i++){ IPreferenceCondition pc = (IPreferenceCondition) ptn.getUserObject(); if (null==pc){ logging.debug("weird"); } if (sr.getConditions().get(i) == null){ logging.debug("even weirder"); } //log("pc: "+pc.toString()); logging.debug("sr con: "+sr.getConditions().get(i).toString()); IPreference temp = new PreferenceTreeNode(sr.getConditions().get(i)); ptn.add(temp); ptn = temp; } ptn.add(leaf); return (IPreference) ptn.getRoot(); } public IPreference mergeNewNodeAsLeaf(IPreference newNode, IPreference oldTree){ //DisplayPreferenceTree dpt; if (newNode.isLeaf() && oldTree.isLeaf()){ //check if they are the same actions IPreferenceOutcome oldOutcome = oldTree.getOutcome(); IPreferenceOutcome newOutcome = newNode.getOutcome(); //if they match, we only need to increase its confidence level if (!oldOutcome.equals(newOutcome)){ String serviceId = ServiceModelUtils.serviceResourceIdentifierToString(oldOutcome.getServiceID()); String preferenceName = oldOutcome.getparameterName(); String[] options = new String []{oldOutcome.getvalue(), newOutcome.getvalue()}; try { List<String> list = this.userFeedback.getExplicitFB(ExpProposalType.RADIOLIST, new ExpProposalContent("Preference Merging information needed\n. Please select your default preference "+preferenceName+" of service: "+serviceId, options)).get(); if (list.size()>0){ String userResponse = list.get(0); if (userResponse.equalsIgnoreCase(oldOutcome.getvalue())){ return oldTree; }else{ return newNode; } } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //dpt = new DisplayPreferenceTree(new PreferenceTreeModel(oldTree), "Merged Tree"); return oldTree; } IPreference root = (IPreference) oldTree.getRoot(); if (root.getUserObject()==null){ root.add(newNode); logging.debug("root user object is null"); //dpt = new DisplayPreferenceTree(new PreferenceTreeModel(root), "Merged Tree"); return root; }else{ IPreference newRoot = new PreferenceTreeNode(); newRoot.add(newNode); newRoot.add(oldTree); //dpt = new DisplayPreferenceTree(new PreferenceTreeModel(newRoot), "Merged Tree"); logging.debug("root user object is not null"); return newRoot; } } /* public IPreference createTree(ArrayList<SingleRule> rules){ log("rules size: "+rules.size()); SingleRule sr = rules.get(0); log("conditions size: "+sr.getConditions().size()); IPreference ptn = new IPreference(rules.get(0).getConditions().get(0)); for (int i = 0; i < rules.size(); i++){ sr = rules.get(i); ptn = createTree(ptn,sr); } return ptn; } public IPreference createTree(IPreference ptn, SingleRule sr){ ptn = (IPreference) ptn.getRoot(); int index = 0; boolean eq = true; while ((eq ==true) && (index < sr.getConditions().size())){ IPreferenceCondition pc = sr.getConditions().get(index); if (ptn.getUserObject().equals(pc)){ ptn = lookupNode(ptn,pc); }else{ eq = false; } index =+1; } for (int i = index; i<sr.getConditions().size();i++){ IPreference temp = new IPreference(sr.getConditions().get(i)); ptn.add(temp); ptn = temp; } ptn.add(new IPreference(sr.getOutcome())); return ptn; } public IPreference lookupNode(IPreference ptn, IPreferenceCondition pc){ IPreference child; for (int i = 0; i < ptn.getChildCount(); i++){ child = (IPreference) ptn.getChildAt(i); if (child.getUserObject().equals(pc)){ return child; } } return ptn; } public ArrayList<SingleRule> sortTree(ArrayList<SingleRule> rules){ //ArrayList<IPreferenceCondition> newConditions = new ArrayList<IPreferenceCondition>(); log("START Sorting rules"); SortingCounter sc = new SortingCounter(); for (int i =0; i< rules.size();i++){ SingleRule temp = rules.get(i); log("Processing Rule: "+temp.toString()); ArrayList<IPreferenceCondition> cons = temp.getConditions(); for (int k=0;k<cons.size();k++){ log("adding condition: "+cons.get(k)); sc.incrementCounter(cons.get(k)); } } log("END Sorting rules"); sc.printSortingCounterData(); ArrayList<SingleRule> sortedRules = this.sortSingleRules(rules, sc); return sortedRules; } public ArrayList<SingleRule> sortSingleRules(ArrayList<SingleRule> rules, SortingCounter sc){ //initially we have to find a condition that is common in all single rules. //if there's not one, then we have to split the tree and put a generic root ArrayList<SingleRule> srs = new ArrayList<SingleRule>(); ArrayList<IPreferenceCondition> sortedConditions = sc.getSortedList(); ArrayList<IPreferenceCondition> popularConditions = sc.getMax(); if (rules.size()>sc.getMaxCounter()){ //split tree }else{ //tree does not have to be split. the most common condition will be the root of the tree for (int i=0; i<rules.size();i++){ srs.add(this.sortRule(rules.get(i),sortedConditions)); } } return srs; } /** * this method sorts a rule according to the order the preference conditions appear in the IPreferenceCondition list * @param sr the rule to sort * @param pcs the list in sorted order * @return the sorted Rule */ /* public SingleRule sortRule(SingleRule sr, ArrayList<IPreferenceCondition> pcs){ SingleRule sorted = new SingleRule(); sorted.setOutcome(sr.getOutcome()); for (int i=0; i<pcs.size(); i++){ IPreferenceCondition pc = pcs.get(i); if (sr.hasCondition(pc)){ sorted.addConditions(pc); sr.removeCondition(pc); } } for (int i=0; i<sr.getConditions().size();i++){ sorted.addConditions(sr.getConditions().get(i)); } return sorted; } */ }