[Bedework-commit] r369 - in trunk/calendar3: bldfiles calCore/src/org/bedework/calcore/hibernate calFacade/src/org/bedework/calfacade calFacade/src/org/bedework/calfacade/base calFacade/src/org/bedework/calfacade/ifs caldav/src/edu/rpi/cct/uwcal/caldav/calquery calsvc calsvc/src/org/bedework/calsvc calsvci/src/org/bedework/calsvci common common/src/edu/rpi/cct common/src/edu/rpi/cct/misc common/src/edu/rpi/cct/misc/indexing common/src/edu/rpi/cct/uwcal lib webclient/src/org/bedework/webclient webclient/war/docs

svnadmin at bedework.org svnadmin at bedework.org
Thu Apr 13 09:35:11 EDT 2006


Author: douglm
Date: 2006-04-13 09:35:10 -0400 (Thu, 13 Apr 2006)
New Revision: 369

Added:
   trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexKey.java
   trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexLuceneDefs.java
   trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexLuceneImpl.java
   trunk/calendar3/common/src/edu/rpi/cct/misc/
   trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/
   trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/Index.java
   trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexDummyImpl.java
   trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexException.java
   trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexLuceneImpl.java
   trunk/calendar3/lib/lucene-core-1.9.1.jar
Removed:
   trunk/calendar3/common/src/edu/rpi/cct/uwcal/common/
Modified:
   trunk/calendar3/bldfiles/defjars.properties
   trunk/calendar3/calCore/src/org/bedework/calcore/hibernate/Calendars.java
   trunk/calendar3/calCore/src/org/bedework/calcore/hibernate/CalintfImpl.java
   trunk/calendar3/calFacade/src/org/bedework/calfacade/BwFreeBusy.java
   trunk/calendar3/calFacade/src/org/bedework/calfacade/base/CalintfBase.java
   trunk/calendar3/calFacade/src/org/bedework/calfacade/ifs/CalendarsI.java
   trunk/calendar3/caldav/src/edu/rpi/cct/uwcal/caldav/calquery/FreeBusyQuery.java
   trunk/calendar3/calsvc/build.xml
   trunk/calendar3/calsvc/src/org/bedework/calsvc/CalSvc.java
   trunk/calendar3/calsvci/src/org/bedework/calsvci/CalSvcI.java
   trunk/calendar3/common/build.xml
   trunk/calendar3/webclient/src/org/bedework/webclient/BwFreeBusyAction.java
   trunk/calendar3/webclient/war/docs/freeBusy.jsp
Log:
Code to support lucene indexing. Nothing being used yet but builds OK

Modified: trunk/calendar3/bldfiles/defjars.properties
===================================================================
--- trunk/calendar3/bldfiles/defjars.properties	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/bldfiles/defjars.properties	2006-04-13 13:35:10 UTC (rev 369)
@@ -61,6 +61,10 @@
 ical4j.jar.name=ical4j-0.9.19-pre.jar
 ical4j.jar=${org.bedework.default.lib}/${ical4j.jar.name}
 
+#      lucene.jar           Used for indexing and searching
+lucene.jar.name=lucene-core-1.9.1.jar
+lucene.jar=${org.bedework.default.lib}/${lucene.jar.name}
+
 #      regexp.jar           Handles regular expressions
 regexp.jar=${org.bedework.default.lib}/jakarta-regexp-1.3.jar
 

Modified: trunk/calendar3/calCore/src/org/bedework/calcore/hibernate/Calendars.java
===================================================================
--- trunk/calendar3/calCore/src/org/bedework/calcore/hibernate/Calendars.java	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/calCore/src/org/bedework/calcore/hibernate/Calendars.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -221,10 +221,14 @@
   }
 
   public BwCalendar getCalendars() throws CalFacadeException {
+    return getCalendars(getUser());
+  }
+
+  public BwCalendar getCalendars(BwUser user) throws CalFacadeException {
     HibSession sess = getSess();
 
     sess.namedQuery("getCalendarByPath");
-    sess.setString("path", userCalendarRootPath + "/" + getUser().getAccount());
+    sess.setString("path", userCalendarRootPath + "/" + user.getAccount());
     sess.cacheableQuery();
 
     BwCalendar cal = (BwCalendar)sess.getUnique();

Modified: trunk/calendar3/calCore/src/org/bedework/calcore/hibernate/CalintfImpl.java
===================================================================
--- trunk/calendar3/calCore/src/org/bedework/calcore/hibernate/CalintfImpl.java	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/calCore/src/org/bedework/calcore/hibernate/CalintfImpl.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -816,6 +816,12 @@
     return calendars.getCalendars();
   }
 
+  public BwCalendar getCalendars(BwUser user) throws CalFacadeException {
+    checkOpen();
+
+    return calendars.getCalendars(user);
+  }
+
   public Collection getCalendarCollections() throws CalFacadeException {
     checkOpen();
 

Modified: trunk/calendar3/calFacade/src/org/bedework/calfacade/BwFreeBusy.java
===================================================================
--- trunk/calendar3/calFacade/src/org/bedework/calfacade/BwFreeBusy.java	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/calFacade/src/org/bedework/calfacade/BwFreeBusy.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -71,7 +71,7 @@
   private BwDateTime start;
   private BwDateTime end;
 
-  /** Collection of FreeBusyComponentVO
+  /** Collection of BwFreeBusyComponent
    */
   private Collection times;
 

Modified: trunk/calendar3/calFacade/src/org/bedework/calfacade/base/CalintfBase.java
===================================================================
--- trunk/calendar3/calFacade/src/org/bedework/calfacade/base/CalintfBase.java	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/calFacade/src/org/bedework/calfacade/base/CalintfBase.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -425,6 +425,10 @@
     throw new CalFacadeUnimplementedException();
   }
 
