/* QueryResult.java This class is a serializable object-list result object, which conveys results from a query/list operation along with methods that can be used to extract the results out of the query/list. Created: 1 October 1997 Module By: Jonathan Abbey, jonabbey@arlut.utexas.edu ----------------------------------------------------------------------- Ganymede Directory Management System Copyright (C) 1996-2014 The University of Texas at Austin Ganymede is a registered trademark of The University of Texas at Austin Contact information Web site: http://www.arlut.utexas.edu/gash2 Author Email: ganymede_author@arlut.utexas.edu Email mailing list: ganymede@arlut.utexas.edu US Mail: Computer Science Division Applied Research Laboratories The University of Texas at Austin PO Box 8029, Austin TX 78713-8029 Telephone: (512) 835-3200 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, see <http://www.gnu.org/licenses/>. */ package arlut.csd.ganymede.common; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Comparator; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; import java.util.Vector; import arlut.csd.JDataComponent.listHandle; import arlut.csd.Util.VecSortInsert; /*------------------------------------------------------------------------------ class QueryResult ------------------------------------------------------------------------------*/ /** * <p>This class is a serializable object-list result object, which * conveys sorted results from a query/list operation along with * methods that can be used to extract the results out of the * query/list.</p> * * <p>The individual elements of a Query Result are {@link * arlut.csd.ganymede.common.ObjectHandle ObjectHandles}, which * transport labeled Invids with a set of object status bits, or in * the case of QueryResults returned by {@link * arlut.csd.ganymede.server.StringDBField#choices()}, simply the * Strings provided as valid choices for string fields.</p> */ public class QueryResult implements java.io.Externalizable { static final boolean debug = false; public static final Comparator comparator = new Comparator() { public int compare(Object o_a, Object o_b) { ObjectHandle a, b; a = (ObjectHandle) o_a; b = (ObjectHandle) o_b; int comp = 0; comp = a.getLabel().compareToIgnoreCase(b.getLabel()); if (comp < 0) { return -1; } else if (comp > 0) { return 1; } else { return 0; } } }; // --- private Vector<ObjectHandle> handles = null; private Set<Invid> invidSet = null; private Set<String> labelSet = null; private boolean nonEditable = false; /* -- */ public QueryResult() { this.clear(); } /** * QueryResult taking a single boolean param for backwards * compatibility now that we are no longer distinguishing between * QueryResults for transport and not. */ @Deprecated public QueryResult(boolean param) { this(); } /** * Initializes this QueryResult to its empty state. */ public synchronized void clear() { this.invidSet = new HashSet<Invid>(); this.labelSet = new HashSet<String>(); this.handles = new Vector<ObjectHandle>(); } /** * Returns a private copy of this QueryResult that will not be * affected if this QueryResult is subsequently modified. */ public synchronized QueryResult getCopy() { QueryResult copy = new QueryResult(); copy.invidSet = new HashSet<Invid>(this.invidSet); copy.labelSet = new HashSet<String>(this.labelSet); copy.handles = new Vector<ObjectHandle>(this.handles); return copy; } /** * This method is used to add a simple String to the QueryResult's * serializable buffer. It is intended to be called on the server, * but may also be called on the client for result augmentation. */ public void addRow(String label) { addRow(null, label, false, false, false, false); } /** * This method is used to add an object's information to * the QueryResult's serializable buffer. It is intended * to be called on the server, but may also be called on * the client for result augmentation. */ public void addRow(Invid invid, String label, boolean editable) { addRow(invid, label, false, false, false, editable); } /** * This method is used to add an object's information to * the QueryResult's serializable buffer. It is intended * to be called on the server, but may also be called on * the client for result augmentation. */ public void addRow(ObjectHandle handle) { addRow(handle.getInvid(), handle.getLabel(), handle.isInactive(), handle.isExpirationSet(), handle.isRemovalSet(), handle.isEditable()); } /** * This method is used to lock this QueryResult so that it cannot be * changed. This will be used in circumstances where the Ganymede * server is caching a singleton QueryResult for some static list of * values. */ public void setNonEditable() { this.nonEditable = true; } /** * This method is used to add an object's information to * the QueryResult's serializable buffer. It is intended * to be called on the server, but may also be called on * the client for result augmentation. */ public synchronized void addRow(Invid invid, String label, boolean inactive, boolean expirationSet, boolean removalSet, boolean editable) { if (debug) { System.err.println("QueryResult: addRow(" + invid + "," + label + ")"); } if (this.nonEditable) { throw new RuntimeException("QueryResult.addRow(): non-editable QueryResult"); } if (label == null) { throw new NullPointerException("QueryResult.addRow(): null label passed in"); } // don't add an object we've already got here if (invid != null && containsInvid(invid)) { return; } else if (invid == null && containsLabel(label)) { return; } this.handles.add(new ObjectHandle(label, invid, inactive, expirationSet, removalSet, editable)); if (invid != null) { this.invidSet.add(invid); } labelSet.add(label); } // *** // // The following methods are intended to be called on a QueryResult // after it has been serialized and passed from the server to the // client. // // *** public Vector<ObjectHandle> getHandles() { if (this.nonEditable) { return new Vector<ObjectHandle>(this.handles); } return this.handles; } public Invid getInvid(int row) { return this.handles.get(row).getInvid(); } public Vector<Invid> getInvids() { Vector<Invid> invidList = new Vector<Invid>(handles.size()); for (ObjectHandle handle: this.handles) { invidList.add(handle.getInvid()); } return invidList; } public Vector<String> getLabels() { Vector<String> labelList = new Vector<String>(handles.size()); for (ObjectHandle handle: this.handles) { labelList.add(handle.getLabel()); } return labelList; } public String getLabel(int row) { return this.handles.get(row).getLabel(); } public int size() { return this.handles.size(); } /** * Returns a complete {@link arlut.csd.JDataComponent.listHandle listHandle} * Vector representation of the results included in this QueryResult. */ public Vector<listHandle> getListHandles() { return getListHandles(true, true); } /** * Returns a (possibly filtered) {@link * arlut.csd.JDataComponent.listHandle listHandle} Vector * representation of the results included in this QueryResult. * * @param includeInactives if false, inactive objects' handles won't * be included in the returned vector * @param includeNonEditables if false, non-editable objects' * handles won't be included in the returned vector */ public synchronized Vector<listHandle> getListHandles(boolean includeInactives, boolean includeNonEditables) { Vector<listHandle> valueHandles = new Vector<listHandle>(); /* -- */ for (ObjectHandle handle: this.handles) { if ((includeInactives || !handle.isInactive()) && (includeNonEditables || handle.isEditable())) { valueHandles.add(handle.getListHandle()); } } return valueHandles; } /** * Returns the listHandle for this row. */ public listHandle getListHandle(int row) { return this.handles.get(row).getListHandle(); } /** * Returns the ObjectHandle for this row. */ public ObjectHandle getObjectHandle(int row) { return this.handles.get(row); } // *** // // pre-serialization (server-side) methods // // *** /** * <p>This method is provided for the server to optimize it's * QueryResult loading operations, and is not intended for use * post-serialization.</p> */ public synchronized boolean containsInvid(Invid invid) { return this.invidSet.contains(invid); } /** * <p>This method is provided for the server to optimize it's * QueryResult loading operations, and is not intended for use * post-serialization.</p> */ public synchronized boolean containsLabel(String label) { return this.labelSet.contains(label); } /** * <p>This is a pre-serialization method for concatenating another * (for transport) QueryResult to ourself.</p> */ public synchronized void append(QueryResult result) { if (this.nonEditable) { throw new RuntimeException("Can't append to a non-editable QueryResult"); } this.handles.addAll(result.getHandles()); this.invidSet.addAll(result.invidSet); this.labelSet.addAll(result.labelSet); } /** * <p>This method returns a QueryResult which holds the intersection * of the contents of this QueryResult and the contents of * operand.</p> */ public synchronized QueryResult intersection(QueryResult operand) { if (this.nonEditable) { throw new RuntimeException("Can't intersect a non-editable QueryResult"); } QueryResult result = new QueryResult(); /* -- */ if (operand == null || operand.size() == 0) { return result; } for (ObjectHandle handle: this.handles) { if (handle.getInvid() != null) { if (operand.containsInvid(handle.getInvid())) { result.addRow(handle); } } else { if (operand.containsLabel(handle.getLabel())) { result.addRow(handle); } } } return result; } // externalization methods public synchronized void writeExternal(ObjectOutput out) throws IOException { out.writeInt(this.handles.size()); for (ObjectHandle handle: this.handles) { handle.writeExternal(out); } } public synchronized void readExternal(ObjectInput in) throws IOException { VecSortInsert inserter = new VecSortInsert(comparator); int size = in.readInt(); this.handles = new Vector<ObjectHandle>(size); for (int i = 0; i < size; i++) { ObjectHandle handle = new ObjectHandle(); handle.readExternal(in); invidSet.add(handle.getInvid()); labelSet.add(handle.getLabel()); inserter.insert(this.handles, handle); } if (this.handles.size() != size) { throw new RuntimeException("QueryResult: bad handles size post readExternal"); } } }