/** *######################################################################### * * A component of the Gatherer application, part of the Greenstone digital * library suite from the New Zealand Digital Library Project at the * University of Waikato, New Zealand. * * Author: John Thompson, Greenstone Digital Library, University of Waikato * * Copyright (C) 1999 New Zealand Digital Library Project * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *######################################################################## */ package org.greenstone.gatherer.cdm; import java.util.*; import javax.swing.*; import org.greenstone.gatherer.DebugStream; import org.greenstone.gatherer.util.StaticStrings; import org.w3c.dom.*; /** This class provides ListModel like access to a list of nodes within a DOM model. * @author John Thompson, Greenstone Digital Library, University of Waikato * @version 2.3d */ public class DOMProxyListModel extends AbstractListModel { protected Element root; private DOMProxyListEntry class_type; private HashMap cache = new HashMap(); private NodeList children = null; private String tag_name; /** Constructor. * @param root the Element at the root of the subtree to be searched for appropriate child elements * @param tag_name the name of appropriate elements as a String * @param class_type the type of object to wrap the elements returned in, as a DOMProxyListEntry */ public DOMProxyListModel(Element root, String tag_name, DOMProxyListEntry class_type) { this.class_type = class_type; this.root = root; this.tag_name = tag_name; this.children = this.root.getElementsByTagName(this.tag_name); } /** Used to add an element into the underlying dom, and fire the appropriate repaint events. This version always adds the new element at the very head of the DOM. */ public synchronized void add(DOMProxyListEntry entry) { Element element = entry.getElement(); if(root.hasChildNodes()) { Node sibling = root.getFirstChild(); root.insertBefore(element, sibling); sibling = null; } else { root.appendChild(element); } element = null; // Regardless fire update event cache.clear(); fireIntervalAdded(this, 0, 0); } /** Used to add an element into the underlying dom, and fire the appropriate repaint events. * @param index the index where the element should be inserted (relative of the other elements in this proxy list) * @param entry the DOMProxyListEntry to be inserted */ public synchronized void add(int index, DOMProxyListEntry entry) { ///atherer.println("Add entry at " + index + " where size = " + getSize()); Element element = entry.getElement(); // retrieve the node where we want to insert if(index < children.getLength()) { Node sibling = children.item(index); // Find the parent node Node parent_node = sibling.getParentNode(); parent_node.insertBefore(element, sibling); sibling = null; } // If the index is too large, we are adding to the end of our list of entries. However you have to remember that this list is only a viewport on the entire DOM so there might be entries following this group that we actually want to insert before (not append at the very end!) else { // Retrieve the currently last entry index = children.getLength() - 1; Node sibling = null; Node parent_node = null; if(index >= 0) { sibling = children.item(index); parent_node = sibling.getParentNode(); sibling = sibling.getNextSibling(); } if(sibling != null && parent_node != null) { parent_node.insertBefore(element, sibling); } // Add to the root node else { index = 0; root.appendChild(element); } } // Regardless fire update event cache.clear(); fireIntervalAdded(this, index, index); } /** Used to add an element into the underlying dom, and fire the appropriate repaint events. This version inserts the new entry immediately -after- the given entry in the DOM. * @param entry the DOMProxyListEntry to be inserted * @param preceeding_entry the DOMProxyListEntry immediately before where we want the new entry */ public synchronized void addAfter(DOMProxyListEntry entry, DOMProxyListEntry preceeding_entry) { Element element = entry.getElement(); Element preceeding_sibling = preceeding_entry.getElement(); Node parent_node = preceeding_sibling.getParentNode(); Node following_sibling = preceeding_sibling.getNextSibling(); if(following_sibling != null) { parent_node.insertBefore(element, following_sibling); } else { parent_node.appendChild(element); } // Regardless fire update event cache.clear(); int index = indexOf(entry); fireIntervalAdded(this, index, index); } /** Used to add an element into the underlying dom, and fire the appropriate repaint events. This version inserts the new entry immediately -before- the given entry in the DOM. * @param entry the DOMProxyListEntry to be inserted * @param following_entry the DOMProxyListEntry immediately after where we want the new entry */ public synchronized void addBefore(DOMProxyListEntry entry, DOMProxyListEntry following_entry) { Element element = entry.getElement(); Element following_sibling = following_entry.getElement(); Node parent_node = following_sibling.getParentNode(); parent_node.insertBefore(element, following_sibling); // Regardless fire update event cache.clear(); int index = indexOf(entry); fireIntervalAdded(this, index, index); } public synchronized void add(Node parent, DOMProxyListEntry entry, Node sibling) { Element child = entry.getElement(); if(sibling != null) { parent.insertBefore(child, sibling); } else { parent.appendChild(child); } cache.clear(); int index = indexOf(entry); fireIntervalAdded(this, index, index); } /** Used to add an element into the underlying dom, and fire the appropriate repaint events. This version always adds the new element at the end of the children. */ public synchronized void append(DOMProxyListEntry entry) { Element element = entry.getElement(); root.appendChild(element); element = null; // Regardless fire update event cache.clear(); fireIntervalAdded(this, 0, 0); } public synchronized ArrayList children() { ArrayList child_list = new ArrayList(); int child_count = children.getLength(); for(int i = 0; i < child_count; i++) { child_list.add(getElementAt(i)); } return child_list; } public synchronized boolean contains(Object entry) { boolean found = false; int size = getSize(); for(int i = 0; !found && i < size; i++) { DOMProxyListEntry sibling = (DOMProxyListEntry) getElementAt(i); if(sibling.equals(entry)) { found = true; } } return found; } public synchronized Object getElementAt(int index) { Object object = cache.get(new Integer(index)); if (object != null) { return object; } // Retrieve the required element Element element = (Element) children.item(index); DebugStream.println("Element at index " + index + " not in cache: " + element); // Now wrap it in the object of the users choice object = class_type.create(element); cache.put(new Integer(index), object); return object; } public synchronized int indexOf(DOMProxyListEntry entry) { Element element = entry.getElement(); int children_length = children.getLength(); for(int i = 0; i < children_length; i++) { Node node = children.item(i); if(element == node) { return i; } } return -1; } public synchronized int getSize() { if(children == null) { children = root.getElementsByTagName(tag_name); } return children.getLength(); } public synchronized void refresh() { fireContentsChanged(this, 0, getSize()); } public synchronized void refresh(DOMProxyListEntry entry) { int index = indexOf(entry); fireContentsChanged(this, index, index); } public synchronized void remove(DOMProxyListEntry entry) { remove(indexOf(entry)); } public synchronized void remove(int index) { // Retrieve the required element Node node = children.item(index); // Find its parent Node parent_node = node.getParentNode(); // Now remove it parent_node.removeChild(node); // Refresh model cache.clear(); fireIntervalRemoved(this, index, index); } /** Changes the 'root' element that this list sources its information from. * @param root the new root Element */ public synchronized void setRoot(Element root) { this.children = null; cache.clear(); this.root = root; this.children = this.root.getElementsByTagName(this.tag_name); fireContentsChanged(this, 0, getSize()); } public synchronized void setAssigned(boolean assigned) { if (assigned) { root.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, StaticStrings.TRUE_STR); } else { root.setAttribute(StaticStrings.ASSIGNED_ATTRIBUTE, StaticStrings.FALSE_STR); } } public boolean isAssigned() { return (root.getAttribute(StaticStrings.ASSIGNED_ATTRIBUTE).equals("") || root.getAttribute(StaticStrings.ASSIGNED_ATTRIBUTE).equals(StaticStrings.TRUE_STR)); } }