diff --git a/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java b/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java index 43a0eb44e..106ce5d1a 100644 --- a/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java +++ b/src/net/java/sip/communicator/impl/callhistory/CallHistoryServiceImpl.java @@ -46,7 +46,7 @@ public class CallHistoryServiceImpl new String[] { "accountUID", "callStart", "callEnd", "dir", "callParticipantIDs", "callParticipantStart", "callParticipantEnd", "callParticipantStates", "callEndReason", - "callParticipantNames"}; + "callParticipantNames", "secondaryCallParticipantIDs"}; private static HistoryRecordStructure recordStructure = new HistoryRecordStructure(STRUCTURE_NAMES); @@ -75,6 +75,9 @@ public class CallHistoryServiceImpl private HistoryReader historyReader; + private List callHistoryRecordlisteners + = new LinkedList(); + /** * Returns the underlying history service. * @return the underlying history service @@ -685,6 +688,7 @@ private void writeCall( CallRecordImpl callRecord, StringBuffer callPeerStartTime = new StringBuffer(); StringBuffer callPeerEndTime = new StringBuffer(); StringBuffer callPeerStates = new StringBuffer(); + StringBuffer callPeerSecondaryIDs = new StringBuffer(); for (CallPeerRecord item : callRecord .getPeerRecords()) @@ -696,6 +700,7 @@ private void writeCall( CallRecordImpl callRecord, callPeerStartTime.append(DELIM); callPeerEndTime.append(DELIM); callPeerStates.append(DELIM); + callPeerSecondaryIDs.append(DELIM); } callPeerIDs.append(item.getPeerAddress()); @@ -703,6 +708,10 @@ private void writeCall( CallRecordImpl callRecord, callPeerStartTime.append(sdf.format(item.getStartTime())); callPeerEndTime.append(sdf.format(item.getEndTime())); callPeerStates.append(item.getState().getStateString()); + callPeerSecondaryIDs.append( + item.getPeerSecondaryAddress() == null? + "" : item.getPeerSecondaryAddress()); + } historyWriter.addRecord(new String[] { @@ -716,7 +725,8 @@ private void writeCall( CallRecordImpl callRecord, callPeerEndTime.toString(), callPeerStates.toString(), String.valueOf(callRecord.getEndReason()), - callPeerNames.toString()}, + callPeerNames.toString(), + callPeerSecondaryIDs.toString()}, new Date()); // this date is when the history // record is written } @@ -871,6 +881,54 @@ public void removeSearchProgressListener( } } + /** + * Adding CallHistoryRecordListener listener to the list. + * + * @param listener CallHistoryRecordListener + */ + public void addCallHistoryRecordListener(CallHistoryPeerRecordListener + listener) + { + synchronized (callHistoryRecordlisteners) + { + callHistoryRecordlisteners.add(listener); + } + } + + /** + * Removing CallHistoryRecordListener listener + * + * @param listener CallHistoryRecordListener + */ + public void removeCallHistoryRecordListener( + CallHistoryPeerRecordListener listener) + { + synchronized(callHistoryRecordlisteners){ + callHistoryRecordlisteners.remove(listener); + } + } + + /** + * Fires the given event to all CallHistoryRecordListener listeners + * @param event the CallHistoryRecordReceivedEvent event to be + * fired + */ + private void fireCallHistoryRecordReceivedEvent( + CallHistoryPeerRecordEvent event) + { + List tmpListeners; + synchronized (callHistoryRecordlisteners) + { + tmpListeners = new LinkedList( + callHistoryRecordlisteners); + } + + for(CallHistoryPeerRecordListener listener : tmpListeners) + { + listener.callPeerRecordReceived(event); + } + } + /** * Add the registered CallHistorySearchProgressListeners to the given * HistoryReader @@ -990,6 +1048,8 @@ public void peerStateChanged(CallPeerChangeEvent evt) newRec.setDisplayName(callPeer.getDisplayName()); callRecord.getPeerRecords().add(newRec); + fireCallHistoryRecordReceivedEvent(new CallHistoryPeerRecordEvent( + callPeer.getAddress(), startDate)); } /** @@ -1025,6 +1085,152 @@ private void handlePeerRemoved( CallPeer callPeer, } } + /** + * Updates the secondary address field of call record. + * @param date the start date of the record which will be updated. + * @param peer the peer of the record which will be updated. + * @param address the value of the secondary address . + */ + public void updateCallRecordPeerSecondaryAddress(final Date date, + final CallPeer peer, + final String address) + { + CallRecord record = findCallRecord(peer.getCall()); + if(record != null) + { + for(CallPeerRecord peerRecord : record.getPeerRecords()) + { + if(peerRecord.getPeerAddress().equals(peer.getAddress())) + { + peerRecord.setPeerSecondaryAddress(address); + } + } + return; + } + + History history; + try + { + history = this.getHistory(null, null); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + return; + } + HistoryWriter historyWriter = history.getWriter(); + + HistoryWriter.HistoryRecordUpdater updater = new HistoryWriter.HistoryRecordUpdater() + { + private HistoryRecord record; + + private int dateIndex; + + private int peerIDIndex; + + private int peerSecondaryIDIndex; + + @Override + public void setHistoryRecord(HistoryRecord historyRecord) + { + record = historyRecord; + String propertyNames[] = record.getPropertyNames(); + for(int i = 0; i < propertyNames.length; i++) + { + if(propertyNames[i].equals(STRUCTURE_NAMES[5])) + { + dateIndex = i; + } + + if(propertyNames[i].equals(STRUCTURE_NAMES[4])) + { + peerIDIndex = i; + } + + if(propertyNames[i].equals(STRUCTURE_NAMES[10])) + { + peerSecondaryIDIndex = i; + } + } + } + + @Override + public boolean isMatching() + { + String[] propertyVlaues = record.getPropertyValues(); + List peerIDs + = getCSVs(propertyVlaues[peerIDIndex]); + + int i = peerIDs.indexOf(peer.getAddress()); + if(i == -1) + return false; + + + String dateString = getCSVs(propertyVlaues[dateIndex]).get(i); + SimpleDateFormat sdf + = new SimpleDateFormat(HistoryService.DATE_FORMAT); + try + { + if(!sdf.parse(dateString).equals(date)) + return false; + } + catch (ParseException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + return false; + } + + String secondaryID + = getCSVs(propertyVlaues[peerSecondaryIDIndex]).get(i); + if(secondaryID != null) + return false; + + return true; + } + + + + @Override + public Map getUpdateChanges() + { + String[] propertyVlaues = record.getPropertyValues(); + List peerIDs + = getCSVs(propertyVlaues[peerIDIndex]); + + int i = peerIDs.indexOf(peer.getAddress()); + if(i == -1) + return null; + + List secondaryID + = getCSVs(record.getPropertyValues()[peerSecondaryIDIndex]); + secondaryID.set(i, peer.getAddress()); + String res = ""; + int j = 0; + for(String id : secondaryID) + { + if(j++ != 0) + res += DELIM; + res += id; + } + Map changesMap = new HashMap(); + changesMap.put(STRUCTURE_NAMES[10], res); + return changesMap; + } + }; + try + { + historyWriter.updateRecord(updater); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + /** * Finding a CallRecord for the given call * @@ -1083,6 +1289,7 @@ private void handleNewCall(Call sourceCall, String direction) currentCallRecords.add(newRecord); + // if has already perticipants Dispatch them Iterator iter = sourceCall.getCallPeers(); while (iter.hasNext()) diff --git a/src/net/java/sip/communicator/impl/history/HistoryWriterImpl.java b/src/net/java/sip/communicator/impl/history/HistoryWriterImpl.java index d7f81ab19..be7006734 100644 --- a/src/net/java/sip/communicator/impl/history/HistoryWriterImpl.java +++ b/src/net/java/sip/communicator/impl/history/HistoryWriterImpl.java @@ -308,4 +308,100 @@ public void updateRecord(String idProperty, String idValue, } } } + + /** + * Updates history record using given HistoryRecordUpdater instance + * to find which is the record to be updated and to get the new values for + * the fields + * @param updater the HistoryRecordUpdater instance. + */ + public void updateRecord(HistoryRecordUpdater updater) throws IOException + { + Iterator fileIterator = this.historyImpl.getFileList(); + String filename = null; + while (fileIterator.hasNext()) + { + filename = fileIterator.next(); + + Document doc = this.historyImpl.getDocumentForFile(filename); + + if(doc == null) + continue; + + NodeList nodes = doc.getElementsByTagName("record"); + + boolean changed = false; + + Node node; + for (int i = 0; i < nodes.getLength(); i++) + { + node = nodes.item(i); + updater.setHistoryRecord(createHistoryRecordFromNode(node)); + if(!updater.isMatching()) + continue; + + Map updates = updater.getUpdateChanges(); + for(String nodeName : updates.keySet()) + { + Element changedNode = + XMLUtils.findChild((Element)node, nodeName); + + if(changedNode != null) + { + Node changedNestedNode = changedNode.getFirstChild(); + + changedNestedNode.setNodeValue(updates.get(nodeName)); + changed = true; + } + } + } + + if(changed) + { + // write changes + synchronized (this.docWriteLock) + { + this.historyImpl.writeFile(filename, doc); + } + + // this prevents that the current writer, which holds + // instance for the last document he is editing will not + // override our last changes to the document + if(filename.equals(this.currentFile)) + { + this.currentDoc = doc; + } + + break; + } + } + } + + /** + * Creates HistoryRecord instance from Node object. + * @param node the node + * @return the HistoryRecord instance + */ + private HistoryRecord createHistoryRecordFromNode(Node node) + { + + HistoryRecordStructure structure + = historyImpl.getHistoryRecordsStructure(); + String propertyValues[] = new String[structure.getPropertyCount()]; + + int i = 0; + for(String propertyName : structure.getPropertyNames()) + { + Element childNode = XMLUtils.findChild((Element)node, propertyName); + if(childNode == null) + { + i++; + continue; + } + propertyValues[i] = childNode.getTextContent(); + i++; + } + + return new HistoryRecord(structure, propertyValues); + } } diff --git a/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java b/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java index f08874ba1..668efd1da 100644 --- a/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java +++ b/src/net/java/sip/communicator/service/callhistory/CallHistoryService.java @@ -10,12 +10,14 @@ import net.java.sip.communicator.service.callhistory.event.*; import net.java.sip.communicator.service.contactlist.*; +import net.java.sip.communicator.service.protocol.*; /** * The Call History Service stores info about calls made from various protocols * * @author Alexander Pelov * @author Damian Minkov + * @author Hristo Terezov */ public interface CallHistoryService { @@ -143,4 +145,14 @@ public void addSearchProgressListener( */ public void removeSearchProgressListener( CallHistorySearchProgressListener listener); + + /** + * Updates the secondary address field of call record. + * @param date the start date of the record which will be updated. + * @param peer the peer of the record which will be updated. + * @param address the value of the secondary address . + */ + public void updateCallRecordPeerSecondaryAddress(final Date date, + final CallPeer peer, + final String address); } diff --git a/src/net/java/sip/communicator/service/callhistory/CallPeerRecord.java b/src/net/java/sip/communicator/service/callhistory/CallPeerRecord.java index a6f6ce56a..c673bccef 100644 --- a/src/net/java/sip/communicator/service/callhistory/CallPeerRecord.java +++ b/src/net/java/sip/communicator/service/callhistory/CallPeerRecord.java @@ -10,6 +10,7 @@ * from the Call History Service * * @author Damian Minkov + * @author Hristo Terezov */ public class CallPeerRecord { @@ -33,6 +34,11 @@ public class CallPeerRecord */ protected Date endTime = null; + /** + * The secondary address of the peer. + */ + protected String secondaryPeerAddress = null; + /** * The state of CallPeer. */ @@ -99,4 +105,22 @@ public CallPeerState getState() { return state; } + + /** + * Sets secondary address to the CallPeerRecord + * @param address the address to be set. + */ + public void setPeerSecondaryAddress(String address) + { + secondaryPeerAddress = address; + } + + /** + * Returns the secondary address to the CallPeerRecord + * @return the secondary address to the CallPeerRecord + */ + public String getPeerSecondaryAddress() + { + return secondaryPeerAddress; + } } diff --git a/src/net/java/sip/communicator/service/callhistory/event/CallHistoryPeerRecordEvent.java b/src/net/java/sip/communicator/service/callhistory/event/CallHistoryPeerRecordEvent.java new file mode 100644 index 000000000..c57071c8f --- /dev/null +++ b/src/net/java/sip/communicator/service/callhistory/event/CallHistoryPeerRecordEvent.java @@ -0,0 +1,58 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.service.callhistory.event; + +import java.util.*; + + +/** + * An event which is fired when a new call peer history record is added. + * @author Hristo Terezov + */ +public class CallHistoryPeerRecordEvent + extends EventObject +{ + + /** + * Serial ID. + */ + private static final long serialVersionUID = 1L; + + /** + * The date when the call peer have started the conversation. + */ + private static Date startDate; + + /** + * Constructs new CallHistoryPeerRecordEvent event. + * @param peer the peer associated with the event. + */ + public CallHistoryPeerRecordEvent(String peerAddress, Date startDate) + { + super(peerAddress); + this.startDate = startDate; + } + + /** + * Returns the start date property of the event. + * @return the start date property of the event. + */ + public Date getStartDate() + { + return startDate; + } + + /** + * Returns the peer address of the event. + * @return the peer address of the event. + */ + public String getPeerAddress() + { + return (String) getSource(); + } + +} diff --git a/src/net/java/sip/communicator/service/callhistory/event/CallHistoryPeerRecordListener.java b/src/net/java/sip/communicator/service/callhistory/event/CallHistoryPeerRecordListener.java new file mode 100644 index 000000000..d62c4faed --- /dev/null +++ b/src/net/java/sip/communicator/service/callhistory/event/CallHistoryPeerRecordListener.java @@ -0,0 +1,21 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.service.callhistory.event; + +/** + * Interface for a listener that will be notified when new call peer is added to + * the history. + * @author Hristo Terezov + */ +public interface CallHistoryPeerRecordListener +{ + /** + * This method is called when CallHistoryPeerRecordEvent occurred. + * @param event the event + */ + public void callPeerRecordReceived(CallHistoryPeerRecordEvent event); +} diff --git a/src/net/java/sip/communicator/service/history/HistoryWriter.java b/src/net/java/sip/communicator/service/history/HistoryWriter.java index d62e320e3..bb7ca3050 100644 --- a/src/net/java/sip/communicator/service/history/HistoryWriter.java +++ b/src/net/java/sip/communicator/service/history/HistoryWriter.java @@ -13,6 +13,7 @@ /** * @author Alexander Pelov + * @author Hristo Terezov */ public interface HistoryWriter { @@ -61,4 +62,39 @@ public interface HistoryWriter { */ public void updateRecord(String idProperty, String idValue, String property, String newValue) throws IOException; + + /** + * Updates history record using given HistoryRecordUpdater instance + * to find which is the record to be updated and to get the new values for + * the fields + * @param updater the HistoryRecordUpdater instance. + */ + public void updateRecord(HistoryRecordUpdater updater) throws IOException; + + /** + * This interface is used to find a history record to update and to get the + * new values for the record. + */ + public interface HistoryRecordUpdater + { + /** + * Sets the current history record. + * @param historyRecord the history record. + */ + public void setHistoryRecord(HistoryRecord historyRecord); + + /** + * Checks if the history record should be updated or not + * @return true if the record should be updated. + */ + public boolean isMatching(); + + /** + * Returns a map with the names and new values of the fields that will + * be updated + * @return a map with the names and new values of the fields that will + * be updated + */ + public Map getUpdateChanges(); + } }