Update one document from another and retunr if an update was needed


import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import lotus.domino.DateTime;
import lotus.domino.Document;
import lotus.domino.Item;
import lotus.domino.NotesException;

import com.ibm.ctp.NSFUtils;

/**
 * @author Stephan H. Wissel st.wissel@sg.ibm.com
 *          Compares 2 documents and
 *         updates changes from the source document to the target document based
 *         on item change dates. One way sync. Documents don't get saved -
 *         that's job of the code calling this
 */
public class DocumentUpdater {

	private final Document				docSource;
	private final Document				docTarget;

	// We copy only field in the fieldsToCopy range
	private boolean						importAllItems	= true;

	// What fields to be copied from source to
	// target, key is the Target field
	private final Map<String, String>	fieldsToCopy	= new HashMap<String, String>();

	/**
	 * Default constructor that provides the two documents for a default
	 * operation nothing else is needed, but optional fields to copy can be
	 * introduced as parameters
	 * 
	 * @param source
	 * @param target
	 */
	public DocumentUpdater(final Document source, final Document target) {
		this.docSource = source;
		this.docTarget = target;
	}

	/**
	 * @return all fields to be copied, defaults to all
	 */
	public Map<String, String> getFieldsToCopy() {
		return fieldsToCopy;
	}

	/**
	 * @return Do we import all items or only selected ones? if selected ones
	 *         the item names can change
	 */
	public boolean isImportAllItems() {
		// If we don't have a field list, we must copy all documents
		if (this.fieldsToCopy == null || this.fieldsToCopy.isEmpty()) {
			return true;
		}
		return this.importAllItems;
	}

	/**
	 * @param fieldsToCopy
	 */
	public void setFieldsToCopy(Map<String, String> fieldsToCopy) {
		this.fieldsToCopy.clear();
		this.fieldsToCopy.putAll(fieldsToCopy);
	}

	/**
	 * @param importAllItems
	 */
	public void setImportAllItems(boolean importAllItems) {
		this.importAllItems = importAllItems;
	}

	/**
	 * Copies new data from the source document to the target document. If the
	 * data has been updated, items are copied
	 * 
	 * @return did the sync operation work (were updates processed)
	 * @throws NotesException
	 */
	@SuppressWarnings("unchecked")
	public boolean syncDocuments() throws NotesException {
		// We can't sync NULL values
		if (docSource == null || docTarget == null) {
			return false;
		}
		boolean updated = false;

		// Ee check for the timestamp, we don't need to do
		// anything if the timestamp is still valid

		if (docTarget.hasItem(ITEM_TIMESTAMP_SRC)) {
			DateTime timeStamp = (DateTime) docTarget.getItemValueDateTimeArray(ITEM_TIMESTAMP_SRC).get(0);
			DateTime lastUpdate = docSource.getLastModified();
			updated = updated || (lastUpdate.timeDifference(timeStamp) > 0);
			NSFUtils.shred(timeStamp, lastUpdate);
			// If the document hasn't changed we stop here!
			if (!updated) {
				return updated;
			}
		}

		// The names of all fields we processed, copied or not
		HashMap<String, String> processedItems = new HashMap<String, String>();

		// Now all items need to be checked and copied
		Vector allItems = docSource.getItems();

		// First through all source items
		for (Object itemObject : allItems) {
			Item curItem = (Item) itemObject;
			String itemName = curItem.getName();
			if (this.isItemToBeImported(itemName)) {
				// When we copy all items we keep the name, otherwise the name
				// is in the fieldsToCopy Map
				String targetItemName = this.isImportAllItems() ? itemName : this.fieldsToCopy.get(itemName.toLowerCase());
				// Note that we have processed it:
				processedItems.put(targetItemName, itemName);

				if (docTarget.hasItem(targetItemName)) {
					Item targetItem = docTarget.getFirstItem(targetItemName);
					if (targetItem.getLastModified().timeDifference(curItem.getLastModified()) > 0
							&& !targetItem.getText().equals(curItem.getText())) {
						updated = true;
						curItem.remove();
						curItem.copyItemToDocument(docTarget, targetItemName);
					}
					NSFUtils.shred(targetItem);
				} else {
					updated = true;
					curItem.copyItemToDocument(docTarget, targetItemName);
				}
			}
			NSFUtils.shred(curItem);
		}

		// Now reverse process to work on the no longer needed target items
		allItems = docTarget.getItems();

		for (Object itemObject : allItems) {
			Item curItem = (Item) itemObject;
			String itemName = curItem.getName();
			if (!processedItems.containsKey(itemName) && !this.isReservedFieldName(itemName)) {
				// This item is no longer in the source
				// and we don't want/need it
				curItem.remove();
				updated = true;
			}
			NSFUtils.shred(curItem);
		}

		if (updated) {
			docTarget.replaceItemValue(ITEM_TIMESTAMP_SRC, docSource.getLastModified());
			docTarget.replaceItemValue(ITEM_EXT_UNID, docSource.getUniversalID());
			// TODO: What is that caching mechanism?
		}
		return updated;
	}

	/**
	 * Check if an item is on the "guest list" - if the guest list is empty then
	 * all items are invited (of course not the local ones
	 * 
	 * @param itemName
	 * @return true if the item needs to be imported
	 */
	private boolean isItemToBeImported(String itemName) {

		// No internal or special field names
		if (this.isReservedFieldName(itemName)) {
			return false;
		}

		// If we have no item name map we presume we copy all items
		if (this.importAllItems || this.fieldsToCopy == null || this.fieldsToCopy.isEmpty()) {
			return true;
		}

		// Finally we check if it is in the list
		return this.fieldsToCopy.containsKey(itemName);
	}

	/**
	 * Check for fields we don't want to copy
	 * 
	 * @param itemName
	 * @return true if is is a special field we don't import
	 */
	private boolean isReservedFieldName(String itemName) {
		return (itemName.equalsIgnoreCase(ITEM_FORM) || itemName.equalsIgnoreCase(ITEM_AUTHORS)
				|| itemName.equalsIgnoreCase(ITEM_READERS) || itemName.toLowerCase().startsWith(ITEM_CTP_SFX)
				|| itemName.toLowerCase().startsWith(ITEM_CTP_DATESFX) || itemName.startsWith("$"));

	}
}
All code submitted to OpenNTF XSnippets, whether submitted as a "Snippet" or in the body of a Comment, is provided under the Apache License Version 2.0. See Terms of Use for full details.
No comments yetLogin first to comment...