+  public BwCalendar getCalendars(BwUser user) throws CalFacadeException {
+    throw new CalFacadeUnimplementedException();
+  }
+
   public Collection getCalendarCollections() throws CalFacadeException {
     checkOpen();
 

Modified: trunk/calendar3/calFacade/src/org/bedework/calfacade/ifs/CalendarsI.java
===================================================================
--- trunk/calendar3/calFacade/src/org/bedework/calfacade/ifs/CalendarsI.java	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/calFacade/src/org/bedework/calfacade/ifs/CalendarsI.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -1,3 +1,56 @@
+/*
+ Copyright (c) 2000-2005 University of Washington.  All rights reserved.
+
+ Redistribution and use of this distribution in source and binary forms,
+ with or without modification, are permitted provided that:
+
+   The above copyright notice and this permission notice appear in
+   all copies and supporting documentation;
+
+   The name, identifiers, and trademarks of the University of Washington
+   are not used in advertising or publicity without the express prior
+   written permission of the University of Washington;
+
+   Recipients acknowledge that this distribution is made available as a
+   research courtesy, "as is", potentially with defects, without
+   any obligation on the part of the University of Washington to
+   provide support, services, or repair;
+
+   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR
+   IMPLIED, WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION
+   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+   PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
+   WASHINGTON BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+   DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+   PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT (INCLUDING
+   NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION WITH
+   THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* **********************************************************************
+    Copyright 2005 Rensselaer Polytechnic Institute. All worldwide rights reserved.
+
+    Redistribution and use of this distribution in source and binary forms,
+    with or without modification, are permitted provided that:
+       The above copyright notice and this permission notice appear in all
+        copies and supporting documentation;
+
+        The name, identifiers, and trademarks of Rensselaer Polytechnic
+        Institute are not used in advertising or publicity without the
+        express prior written permission of Rensselaer Polytechnic Institute;
+
+    DISCLAIMER: The software is distributed" AS IS" without any express or
+    implied warranty, including but not limited to, any implied warranties
+    of merchantability or fitness for a particular purpose or any warrant)'
+    of non-infringement of any current or pending patent rights. The authors
+    of the software make no representations about the suitability of this
+    software for any particular purpose. The entire risk as to the quality
+    and performance of the software is with the user. Should the software
+    prove defective, the user assumes the cost of all necessary servicing,
+    repair or correction. In particular, neither Rensselaer Polytechnic
+    Institute, nor the authors of the software are liable for any indirect,
+    special, consequential, or incidental damages related to the software,
+    to the maximum extent the law permits.
+*/
 package org.bedework.calfacade.ifs;
 
 import org.bedework.calfacade.BwCalendar;
@@ -40,9 +93,17 @@
 
   /** Returns calendars owned by the current user.
    *
-   * <p>For anonymous (public events) access, this method returns the same
-   * as getPublicCalendars().
+   * <p>For authenticated, personal access this always returns the user
+   * entry in the /user calendar tree, e.g. for user smithj it would return
+   * an entry /user/smithj
    *
+   * @return BwCalendar   root with all children attached
+   * @throws CalFacadeException
+   */
+  public BwCalendar getCalendars() throws CalFacadeException;
+
+  /** Returns calendars owned by the given user.
+   *
    * <p>For authenticated, personal access this always returns the user
    * entry in the /user calendar tree, e.g. for user smithj it would return
    * an entry smithj
@@ -50,7 +111,7 @@
    * @return BwCalendar   root with all children attached
    * @throws CalFacadeException
    */
-  public BwCalendar getCalendars() throws CalFacadeException;
+  public BwCalendar getCalendars(BwUser user) throws CalFacadeException;
 
   /** Return a list of user calendars in which calendar objects can be
    * placed by the current user.

Modified: trunk/calendar3/caldav/src/edu/rpi/cct/uwcal/caldav/calquery/FreeBusyQuery.java
===================================================================
--- trunk/calendar3/caldav/src/edu/rpi/cct/uwcal/caldav/calquery/FreeBusyQuery.java	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/caldav/src/edu/rpi/cct/uwcal/caldav/calquery/FreeBusyQuery.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -146,7 +146,8 @@
 
       try {
         BwFreeBusy fb = svci.getFreeBusy(null, new BwUser(user),
-                                         tr.getStart(), tr.getEnd(), null);
+                                         tr.getStart(), tr.getEnd(), null,
+                                         false);
 
         if (debug) {
           trace("Got " + fb);

Modified: trunk/calendar3/calsvc/build.xml
===================================================================
--- trunk/calendar3/calsvc/build.xml	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/calsvc/build.xml	2006-04-13 13:35:10 UTC (rev 369)
@@ -30,6 +30,7 @@
     <path id="compile.classpath">
       <pathelement location="${ical4j.jar}"/>
       <pathelement location="${log4j.jar}"/>
+      <pathelement location="${lucene.jar}"/>
       <pathelement location="${org.bedework.calsvci.jar}"/>
       <pathelement location="${org.bedework.calcore.jar}"/>
       <pathelement location="${org.bedework.common.jar}"/>

Added: trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexKey.java
===================================================================
--- trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexKey.java	                        (rev 0)
+++ trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexKey.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -0,0 +1,394 @@
+/*
+ Copyright (c) 2000-2005 University of Washington.  All rights reserved.
+
+ Redistribution and use of this distribution in source and binary forms,
+ with or without modification, are permitted provided that:
+
+   The above copyright notice and this permission notice appear in
+   all copies and supporting documentation;
+
+   The name, identifiers, and trademarks of the University of Washington
+   are not used in advertising or publicity without the express prior
+   written permission of the University of Washington;
+
+   Recipients acknowledge that this distribution is made available as a
+   research courtesy, "as is", potentially with defects, without
+   any obligation on the part of the University of Washington to
+   provide support, services, or repair;
+
+   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR
+   IMPLIED, WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION
+   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+   PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
+   WASHINGTON BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+   DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+   PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT (INCLUDING
+   NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION WITH
+   THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* **********************************************************************
+    Copyright 2005 Rensselaer Polytechnic Institute. All worldwide rights reserved.
+
+    Redistribution and use of this distribution in source and binary forms,
+    with or without modification, are permitted provided that:
+       The above copyright notice and this permission notice appear in all
+        copies and supporting documentation;
+
+        The name, identifiers, and trademarks of Rensselaer Polytechnic
+        Institute are not used in advertising or publicity without the
+        express prior written permission of Rensselaer Polytechnic Institute;
+
+    DISCLAIMER: The software is distributed" AS IS" without any express or
+    implied warranty, including but not limited to, any implied warranties
+    of merchantability or fitness for a particular purpose or any warrant)'
+    of non-infringement of any current or pending patent rights. The authors
+    of the software make no representations about the suitability of this
+    software for any particular purpose. The entire risk as to the quality
+    and performance of the software is with the user. Should the software
+    prove defective, the user assumes the cost of all necessary servicing,
+    repair or correction. In particular, neither Rensselaer Polytechnic
+    Institute, nor the authors of the software are liable for any indirect,
+    special, consequential, or incidental damages related to the software,
+    to the maximum extent the law permits.
+*/
+package org.bedework.calsvc;
+
+import org.bedework.calfacade.BwCalendar;
+import org.bedework.calfacade.CalFacadeDefs;
+import org.bedework.calsvci.CalSvcI;
+
+import edu.rpi.cct.misc.indexing.Index;
+import edu.rpi.cct.misc.indexing.IndexException;
+
+import java.io.CharArrayWriter;
+import java.util.Collection;
+
+/**
+ * @author Mike Douglass douglm @ rpi.edu
+ *
+ */
+public class BwIndexKey extends Index.Key {
+  private CalSvcI svci;
+
+  private String key1; // calendar:path
+  private String key2; // event:guid
+  private String key3; // event:recurrenceid
+
+  /** An event key is stored as a concatenated set of Strings,
+   * calendar:path + guid + (recurrenceid | null).
+   *
+   * <p>We set it here and use the decode methods to split it up.
+   */
+  private char[] encoded;
+
+  private String itemType;
+
+  public BwIndexKey() {
+  }
+
+  public BwIndexKey(CalSvcI svci) {
+    this.svci = svci;
+  }
+
+  public BwIndexKey(CalSvcI svci, float score) {
+    this.score = score;
+    this.svci = svci;
+  }
+
+  public void setItemType(String val) {
+    itemType = val;
+  }
+
+  public void setScore(float val) {
+    score = val;
+  }
+
+  public void setCalendarKey(String key1) {
+    this.key1 = key1;
+  }
+
+  public void setEventKey(String key) throws IndexException {
+    encoded = key.toCharArray();
+    pos = 0;
+
+    this.key1 = getKeyString();
+    this.key2 = getKeyString();
+    this.key3 = getKeyString();
+  }
+
+  public String makeEventKey(String key1, String key2,
+                             String key3) throws IndexException {
+    startEncoding();
+    encodeString(key1);
+    encodeString(key2);
+    encodeString(key3);
+
+    return getEncodedKey();
+  }
+
+  public Object getRecord() throws IndexException {
+    try {
+      if (itemType == null) {
+        throw new IndexException("org.bedework.index.nullkeyitemtype");
+      }
+
+      if (itemType.equals(BwIndexLuceneDefs.itemTypeCalendar)) {
+        return svci.getCalendar(key1);
+      }
+
+      if (itemType.equals(BwIndexLuceneDefs.itemTypeEvent)) {
+        BwCalendar cal = svci.getCalendar(key1);
+        if (cal == null) {
+          return null;
+        }
+        Collection eis = svci.getEvent(null, cal, key2, key3,
+                                       CalFacadeDefs.retrieveRecurExpanded);
+        if ((eis == null) || (eis.size() == 0)) {
+          return null;
+        }
+
+        return eis.iterator().next();
+      }
+
+      throw new IndexException(IndexException.unknownRecordType,
+                               itemType);
+    } catch (IndexException ie) {
+      throw ie;
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+  /* ====================================================================
+   *                 Key decoding methods
+   * ==================================================================== */
+
+  /** Current position in the key */
+  private int pos;
+
+  /** When encoding a key we build it here.
+   */
+  private CharArrayWriter caw;
+
+  /** Get next char from encoded value. Return < 0 for no more
+   *
+   * @return char value
+   */
+  public char getChar() {
+    if ((encoded == null) || (pos == encoded.length)) {
+      return (char)-1;
+    }
+
+    char c = encoded[pos];
+    pos++;
+
+    return c;
+  }
+
+  /** Back off one char
+   *
+   * @throws AccessException
+   */
+  public void back() throws IndexException {
+    back(1);
+  }
+
+  /** Back off n chars
+   *
+   * @param n   int number of chars
+   * @throws AccessException
+   */
+  public void back(int n) throws IndexException {
+    if (pos - n < 0) {
+      throw new IndexException("org.bedework.index.badKeyRewind");
+    }
+
+    pos -= n;
+  }
+
+  /** Get current position
+   *
+   * @return int position
+   */
+  public int getPos() {
+    return pos;
+  }
+
+  /** Set current position
+   *
+   * @param val  int position
+   */
+  public void setPos(int val) {
+    pos = val;
+  }
+
+  /** Get number of chars remaining
+   *
+   * @return int number of chars remaining
+   */
+  public int remaining() {
+    if (encoded == null) {
+      return 0;
+    }
+    return encoded.length - pos;
+  }
+
+  /** Test for more
+   *
+   * @return boolean true for more
+   */
+  public boolean hasMore() {
+    return remaining() > 0;
+  }
+
+  /** Test for no more
+   *
+   * @return boolean true for no more
+   */
+  public boolean empty() {
+    return (encoded == null) || (encoded.length == 0);
+  }
+
+  /** Rewind to the start
+   */
+  public void rewind() {
+    pos = 0;
+  }
+
+  /** Return the value of a blank terminated length. On success current pos
+   * has been incremented.
+   *
+   * @return int length
+   * @throws AccessException
+   */
+  public int getLength() throws IndexException {
+    int res = 0;
+
+    for (;;) {
+      char c = getChar();
+      if (c == ' ') {
+        break;
+      }
+
+      if (c < 0) {
+        throw new IndexException("org.bedework.index.badKeyLength");
+      }
+
+      if ((c < '0') || (c > '9')) {
+        throw new IndexException("org.bedework.index.badkeychar");
+      }
+
+      res = res * 10 + Character.digit(c, 10);
+    }
+
+    return res;
+  }
+
+  /** Get a String from the encoded acl at the current position.
+   *
+   * @return String decoded String value
+   * @throws AccessException
+   */
+  public String getKeyString() throws IndexException {
+    if (getChar() == 'N') {
+      return null;
+    }
+    back();
+    int len = getLength();
+
+    if ((encoded.length - pos) < len) {
+      throw new IndexException("org.bedework.index.badKeyLength");
+    }
+
+    String s = new String(encoded, pos, len);
+    pos += len;
+
+    return s;
+  }
+
+  /** Skip a String from the encoded acl at the current position.
+   *
+   * @throws AccessException
+   */
+  public void skipString() throws IndexException {
+    if (getChar() == 'N') {
+      return;
+    }
+
+    back();
+    int len = getLength();
+    pos += len;
+  }
+
+  /* ====================================================================
+   *                 Encoding methods
+   * ==================================================================== */
+
+  /** Get ready to encode
+   *
+   */
+  public void startEncoding() {
+    caw = new CharArrayWriter();
+  }
+
+  /** Encode a blank terminated 0 prefixed length.
+   *
+   * @param len
+   * @throws AccessException
+   */
+  public void encodeLength(int len) throws IndexException {
+    try {
+      String slen = String.valueOf(len);
+      caw.write('0');
+      caw.write(slen, 0, slen.length());
+      caw.write(' ');
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+  /** Encode a String with length prefix. String is encoded as <ul>
+   * <li>One byte 'N' for null string or</li>
+   * <li>length {@link #encodeLength(int)} followed by</li>
+   * <li>String value.</li>
+   * </ul>
+   *
+   * @param val
+   * @throws AccessException
+   */
+  public void encodeString(String val) throws IndexException {
+    try {
+      if (val == null) {
+        caw.write('N'); // flag null
+      } else {
+        encodeLength(val.length());
+        caw.write(val, 0, val.length());
+      }
+    } catch (IndexException ie) {
+      throw ie;
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+  /** Add a character
+   *
+   * @param c char
+   * @throws AccessException
+   */
+  public void addChar(char c) throws IndexException {
+    try {
+      caw.write(c);
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+  /** Get the current encoed value
+   *
+   * @return char[] encoded value
+   */
+  public String getEncodedKey() {
+    return new String(caw.toCharArray());
+  }
+}

Added: trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexLuceneDefs.java
===================================================================
--- trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexLuceneDefs.java	                        (rev 0)
+++ trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexLuceneDefs.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -0,0 +1,152 @@
+/*
+ Copyright (c) 2000-2005 University of Washington.  All rights reserved.
+
+ Redistribution and use of this distribution in source and binary forms,
+ with or without modification, are permitted provided that:
+
+   The above copyright notice and this permission notice appear in
+   all copies and supporting documentation;
+
+   The name, identifiers, and trademarks of the University of Washington
+   are not used in advertising or publicity without the express prior
+   written permission of the University of Washington;
+
+   Recipients acknowledge that this distribution is made available as a
+   research courtesy, "as is", potentially with defects, without
+   any obligation on the part of the University of Washington to
+   provide support, services, or repair;
+
+   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR
+   IMPLIED, WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION
+   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+   PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
+   WASHINGTON BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+   DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+   PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT (INCLUDING
+   NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION WITH
+   THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* **********************************************************************
+    Copyright 2005 Rensselaer Polytechnic Institute. All worldwide rights reserved.
+
+    Redistribution and use of this distribution in source and binary forms,
+    with or without modification, are permitted provided that:
+       The above copyright notice and this permission notice appear in all
+        copies and supporting documentation;
+
+        The name, identifiers, and trademarks of Rensselaer Polytechnic
+        Institute are not used in advertising or publicity without the
+        express prior written permission of Rensselaer Polytechnic Institute;
+
+    DISCLAIMER: The software is distributed" AS IS" without any express or
+    implied warranty, including but not limited to, any implied warranties
+    of merchantability or fitness for a particular purpose or any warrant)'
+    of non-infringement of any current or pending patent rights. The authors
+    of the software make no representations about the suitability of this
+    software for any particular purpose. The entire risk as to the quality
+    and performance of the software is with the user. Should the software
+    prove defective, the user assumes the cost of all necessary servicing,
+    repair or correction. In particular, neither Rensselaer Polytechnic
+    Institute, nor the authors of the software are liable for any indirect,
+    special, consequential, or incidental damages related to the software,
+    to the maximum extent the law permits.
+*/
+
+package org.bedework.calsvc;
+
+/** Haven't yet figured out how we'll internationalize queries. I think internal
+ * lucene field names will have to be fixed and defined below and front end
+ * implementors will need to provide a mapping.
+ *
+ * <p>We can possibly provide a subsclass of the parser to take a mapping table
+ * of allowable external names to internal names.
+ *
+ * <p>In any case, this class defines the names of all the fields we index.
+ *
+ * @author Mike Douglass douglm @ rpi.edu
+ *
+ */
+public class BwIndexLuceneDefs {
+  private BwIndexLuceneDefs() {
+    // There'll be no instantiation here
+  }
+
+  /* ---------------------------- Calendar fields ------------------------- */
+
+  /** */
+  public static final String calendarDescription = "calendarDescription";
+
+  /** Key field for a calendar */
+  public static final String calendarPath = "calendarPath";
+
+  /** */
+  public static final String calendarSummary = "calendarSummary";
+
+  /* ---------------------------- Event fields ------------------------- */
+
+  /** */
+  public static final String eventCategory = "eventCategory";
+
+  /** */
+  public static final String eventDescription = "eventDescription";
+
+  /** */
+  public static final String eventEnd = "eventEnd";
+
+  /** */
+  public static final String eventLocation = "eventLocation";
+
+  /** */
+  public static final String eventStart = "eventStart";
+
+  /** */
+  public static final String eventSummary = "eventSummary";
+
+  /** */
+  public static final String defaultFieldName = "default";
+
+  /* Field names for fields which contain item type and key information.
+   */
+
+  /** Field name defining type of item */
+  public static final String itemTypeName = "itemType";
+
+  /** */
+  public static final String[] termNames = {
+    itemTypeName,
+
+    // ----------------- Calendar
+    calendarDescription,
+    calendarSummary,
+
+    // ----------------- Event
+    eventCategory,
+    eventDescription,
+    eventLocation,
+    eventSummary,
+  };
+
+  /**
+   * @return String[]
+   */
+  public static String[] getTermNames() {
+    return termNames;
+  }
+
+  /* Item types. We index various item types and these strings define each
+   * type.
+   */
+
+  /** */
+  public static final String itemTypeCalendar = "calendar";
+
+  /** */
+  public static final String itemTypeEvent = "event";
+
+  /** Key field for calendar */
+  public static final String keyCalendar = "calendarPath";
+
+  /** Key for an event */
+  public static final String keyEvent = "eventKey";
+
+}

Added: trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexLuceneImpl.java
===================================================================
--- trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexLuceneImpl.java	                        (rev 0)
+++ trunk/calendar3/calsvc/src/org/bedework/calsvc/BwIndexLuceneImpl.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -0,0 +1,238 @@
+/*
+ Copyright (c) 2000-2005 University of Washington.  All rights reserved.
+
+ Redistribution and use of this distribution in source and binary forms,
+ with or without modification, are permitted provided that:
+
+   The above copyright notice and this permission notice appear in
+   all copies and supporting documentation;
+
+   The name, identifiers, and trademarks of the University of Washington
+   are not used in advertising or publicity without the express prior
+   written permission of the University of Washington;
+
+   Recipients acknowledge that this distribution is made available as a
+   research courtesy, "as is", potentially with defects, without
+   any obligation on the part of the University of Washington to
+   provide support, services, or repair;
+
+   THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR
+   IMPLIED, WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION
+   ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+   PARTICULAR PURPOSE, AND IN NO EVENT SHALL THE UNIVERSITY OF
+   WASHINGTON BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+   DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+   PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT (INCLUDING
+   NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION WITH
+   THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* **********************************************************************
+    Copyright 2005 Rensselaer Polytechnic Institute. All worldwide rights reserved.
+
+    Redistribution and use of this distribution in source and binary forms,
+    with or without modification, are permitted provided that:
+       The above copyright notice and this permission notice appear in all
+        copies and supporting documentation;
+
+        The name, identifiers, and trademarks of Rensselaer Polytechnic
+        Institute are not used in advertising or publicity without the
+        express prior written permission of Rensselaer Polytechnic Institute;
+
+    DISCLAIMER: The software is distributed" AS IS" without any express or
+    implied warranty, including but not limited to, any implied warranties
+    of merchantability or fitness for a particular purpose or any warrant)'
+    of non-infringement of any current or pending patent rights. The authors
+    of the software make no representations about the suitability of this
+    software for any particular purpose. The entire risk as to the quality
+    and performance of the software is with the user. Should the software
+    prove defective, the user assumes the cost of all necessary servicing,
+    repair or correction. In particular, neither Rensselaer Polytechnic
+    Institute, nor the authors of the software are liable for any indirect,
+    special, consequential, or incidental damages related to the software,
+    to the maximum extent the law permits.
+*/
+package org.bedework.calsvc;
+
+import org.bedework.calfacade.BwCalendar;
+import org.bedework.calfacade.BwCategory;
+import org.bedework.calfacade.BwEvent;
+import org.bedework.calfacade.BwLocation;
+import org.bedework.calsvci.CalSvcI;
+
+import edu.rpi.cct.misc.indexing.Index;
+import edu.rpi.cct.misc.indexing.IndexException;
+import edu.rpi.cct.misc.indexing.IndexLuceneImpl;
+
+import java.util.Iterator;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.index.Term;
+
+/**
+ * @author Mike Douglass douglm @ rpi.edu
+ *
+ */
+public class BwIndexLuceneImpl extends IndexLuceneImpl {
+  private CalSvcI svci;
+  private BwIndexKey keyConverter = new BwIndexKey();
+
+  public BwIndexLuceneImpl(CalSvcI svci,
+                           String sysfilePath,
+                           boolean admin,
+                           boolean debug) throws IndexException {
+    super(sysfilePath,
+          BwIndexLuceneDefs.defaultFieldName,
+          admin, debug);
+    this.svci = svci;
+  }
+
+  /** Called to make or fill in a Key object.
+   *
+   * @param key   Possible Index.Key object for reuse
+   * @param doc   The retrieved Document
+   * @param score The rating for this entry
+   * @return Index.Key  new or reused object
+   */
+  public Index.Key makeKey(Index.Key key,
+                           Document doc,
+                           float score) throws IndexException {
+    if ((key == null) || (!(key instanceof BwIndexKey))) {
+      key = new BwIndexKey(svci);
+    }
+
+    BwIndexKey bwkey = (BwIndexKey)key;
+
+    bwkey.setScore(score);
+
+    String itemType = doc.get(BwIndexLuceneDefs.itemTypeName);
+    bwkey.setItemType(itemType);
+
+    if (itemType == null) {
+      throw new IndexException("org.bedework.index.noitemtype");
+    }
+
+    if (itemType.equals(BwIndexLuceneDefs.itemTypeCalendar)) {
+      bwkey.setCalendarKey(doc.get(BwIndexLuceneDefs.keyCalendar));
+    } else if (itemType.equals(BwIndexLuceneDefs.itemTypeEvent)) {
+      bwkey.setEventKey(doc.get(BwIndexLuceneDefs.keyEvent));
+    } else {
+      throw new IndexException(IndexException.unknownRecordType,
+                               itemType);
+    }
+
+    return key;
+  }
+
+  /** Called to make a key term for a record.
+   *
+   * @param   rec      The record
+   * @return  Term     Lucene term which uniquely identifies the record
+   */
+  public Term makeKeyTerm(Object rec) throws IndexException {
+    String name = makeKeyName(rec);
+    String key = makeKeyVal(rec);
+
+    return new Term(name, key);
+  }
+
+  /** Called to make a key value for a record.
+   *
+   * @param   rec      The record
+   * @return  String   String which uniquely identifies the record
+   */
+  public String makeKeyVal(Object rec) throws IndexException {
+    if (rec instanceof BwCalendar) {
+      return ((BwCalendar)rec).getPath();
+    }
+
+    if (rec instanceof BwEvent) {
+      BwEvent ev = (BwEvent)rec;
+
+      String path = ev.getCalendar().getPath();
+      String guid = ev.getGuid();
+      String recurid = null;
+      if (ev.getRecurrence() != null) {
+        recurid = ev.getRecurrence().getRecurrenceId();
+      }
+
+      return keyConverter.makeEventKey(path, guid, recurid);
+    }
+
+    throw new IndexException(IndexException.unknownRecordType,
+                             rec.getClass().getName());
+  }
+
+  /** Called to make the primary key name for a record.
+   *
+   * @param   rec      The record
+   * @return  String   Name for the field/term
+   */
+  public String makeKeyName(Object rec) throws IndexException {
+    if (rec instanceof BwCalendar) {
+      return BwIndexLuceneDefs.keyCalendar;
+    }
+
+    if (rec instanceof BwEvent) {
+      return BwIndexLuceneDefs.keyEvent;
+    }
+
+    throw new IndexException(IndexException.unknownRecordType,
+                             rec.getClass().getName());
+  }
+
+  /** Called to fill in a Document from an object.
+   *
+   * @param doc   The Document
+   * @param o     Object to be indexed
+   */
+  public void addFields(Document doc,
+                        Object o) throws IndexException {
+    if (o == null) {
+      System.out.println("Tried to index null record");
+      return;
+    }
+
+    if (o instanceof BwCalendar) {
+      BwCalendar cal = (BwCalendar)o;
+
+      addString(doc, BwIndexLuceneDefs.calendarPath, cal.getPath());
+      addLongString(doc, BwIndexLuceneDefs.calendarDescription, cal.getDescription());
+      addString(doc, BwIndexLuceneDefs.calendarSummary, cal.getSummary());
+    } else if (o instanceof BwEvent) {
+      BwEvent ev = (BwEvent)o;
+
+      addString(doc, BwIndexLuceneDefs.keyEvent, makeKeyVal(ev));
+
+      addLongString(doc, BwIndexLuceneDefs.eventDescription, ev.getDescription());
+      addString(doc, BwIndexLuceneDefs.eventSummary, ev.getSummary());
+
+      addUntokenized(doc, BwIndexLuceneDefs.eventStart, ev.getDtstart().getDtval());
+      addUntokenized(doc, BwIndexLuceneDefs.eventStart, ev.getDtend().getDtval());
+
+      BwLocation loc = ev.getLocation();
+      if (loc != null) {
+        addString(doc, BwIndexLuceneDefs.eventLocation, loc.getAddress());
+        addString(doc, BwIndexLuceneDefs.eventLocation, loc.getSubaddress());
+      }
+
+      Iterator it = ev.iterateCategories();
+      while (it.hasNext()) {
+        BwCategory cat = (BwCategory)it.next();
+
+        addString(doc, BwIndexLuceneDefs.eventCategory, cat.getWord());
+        addString(doc, BwIndexLuceneDefs.eventCategory, cat.getDescription());
+      }
+    } else {
+      throw new IndexException(IndexException.unknownRecordType,
+                               o.getClass().getName());
+    }
+  }
+
+  /** Called to return an array of valid term names.
+   *
+   * @return  String[]   term names
+   */
+  public String[] getTermNames() {
+    return BwIndexLuceneDefs.getTermNames();
+  }
+}

Modified: trunk/calendar3/calsvc/src/org/bedework/calsvc/CalSvc.java
===================================================================
--- trunk/calendar3/calsvc/src/org/bedework/calsvc/CalSvc.java	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/calsvc/src/org/bedework/calsvc/CalSvc.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -99,6 +99,7 @@
 import org.bedework.icalendar.URIgen;
 //import org.bedework.mail.MailerIntf;
 
+import edu.rpi.cct.misc.indexing.Index;
 import edu.rpi.cct.uwcal.access.PrivilegeDefs;
 import edu.rpi.cct.uwcal.access.Acl.CurrentAccess;
 import edu.rpi.cct.uwcal.resources.Resources;
@@ -127,6 +128,8 @@
 public class CalSvc extends CalSvcI {
   private CalSvcIPars pars;
 
+  private Index indexer;
+
   private boolean debug;
 
   private boolean open;
@@ -1014,21 +1017,19 @@
 
   public BwFreeBusy getFreeBusy(BwCalendar cal, BwPrincipal who,
                                 BwDateTime start, BwDateTime end,
-                                BwDuration granularity)
+                                BwDuration granularity,
+                                boolean returnAll)
           throws CalFacadeException {
     if (!(who instanceof BwUser)) {
       throw new CalFacadeException("Unsupported: non user principal for free-busy");
     }
 
-    if (isGuest() || (!currentUser().equals(who))) {
-      // No access for the moment
-      throw new CalFacadeAccessException();
-    }
-
     BwUser u = (BwUser)who;
     Collection subs;
 
     if (cal != null) {
+      getCal().checkAccess(cal, PrivilegeDefs.privReadFreeBusy, false);
+
       BwSubscription sub = BwSubscription.makeSubscription(cal);
 
       subs = new ArrayList();
@@ -1036,6 +1037,9 @@
     } else if (currentUser().equals(who)) {
       subs = getSubscriptions();
     } else {
+      getCal().checkAccess(getCal().getCalendars(u),
+                           PrivilegeDefs.privReadFreeBusy, false);
+
       subs = dbi.fetchPreferences(u).getSubscriptions();
     }
 
@@ -1079,15 +1083,19 @@
         gpp.dur = granularity;
         gpp.tzcache = getTimezones();
 
-        /* For the moment just build a single BwFreeBusyComponent
-         */
-        BwFreeBusyComponent fbc = new BwFreeBusyComponent();
+        BwFreeBusyComponent fbc = null;
 
+        if (!returnAll) {
+          // One component
+          fbc = new BwFreeBusyComponent();
+          fb.addTime(fbc);
+        }
+
         int limit = 10000; // XXX do this better
         while (gpp.startDt.before(end)) {
-          if (debug) {
-            trace("gpp.startDt=" + gpp.startDt + " end=" + end);
-          }
+          //if (debug) {
+          //  trace("gpp.startDt=" + gpp.startDt + " end=" + end);
+          //}
           if (limit < 0) {
             throw new CalFacadeException("org.bedework.svci.limit.exceeded");
           }
@@ -1095,14 +1103,21 @@
 
           Collection periodEvents = CalFacadeUtil.getPeriodsEvents(gpp);
 
-          if (periodEvents.size() != 0) {
+          if (returnAll) {
+            fbc = new BwFreeBusyComponent();
+            fb.addTime(fbc);
+            fbc.addPeriod(new Period(new DateTime(gpp.startDt.getDtval()),
+                                     new DateTime(gpp.endDt.getDtval())));
+            if (periodEvents.size() == 0) {
+              fbc.setType(BwFreeBusyComponent.typeFree);
+            }
+          } else if (periodEvents.size() != 0) {
             // Some events fall in the period. Add an entry.
 
             fbc.addPeriod(new Period(new DateTime(gpp.startDt.getDtval()),
                                      new DateTime(gpp.endDt.getDtval())));
           }
         }
-        fb.addTime(fbc);
 
         return fb;
       }
@@ -2373,6 +2388,70 @@
     }
   }
 
+  /* ====================================================================
+   *                   Private indexing methods
+   * ==================================================================== */
+
+  private Index getIndexer() throws CalFacadeException {
+    try {
+      if (indexer == null) {
+        indexer = new BwIndexLuceneImpl(this,
+                                        // XXX Schema change - in syspars
+                                        "/temp/bedeworkindex",
+                                        isPublicAdmin(),
+                                        debug);
+      }
+
+      return indexer;
+    } catch (Throwable t) {
+      throw new CalFacadeException(t);
+    }
+  }
+
+  /* Call to (re)index an event
+   */
+  private void indexEvent(BwEvent ev) throws CalFacadeException {
+    try {
+      getIndexer().indexRec(ev);
+    } catch (Throwable t) {
+      throw new CalFacadeException(t);
+    }
+  }
+
+  private void unindexEvent(BwEvent ev) throws CalFacadeException {
+    try {
+      getIndexer().unindexRec(ev);
+    } catch (Throwable t) {
+      throw new CalFacadeException(t);
+    }
+  }
+
+  /* Call to (re)index a calendar
+   */
+  private void indexCalendar(BwCalendar cal) throws CalFacadeException {
+    try {
+      getIndexer().indexRec(cal);
+    } catch (Throwable t) {
+      throw new CalFacadeException(t);
+    }
+  }
+
+  private void unindexCalendar(BwCalendar cal) throws CalFacadeException {
+    try {
+      getIndexer().unindexRec(cal);
+
+      // And all the children
+      Iterator it = cal.iterateChildren();
+      while (it.hasNext()) {
+        BwCalendar ch = (BwCalendar)it.next();
+
+        unindexCalendar(ch);
+      }
+    } catch (Throwable t) {
+      throw new CalFacadeException(t);
+    }
+  }
+
   /* Get a logger for messages
    */
   private Logger getLogger() {

Modified: trunk/calendar3/calsvci/src/org/bedework/calsvci/CalSvcI.java
===================================================================
--- trunk/calendar3/calsvci/src/org/bedework/calsvci/CalSvcI.java	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/calsvci/src/org/bedework/calsvci/CalSvcI.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -809,12 +809,15 @@
    * @param granularity BwDuration object defining how to divide free/busy
    *                    null for one big glob. or set to e.g. 1 hour for
    *                    hourly chunks.
+   * @param returnAll   if true return entries for free time otherwise just for busy
+   *                    (only for granularity not null)
    * @return BwFreeBusy
    * @throws CalFacadeException
    */
   public abstract BwFreeBusy getFreeBusy(BwCalendar cal, BwPrincipal who,
                                          BwDateTime start, BwDateTime end,
-                                         BwDuration granularity)
+                                         BwDuration granularity,
+                                         boolean returnAll)
           throws CalFacadeException;
 
   /* ====================================================================

Modified: trunk/calendar3/common/build.xml
===================================================================
--- trunk/calendar3/common/build.xml	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/common/build.xml	2006-04-13 13:35:10 UTC (rev 369)
@@ -16,13 +16,11 @@
     <!-- ==================== Sources and classes ====================== -->
 
     <fileset id="base.java.sources" dir="${source.home}" >
-     <include name="edu/rpi/sss/util/**/*.java"/>
-      <include name="edu/rpi/cct/uwcal/**/*.java"/>
+     <include name="edu/rpi/**/*.java"/>
     </fileset>
 
     <patternset id="base.class.patternset">
-      <include name="edu/rpi/sss/util/**/*.class"/>
-      <include name="edu/rpi/cct/uwcal/**/*.class"/>
+      <include name="edu/rpi/**/*.class"/>
     </patternset>
 
     <!-- ==================== Compilation Classpath ==================== -->
@@ -33,7 +31,8 @@
       <pathelement location="${fop.jar}"/>
       <pathelement location="${avalon.jar}"/>
       <pathelement location="${log4j.jar}"/>
-      <pathelement location="${logkit.jar}"/>
+      <pathelement location="${log4j.jar}"/>
+      <pathelement location="${lucene.jar}"/>
       <pathelement location="${jtidy.jar}"/>
     </path>
 

Added: trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/Index.java
===================================================================
--- trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/Index.java	                        (rev 0)
+++ trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/Index.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -0,0 +1,119 @@
+package edu.rpi.cct.misc.indexing;
+
+import java.io.Serializable;
+
+/** This interface describes how we build and search indexes for
+ * applications such as Luwak, forum systems etc.
+ */
+public interface Index extends Serializable {
+  public void setDebug(boolean val);
+
+  /** This might need to be called to open the index in the appropriate manner
+   *  probably determined by information passed to the constructor.
+   */
+  public void open() throws IndexException;
+
+  /** This can be called to (re)create the index. It will destroy any
+   * previously existing index.
+   */
+  public void create() throws IndexException;
+
+  /** See if we need to call open
+   */
+  public boolean getIsOpen();
+
+  /** This gives a single keyword to identify the implementation.
+   *
+   * @return  String    An identifying key.
+   */
+  public String id();
+
+  /** This can be called to obtain some information about the index
+   * implementation. id gives a single keyword whereas this gives a more
+   * detailed description.
+   *
+   * @return  String    A descrptive string.
+   */
+  public String info();
+
+  /** Called to (re)index a record
+   *
+   * @param   rec      The record to index
+   */
+  public void indexRec(Object rec) throws IndexException;
+
+  /** Called to unindex a record
+   *
+   * @param   rec      The record to unindex
+   */
+  public void unindexRec(Object rec) throws IndexException;
+
+  /** Called to (re)index a batch of records
+   *
+   * @param   recs     The records to index
+   */
+  public void indexRecs(Object[] recs) throws IndexException;
+
+  /** Called to index a batch of new records. More efficient way of
+   * rebuilding the index.
+   *
+   * @param   recs     The records to index
+   */
+  public void indexNewRecs(Object[] recs) throws IndexException;
+
+  /** Called to unindex a batch of records
+   *
+   * @param   recs      The records to unindex
+   */
+  public void unindexRecs(Object[] recs) throws IndexException;
+
+  /** Called to find entries that match the search string. This string may
+   * be a simple sequence of keywords or some sort of query the syntax of
+   * which is determined by the underlying implementation.
+   *
+   * @param   query    Query string
+   * @return  int      Number found. 0 means none found,
+   *                                -1 means indeterminate
+   */
+  public int search(String query) throws IndexException;
+
+  /** The implementation will determine what information the Key object
+   * actually contains. The getRecord method can be called to retrieve the
+   * referenced entry.
+   */
+  public abstract class Key {
+    public float score;       // Score this record
+
+    public abstract Object getRecord() throws IndexException;
+  }
+
+  /** Called to retrieve record keys from the result.
+   *
+   * @param   n        Starting index
+   * @param   keys     Array for the record keys
+   * @return  int      Actual number of records
+   */
+  public int retrieve(int n, Key[] keys) throws IndexException;
+
+  /** Called if we intend to run a batch of updates. endBatch MUST be
+   * called or manual intervention may be required to remove locks.
+   */
+  public void startBatch() throws IndexException;
+
+  /** Called at the end of a batch of updates.
+   */
+  public void endBatch() throws IndexException;
+
+  /** Called to close at the end of a session.
+   */
+  public void close() throws IndexException;
+
+  /** Called to provide some debugging dump info. Must be open.
+   */
+  public void dump();
+
+  /** Called to provide some statistics. Must be open.
+   */
+  public void stats();
+
+}

Added: trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexDummyImpl.java
===================================================================
--- trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexDummyImpl.java	                        (rev 0)
+++ trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexDummyImpl.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -0,0 +1,136 @@
+package edu.rpi.cct.misc.indexing;
+
+/** This class implements a dummy null indexer.
+ */
+public class IndexDummyImpl implements Index {
+  private boolean isOpen;
+
+  public IndexDummyImpl() throws IndexException {
+  }
+
+  public void setDebug(boolean val) {
+  }
+
+  /** This must be called to open the index in the appropriate manner
+   *  probably determined by information passed to the constructor.
+   */
+  public void open() throws IndexException {
+    isOpen = true;
+  }
+
+  /** This can be called to (re)create the index. It will destroy any
+   * previously existing index.
+   */
+  public void create() throws IndexException {
+    isOpen = true;
+  }
+
+  /** See if we need to call open
+   */
+  public boolean getIsOpen() {
+    return isOpen;
+  }
+
+  /** This gives a single keyword to identify the implementation.
+   *
+   * @return  String    An identifying key.
+   */
+  public String id() {
+    return "dummy";
+  }
+
+  /** This can be called to obtain some information about the index
+   * implementation. id gives a single keyword whereas this gives a more
+   * detailed description.
+   *
+   * @return  String    A descrptive string.
+   */
+  public String info() {
+    return "A dummy implementation of an indexer.";
+  }
+
+  /** Called to index a record
+   *
+   * @param   rec      The record to index
+   */
+  public void indexRec(Object rec) throws IndexException {
+  }
+
+  /** Called to unindex a record
+   *
+   * @param   rec      The record to unindex
+   */
+  public void unindexRec(Object rec) throws IndexException {
+  }
+
+  /** Called to (re)index a batch of records
+   *
+   * @param   recs     The records to index
+   */
+  public void indexRecs(Object[] recs) throws IndexException {
+  }
+
+  /** Called to index a batch of new records. More efficient way of
+   * rebuilding the index.
+   *
+   * @param   recs     The records to index
+   */
+  public void indexNewRecs(Object[] recs) throws IndexException {
+  }
+
+  /** Called to unindex a batch of records
+   *
+   * @param   recs      The records to unindex
+   */
+  public void unindexRecs(Object[] recs) throws IndexException {
+  }
+
+  /** Called to find entries that match the search string. This string may
+   * be a simple sequence of keywords or some sort of query the syntax of
+   * which is determined by the underlying implementation.
+   *
+   * @param   query    Query string
+   * @return  int      Number found. 0 means none found,
+   *                                -1 means indeterminate
+   */
+  public int search(String query) throws IndexException {
+    return 0;
+  }
+
+  /** Called to retrieve record keys from the result.
+   *
+   * @param   n        Starting index
+   * @param   keys     Array for the record keys
+   * @return  int      Actual number of records
+   */
+  public int retrieve(int n, Index.Key[] keys) throws IndexException {
+    return 0;
+  }
+
+  /** Called if we intend to run a batch of updates. endBatch MUST be
+   * called or manual intervention may be required to remove locks.
+   */
+  public void startBatch() throws IndexException {
+  }
+
+  /** Called at the end of a batch of updates.
+   */
+  public void endBatch() throws IndexException {
+  }
+
+  /** Called to close at the end of a session.
+   */
+  public void close() {
+    isOpen = false;
+  }
+
+  /** Called to provide some debugging dump info.
+   */
+  public void dump() {
+  }
+
+  /** Called to provide some statistics.
+   */
+  public void stats() {
+  }
+}

Added: trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexException.java
===================================================================
--- trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexException.java	                        (rev 0)
+++ trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexException.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -0,0 +1,56 @@
+package edu.rpi.cct.misc.indexing;
+
+/** Pass back an exception from the indexing classes.
+ *  The object will contain a property name and optionally one of an
+ *  exception object representing some underlying cause or a string value for
+ *  insertion in the message.
+ *  <p>If a call on getCause returns a non-null result, the cause of the
+ *  exception is some underlying exception.
+ *  <p>Otherwise the property name and possibly the reason will be set.
+ */
+public class IndexException extends Exception {
+  /** Exception property names.
+   */
+
+  /** "No base path specified" */
+  public final static String noBasePath = "edu.rpi.sss.indexing.exc.nobasepath";
+
+  /** "Insufficient access to create an index" */
+  public final static String noIdxCreateAccess = "edu.rpi.sss.indexing.exc.noIdxCreateAccess";
+
+  /** "Non-unique key term {0}" */
+  public final static String dupKey = "edu.rpi.sss.indexing.exc.dupkey";
+
+  /** "Insufficient access" */
+  public final static String noAccess = "edu.rpi.sss.indexing.exc.noaccess";
+
+  /** "Application error: Unknown record type {0}" */
+  public final static String unknownRecordType = "edu.rpi.sss.indexing.exc.unknownrecordtype";
+
+  /** "Exception occurred" */
+  public final static String errException = "edu.rpi.sss.indexing.exc.exception";
+
+  private String reason1;
+
+  public IndexException() {
+    super();
+  }
+
+  public IndexException(Throwable cause) {
+    super(cause);
+  }
+
+  public IndexException(String pr) {
+    super(pr);
+  }
+
+  public IndexException(String pr, String reason1) {
+    super(pr);
+    this.reason1 = reason1;
+  }
+
+  public String getReason1() {
+    return reason1;
+  }
+}
+

Added: trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexLuceneImpl.java
===================================================================
--- trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexLuceneImpl.java	                        (rev 0)
+++ trunk/calendar3/common/src/edu/rpi/cct/misc/indexing/IndexLuceneImpl.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -0,0 +1,700 @@
+package edu.rpi.cct.misc.indexing;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.Hits;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Searcher;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.sql.Timestamp;
+
+/** This class implements indexing using Lucene.
+ * There is an abstract method to create a Lucene Document from an object.
+ * Also an abstract method to create the Key object.
+ */
+public abstract class IndexLuceneImpl implements Index {
+  private boolean debug;
+  private boolean writeable;
+  private String basePath;
+  private String defaultFieldName;
+
+  /** We append this to basePath to reach the indexes.
+   */
+  private final static String indexDir = "/indexes";
+
+  private boolean updatedIndex;
+  private boolean isOpen;
+
+  /** We try to keep readers and writers open if batchMode is true.
+   */
+  //private boolean batchMode;
+
+  IndexReader rdr;
+  IndexWriter wtr;
+  Searcher sch;
+  Analyzer defaultAnalyzer;
+
+  QueryParser queryParser;
+
+  Hits lastResult;
+
+  private String[] stopWords;
+
+  /** Create an indexer with the default set of stop words.
+   *
+   * @param basePath    String path to where we should read/write indexes
+   * @param defaultFieldName  default name for searches
+   * @param writeable   true if the caller can update the index
+   * @param debug       true if we want to see what's going on
+   */
+  public IndexLuceneImpl(String basePath,
+                         String defaultFieldName,
+                         boolean writeable,
+                         boolean debug) throws IndexException {
+    this(basePath, defaultFieldName, writeable, null, debug);
+  }
+
+  /** Create an indexer with the given set of stop words.
+   *
+   * @param basePath    String path to where we should read/write indexes
+   * @param defaultFieldName  default name for searches
+   * @param writeable   true if the caller can update the index
+   * @param stopWords   set of stop words, null for default.
+   * @param debug       true if we want to see what's going on
+   */
+  public IndexLuceneImpl(String basePath,
+                         String defaultFieldName,
+                         boolean writeable,
+                         String[] stopWords,
+                         boolean debug) throws IndexException {
+    setDebug(debug);
+    this.writeable = writeable;
+    this.basePath = basePath;
+    this.defaultFieldName = defaultFieldName;
+    this.stopWords = stopWords;
+  }
+
+  public boolean getDebug() {
+    return debug;
+  }
+
+  /** Called to make or fill in a Key object.
+   *
+   * @param key   Possible Index.Key object for reuse
+   * @param doc   The retrieved Document
+   * @param score The rating for this entry
+   * @return Index.Key  new or reused object
+   */
+  public abstract Index.Key makeKey(Index.Key key,
+                                    Document doc,
+                                    float score) throws IndexException;
+
+  /** Called to make a key term for a record.
+   *
+   * @param   rec      The record
+   * @return  Term     Lucene term which uniquely identifies the record
+   */
+  public abstract Term makeKeyTerm(Object rec) throws IndexException;
+
+  /** Called to make the primary key name for a record.
+   *
+   * @param   rec      The record
+   * @return  String   Name for the field/term
+   */
+  public abstract String makeKeyName(Object rec) throws IndexException;
+
+  /** Called to fill in a Document from an object.
+   *
+   * @param doc   The =Document
+   * @param rec   The record
+   */
+  public abstract void addFields(Document doc,
+                                 Object rec) throws IndexException;
+
+  /** Called to return an array of valid term names.
+   *
+   * @return  String[]   term names
+   */
+  public abstract String[] getTermNames();
+
+  public void setDebug(boolean val) {
+    debug = val;
+  }
+
+  /** This can be called to open the index in the appropriate manner
+   *  probably determined by information passed to the constructor.
+   *
+   * <p>For a first time call a new analyzer will be created.
+   */
+  public void open() throws IndexException {
+    close();
+
+    if (defaultAnalyzer == null) {
+      if (stopWords == null) {
+        defaultAnalyzer = new StandardAnalyzer();
+      } else {
+        defaultAnalyzer = new StandardAnalyzer(stopWords);
+      }
+
+      queryParser = new QueryParser(defaultFieldName,
+                                    defaultAnalyzer);
+    }
+
+    updatedIndex = false;
+    isOpen = true;
+  }
+
+  /** This can be called to (re)create the index. It will destroy any
+   * previously existing index.
+   */
+  public void create() throws IndexException {
+    try {
+      if (!writeable) {
+        throw new IndexException(IndexException.noIdxCreateAccess);
+      }
+
+      close();
+
+      if (basePath == null) {
+        throw new IndexException(IndexException.noBasePath);
+      }
+
+      IndexWriter iw = new IndexWriter(basePath + indexDir, defaultAnalyzer, true);
+      iw.optimize();
+      iw.close();
+      iw = null;
+
+      open();
+    } catch (IOException e) {
+      throw new IndexException(e);
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+  /** See if we need to call open
+   */
+  public boolean getIsOpen() {
+    return isOpen;
+  }
+
+  /** This gives a single keyword to identify the implementation.
+   *
+   * @return  String    An identifying key.
+   */
+  public String id() {
+    return "LUCENE";
+  }
+
+  /** This can be called to obtain some information about the index
+   * implementation. id gives a single keyword whereas this gives a more
+   * detailed description.
+   *
+   * @return  String    A descrptive string.
+   */
+  public String info() {
+    return "An implementation of an indexer using jakarta Lucene.";
+  }
+
+  /** Called to index a record
+   *
+   * @param   rec      The record to index
+   */
+  public void indexRec(Object rec) throws IndexException {
+    unindexRec(rec);
+    intIndexRec(rec);
+    closeWtr();
+  }
+
+  /** Called to unindex a record
+   *
+   * @param   rec      The record to unindex
+   */
+  public void unindexRec(Object rec) throws IndexException {
+    try {
+      checkOpen();
+      closeWtr();
+      intUnindexRec(rec);
+    } finally {
+      closeWtr(); // Just in case
+      closeRdr();
+    }
+  }
+
+  /** Called to (re)index a batch of records
+   *
+   * @param   recs     The records to index
+   */
+  public void indexRecs(Object[] recs) throws IndexException {
+    if (recs == null) {
+      return;
+    }
+
+    try {
+      unindexRecs(recs);
+
+      for (int i = 0; i < recs.length; i++) {
+        if (recs[i] != null) {
+          intIndexRec(recs[i]);
+        }
+      }
+    } finally {
+      closeWtr();
+      closeRdr(); // Just in case
+    }
+  }
+
+  /** Called to index a batch of new records. More efficient way of
+   * rebuilding the index.
+   *
+   * @param   recs     The records to index
+   */
+  public void indexNewRecs(Object[] recs) throws IndexException {
+    if (recs == null) {
+      return;
+    }
+
+    try {
+      closeRdr(); // Just in case
+
+      for (int i = 0; i < recs.length; i++) {
+        if (recs[i] != null) {
+          intIndexRec(recs[i]);
+        }
+      }
+    } finally {
+      closeWtr();
+    }
+  }
+
+  /** Called to unindex a batch of records
+   *
+   * @param   recs      The records to unindex
+   */
+  public void unindexRecs(Object[] recs) throws IndexException {
+    if (recs == null) {
+      return;
+    }
+
+    try {
+      checkOpen();
+      closeWtr();
+
+      for (int i = 0; i < recs.length; i++) {
+        if (recs[i] != null) {
+          intUnindexRec(recs[i]);
+        }
+      }
+    } finally {
+      closeWtr(); // Just in case
+      closeRdr();
+    }
+  }
+
+  /** Called to find entries that match the search string. This string may
+   * be a simple sequence of keywords or some sort of query the syntax of
+   * which is determined by the underlying implementation.
+   *
+   * @param   query    Query string
+   * @return  int      Number found. 0 means none found,
+   *                                -1 means indeterminate
+   */
+  public int search(String query) throws IndexException {
+    checkOpen();
+
+    try {
+      if (debug) {
+        log("About to search for " + query);
+      }
+
+      Query parsed = queryParser.parse(query);
+
+      if (debug) {
+        log("     with parsed query " + parsed.toString(null));
+      }
+
+      if (sch == null) {
+        sch = new IndexSearcher(getRdr());
+      }
+      lastResult = sch.search(parsed);
+
+      if (debug) {
+        log("     found " + lastResult.length());
+      }
+
+      return lastResult.length();
+    } catch (ParseException pe) {
+      throw new IndexException(pe);
+    } catch (IOException e) {
+      throw new IndexException(e);
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+  /** Called to retrieve record keys from the result.
+   *
+   * @param   n        Starting index
+   * @param   keys     Array for the record keys
+   * @return  int      Actual number of records
+   */
+  public int retrieve(int n, Index.Key[] keys) throws IndexException {
+    checkOpen();
+
+    if ((lastResult == null) ||
+        (keys == null) ||
+        (n >= lastResult.length())) {
+      return 0;
+    }
+
+    int i;
+
+    for (i = 0; i < keys.length; i++) {
+      int hi = i + n;
+      if (hi >= lastResult.length()) {
+        break;
+      }
+
+      try {
+        keys[i] = makeKey(keys[i], lastResult.doc(hi), lastResult.score(hi));
+      } catch (IOException e) {
+        throw new IndexException(e);
+      } catch (Throwable t) {
+        throw new IndexException(t);
+      }
+    }
+
+    return i;
+  }
+
+  /** Called if we intend to run a batch of updates. endBatch MUST be
+   * called or manual intervention may be required to remove locks.
+   */
+  public void startBatch() throws IndexException {
+    //batchMode = true;
+  }
+
+  /** Called at the end of a batch of updates.
+   */
+  public void endBatch() throws IndexException {
+    //batchMode = false;
+    close();
+  }
+
+  /** Called to close at the end of a session.
+   */
+  public synchronized void close() throws IndexException {
+    closeWtr();
+    closeRdr();
+
+    isOpen = false;
+  }
+
+  /** Called if we need to close the writer
+   */
+  public synchronized void closeWtr() throws IndexException {
+    try {
+      if (wtr != null) {
+        if (updatedIndex) {
+          wtr.optimize();
+          updatedIndex = false;
+        }
+        wtr.close();
+        wtr = null;
+      }
+    } catch (IOException e) {
+      throw new IndexException(e);
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+  /** Called if we need to close the reader
+   */
+  public synchronized void closeRdr() throws IndexException {
+    try {
+      if (sch != null) {
+        try {
+          sch.close();
+        } catch (Exception e) {}
+        sch = null;
+      }
+
+      if (rdr != null) {
+        rdr.close();
+        rdr = null;
+      }
+    } catch (IOException e) {
+      throw new IndexException(e);
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+  /** Called to provide some debugging dump info.
+   */
+  public void dump() {
+  }
+
+  /** Called to provide some statistics.
+   */
+  public void stats() {
+  }
+
+  /** ===================================================================
+                    Private methods
+      =================================================================== */
+
+  /** Ensure we're open
+   */
+  private void checkOpen() throws IndexException {
+    if (isOpen) {
+      return;
+    }
+
+    open();
+  }
+
+  /** Called to obtain the current or a new writer.
+   *
+   * @return IndexWriter  writer to our index
+   */
+  private IndexWriter getWtr() throws IndexException {
+    if (!writeable) {
+      throw new IndexException(IndexException.noAccess);
+    }
+
+    try {
+      if (wtr == null) {
+        wtr = new IndexWriter(basePath + indexDir, defaultAnalyzer, false);
+      }
+
+      return wtr;
+    } catch (IOException e) {
+      throw new IndexException(e);
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+  /** Called to obtain the current or a new reader.
+   *
+   * @return IndexReader  reader of our index
+   */
+  private IndexReader getRdr() throws IndexException {
+    if (basePath == null) {
+      throw new IndexException(IndexException.noBasePath);
+    }
+
+    try {
+      if (rdr == null) {
+        rdr = IndexReader.open(basePath + indexDir);
+      }
+
+      return rdr;
+    } catch (IOException e) {
+      throw new IndexException(e);
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+
+  /** Called to unindex a record. The reader will be left
+   * open. The writer must be closed and will stay closed.
+   *
+   * @param   rec      The record to unindex
+   */
+  private void intUnindexRec(Object rec) throws IndexException {
+    try {
+      Term t = makeKeyTerm(rec);
+
+      int numDeleted = getRdr().deleteDocuments(t);
+
+      if (numDeleted > 1) {
+        throw new IndexException(IndexException.dupKey, t.toString());
+      }
+
+      if (debug) {
+        log("removed " + numDeleted + " entries for " + t);
+      }
+
+      updatedIndex = true;
+    } catch (IOException e) {
+      throw new IndexException(e);
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+  /** Called to index a record. The writer will be left open and the reader
+   * must be closed on entry and will stay closed.
+   *
+   * @param   rec      The record to index
+   */
+  private void intIndexRec(Object rec) throws IndexException {
+    Document doc = new Document();
+
+    addFields(doc, rec);
+
+    try {
+      closeRdr();
+      getWtr().addDocument(doc);
+
+      updatedIndex = true;
+    } catch (IOException e) {
+      throw new IndexException(e);
+    } catch (Throwable t) {
+      throw new IndexException(t);
+    }
+  }
+
+  /** ===================================================================
+                Some useful methods
+      =================================================================== */
+
+  /** Called to add an array of keys to a document
+   *
+   * @param   doc      Document object
+   * @param   name     String field name
+   * @param   ss       String array of keywords
+   */
+  protected void addKeyArray(Document doc, String name, String[] ss)
+      throws IndexException {
+    if (ss == null) {
+      return;
+    }
+
+    for (int i = 0; i < ss.length; i++) {
+      if (ss[i] != null) {
+        doc.add(new Field(name, ss[i], Field.Store.YES, Field.Index.UN_TOKENIZED));
+      }
+    }
+  }
+
+  /** Called to add a timestamp date to a document
+   *
+   * @param   doc      The document
+   * @param   name     Field name
+   * @param   dt       The timestamp
+   */
+  protected void addTimestamp(Document doc, String name, Timestamp dt)
+      throws IndexException {
+    if (dt == null) {
+      return;
+    }
+
+    doc.add(new Field(name, dt.toString(), Field.Store.YES, Field.Index.UN_TOKENIZED));
+  }
+
+  /** Called to add a String val to a document
+   *
+   * @param   doc      The document
+   * @param   name     Field name
+   * @param   val      The value
+   */
+  protected void addString(Document doc, String name, String val)
+      throws IndexException {
+    if (val == null) {
+      return;
+    }
+
+    doc.add(new Field(name, val, Field.Store.YES, Field.Index.TOKENIZED));
+  }
+
+  /** Called to add a cost to a document
+   *
+   * @param   doc      The document
+   * @param   name     Field name
+   * @param   cost     The cost in cents
+   */
+  protected void addCost(Document doc, String name, Long cost)
+      throws IndexException {
+    if (cost == null) {
+      return;
+    }
+
+    doc.add(new Field(name, cost.toString(), Field.Store.YES,
+                      Field.Index.UN_TOKENIZED));
+  }
+
+  /** Called to add a long value to a document
+   *
+   * @param   doc      The document
+   * @param   name     Field name
+   * @param   val      The long value
+   */
+  protected void addLong(Document doc, String name, long val) {
+    doc.add(new Field(name, String.valueOf(val), Field.Store.YES, Field.Index.UN_TOKENIZED));
+  }
+
+  /** Called to add an untokenized value to a document
+   *
+   * @param   doc      The document
+   * @param   name     Field name
+   * @param   val      The value
+   */
+  protected void addUntokenized(Document doc, String name, String val) {
+    doc.add(new Field(name, val, Field.Store.YES, Field.Index.UN_TOKENIZED));
+  }
+
+  /** Called to add a keyword val to a document
+   *
+   * @param   doc      The document
+   * @param   name     Field name
+   * @param   val      The value
+   */
+  protected void addKey(Document doc, String name, String val)
+      throws IndexException {
+    if (val == null) {
+      return;
+    }
+
+    doc.add(new Field(name, val, Field.Store.YES, Field.Index.UN_TOKENIZED));
+  }
+
+  /** Called to add a long String val to a document
+   *
+   * @param   doc      The document
+   * @param   name     Field name
+   * @param   val      The value
+   */
+  protected void addLongStoredString(Document doc, String name, String val)
+      throws IndexException {
+    if (val == null) {
+      return;
+    }
+
+    doc.add(new Field(name, new StringReader(val)));
+  }
+
+  /** Called to add a long String val to a document
+   *
+   * @param   doc      The document
+   * @param   name     Field name
+   * @param   val      The value
+   */
+  protected void addLongString(Document doc, String name, String val)
+      throws IndexException {
+    if (val == null) {
+      return;
+    }
+
+    doc.add(new Field(name, val, Field.Store.NO, Field.Index.TOKENIZED));
+  }
+
+  protected void log(String msg) {
+    System.out.println(getClass().getName() + ": " + msg);
+  }
+}
+

Added: trunk/calendar3/lib/lucene-core-1.9.1.jar
===================================================================
(Binary files differ)


Property changes on: trunk/calendar3/lib/lucene-core-1.9.1.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Modified: trunk/calendar3/webclient/src/org/bedework/webclient/BwFreeBusyAction.java
===================================================================
--- trunk/calendar3/webclient/src/org/bedework/webclient/BwFreeBusyAction.java	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/webclient/src/org/bedework/webclient/BwFreeBusyAction.java	2006-04-13 13:35:10 UTC (rev 369)
@@ -157,7 +157,7 @@
     BwFreeBusy fb = svci.getFreeBusy(null, user,
                                      CalFacadeUtil.getDateTime(sdt, false, false, tzs),
                                      CalFacadeUtil.getDateTime(edt, false, false, tzs),
-                                     dur);
+                                     dur, true);
 
     form.assignFreeBusy(fb);
 

Modified: trunk/calendar3/webclient/war/docs/freeBusy.jsp
===================================================================
--- trunk/calendar3/webclient/war/docs/freeBusy.jsp	2006-04-12 21:10:00 UTC (rev 368)
+++ trunk/calendar3/webclient/war/docs/freeBusy.jsp	2006-04-13 13:35:10 UTC (rev 369)
@@ -1,6 +1,7 @@
 <%@ taglib uri='struts-bean' prefix='bean' %>
 <%@ taglib uri='struts-logic' prefix='logic' %>
 <%@ taglib uri='struts-genurl' prefix='genurl' %>
+<%@ taglib uri='bedework' prefix='bw' %>
 
 <%@ include file="/docs/header.jsp" %>
 
@@ -16,9 +17,9 @@
              toScope="request"/>
 
 <freebusy>
-  <who><bean:write name="freeBusyObj" property="who.account" /></who>
-  <start><bean:write name="freeBusyObj" property="start" /></start>
-  <end><bean:write name="freeBusyObj" property="end" /></end>
+  <bw:emitText name="freeBusyObj" property="who.account" tagName="who" />
+  <bw:emitText name="freeBusyObj" property="start.dtval" tagName="start" />
+  <bw:emitText name="freeBusyObj" property="end.dtval" tagName="end" />
   <logic:iterate id="freeBusyComponent"  name="freeBusyObj" property="times" >
     <freeBusyComponent>
       <fbtype><bean:write name="freeBusyComponent" property="type" /></fbtype>



More information about the Bedework-commit mailing list