001    /***************************************************************************************************
002     *               MODULE DESCRIPTION
003     ****************************************************************************************************
004     *
005     *               NAME:           SearchHandler.java
006     *               LANGUAGE:       Java2
007     *               DATE:           14.1.2003
008     *               AUTHOR:         Miika Nurminen, Jyväskylän yliopisto
009     *
010     ****************************************************************************************************
011     *               COPYRIGHT (C) KIURU-PROJEKTIRYHMÄ
012     *               Limited rights granted. Please refer to license
013     ****************************************************************************************************
014     *  
015     ****************************************************************************************************
016     *               UPDATES
017     ****************************************************************************************************
018     *
019     *   14.1.2003 Initial release
020     *
021     ****************************************************************************************************/
022    package kiurubeans;
023    
024    import kotkabeans.Log;
025    import kotkabeans.Encoder;
026    import javax.servlet.http.HttpServletRequest;
027    
028    /**
029     * Superclass for searchobjects (eg. SpaceSearch, PersonSearch...)
030     * 
031     * @author Miika Nurminen
032     */
033    public abstract class SearchHandler extends KiuruHandler {
034    
035    // Attributes 
036    
037      /** Submits search with current field value. */
038      public static final int SUBMIT_SEARCH=1;
039      
040      /** Clears search field. Search does not return anything. */
041      public static final int RESET_SEARCH=2; 
042    
043      /** Fields used for ordering sorted in preferred order */
044      private Field[] visibleFields;
045      
046      /** Value for SQL ordering. Multiple values are separated by ,*/
047      private String order = new String();
048      
049      
050    // Constructors
051    
052      /** Dummy empty constructor. */
053      public SearchHandler() {
054      }
055      
056    // Access methods
057    
058      /** 
059       * Returns current max state value. Inherited classes may override this
060       * when new states are introduced.
061       *
062       * @return max state value
063       */
064      protected int getMaxState() {
065        return 2;
066      }
067      
068      /** 
069       * Indexed getter for property visibleFields.
070       *
071       * @param index Index of the property.
072       * @return Value of the property at <CODE>index</CODE>.
073       */
074      public Field getVisibleFields(int index) {
075        return this.visibleFields[index];
076      }
077      
078      /** 
079       * Getter for property visibleFields.
080       *
081       * @return Value of property visibleFields.
082       */
083      public Field[] getVisibleFields() {
084        return this.visibleFields;
085      }
086      
087      /** 
088       * Indexed setter for property visibleFields.
089       *
090       * @param index Index of the property.
091       * @param visibleFields New value of the property at <CODE>index</CODE>.
092       */
093      public void setVisibleFields(int index, Field visibleFields) {
094        this.visibleFields[index] = visibleFields;
095      }
096      
097      /** 
098       * Setter for property visibleFields.
099       *
100       * @param visibleFields New value of property visibleFields.
101       */
102      public void setVisibleFields(Field[] visibleFields) {
103        this.visibleFields = visibleFields;
104      }
105      
106      /** 
107       * Getter for property order.
108       *
109       * @return Value of property order.
110       */
111      public String getOrder() {
112        return this.order;
113      }
114      
115      /**
116       * Sets bean state to SUBMIT_SEARCH. Usually called from a web form.
117       *
118       * @param s any nonempty string.
119       * @see #SUBMIT_SEARCH
120       */  
121      public void setSubmitSearch(String s) {
122        setEnumState(SUBMIT_SEARCH);
123      }
124      
125      /**
126       * Sets bean state to NO_ACTION. Usually called from a web form.
127       *
128       * @param s any nonempty string.
129       * @see #NO_ACTION
130       */ 
131      public void setContinueSearch(String s) {
132        setEnumState(NO_ACTION);
133      }
134    
135      
136      /**
137       * Returns incasesensitive index of field alias.
138       *
139       * @param name field alias name
140       * @return index of field, -1 if not valid
141       */
142      public int fieldIndexOf(String name) {
143        for (int i=0; i<getVisibleFields().length; i++) {
144          if (getVisibleFields(i).getAlias().equalsIgnoreCase(name)) {
145            return i;
146          }
147        }
148        return -1;
149      }
150      
151      
152      
153      /** 
154       * Setter for property order.
155       *
156       * @param order New value of property order. Multiple fields are separated by ,
157       */
158      public void setOrder(String order) {
159        if ((getVisibleFields()!=null) && (getVisibleFields().length>0)) {
160          if (order.indexOf(',') ==-1) {
161            order = kotkabeans.Encoder.SQLEncode(order);
162            int index = fieldIndexOf(order);
163            
164            // if order is not valid, do nothing (it might be meant for somewhere else...)
165            if (index==-1) return;
166            StringBuffer newOrder = new StringBuffer(10*getVisibleFields().length);
167            // first chosen order, default ordering follows
168            newOrder.append(getVisibleFields(index).getOrderName());
169            for (int i=0; i<getVisibleFields().length; i++) {
170              if (i!=index) {
171                newOrder.append(", ");
172                newOrder.append(getVisibleFields(i).getOrderName());
173              }
174            }
175            this.order=newOrder.toString();
176          }
177          else {
178            setMultiOrder(order);
179          }
180        }
181        else this.order = order;
182      }
183      
184      
185      /** 
186       * Setter for property order when string contains multiple fields.
187       *
188       * @param order New value of property order. Multiple fields are separated by ,
189       */
190      private void setMultiOrder(String order) {
191        if ((order==null) || (order.length()==0)) return;
192        
193        java.util.StringTokenizer sT = new java.util.StringTokenizer(order,",");
194        java.util.ArrayList fields = new java.util.ArrayList(6);
195        String s;
196        int i,j;
197        while(sT.hasMoreElements()) {
198          s = kotkabeans.Encoder.SQLEncode( ((String)(sT.nextElement())).trim() );
199          i = fieldIndexOf(s);
200          if (i>-1) {
201            if (fields.size()>0) {
202              j=fields.indexOf(new Integer(i));
203              if (j>-1) continue; // value already exists in list
204            }
205            fields.add(new Integer(i));
206          }
207        }
208        // if order is not valid, do nothing (it might be meant for somewhere else...)
209        if (fields.size()==0) return;
210        StringBuffer newOrder = new StringBuffer(10*getVisibleFields().length);
211        for (i=0; i<fields.size(); i++) {
212          if (i>0) newOrder.append(", ");
213          newOrder.append(getVisibleFields(((Integer)(fields.get(i))).intValue()).getOrderName());
214        }
215        
216        // first chosen order, default ordering follows
217        for (i=0; i<getVisibleFields().length; i++) {
218          if (fields.indexOf(new Integer(i))==-1) {
219            newOrder.append(", ");
220            newOrder.append(getVisibleFields(i).getOrderName());
221          }
222        }
223        this.order=newOrder.toString();
224      }
225      
226    // Operations
227      
228      /**
229       * Returns order clause to be appended in search SQL.
230       * 
231       * @return order part of SQL clause or nothing if there is no ordering.
232       */
233      public String getOrderClause() {
234        if ((getOrder()!=null) && (getOrder().length()>0)) {
235          return " order by "+getOrder();
236        }
237        else return " ";
238      }
239    
240     
241      /** Assigns order according to visibleFields.  */
242      public void assignDefaultOrder() {
243        if ((getVisibleFields()!=null) && (getVisibleFields().length>0)) {
244          StringBuffer newOrder = new StringBuffer(10*getVisibleFields().length);
245          for (int i=0; i<getVisibleFields().length; i++) {
246            if (i>0) newOrder.append(", ");
247            newOrder.append(getVisibleFields(i).getOrderName());
248          }
249          this.order=newOrder.toString();
250        }
251        else setOrder("");
252      }
253      
254      /**
255       * Ensures empty request parameters are clearer when entity is posted.
256       * <p>
257       * If bean is used in a JSP, this should be called in the beginning of page.
258       * If form content is "" or null it is not sent via HTTP, so those fields
259       * must be cleared manually. Method may be called from a handler or UI page.
260       * Descendant classes should redefine conditions for calling 
261       * doClearEmpty parameters method.
262       *
263       * @param request HTTP request with parameters
264       * @see #doClearEmptyParameters(HttpServletRequest)
265       */
266      public void clearEmptyParameters(HttpServletRequest request) {
267        super.clearEmptyParameters(request);
268        if ((getEnumState()==SUBMIT_SEARCH)){ //or: action!= NO_ACTION && :=CONTINUE_SEARCH
269          kotkabeans.Log.log("Clearing empty parameters");
270          doClearEmptyParameters(request);
271        }
272      }
273      
274      /** 
275       * Implementation of clearing action state. Inherited classes should always override this.
276       *
277       * @param request Http request used for parameter parsing.
278       */
279      protected void doClearActionState(javax.servlet.http.HttpServletRequest request) {
280        if ((KiuruString.isEmpty(request.getParameter("submitSearch"))) && 
281            (KiuruString.isEmpty(request.getParameter("continueSearch")))) {
282          setEnumState(RESET_SEARCH);
283        }
284      }
285    
286      /**
287       * Implementation for SUBMIT_SEARCH
288       */
289      public abstract void submitSearch();
290    
291      /**
292       * Implementation for RESET_SEARCH
293       */
294      public abstract void resetSearch();
295      
296      /**
297       * Performs action based on actionType. Called from performAction.
298       * Descendant classes should override this and call the same method
299       * in inherited class as default.
300       *
301       * @throws Exception If something went wrong during actual executed action.
302       * @see KiuruHandler#performAction()
303       */  
304      protected void defaultAction() throws java.lang.Exception {
305        switch (getEnumState()) {
306          case SUBMIT_SEARCH:
307            submitSearch();
308            break;
309          default: // RESET_SEARCH by default
310            resetSearch();
311            break;
312        }
313      }
314      
315      protected void doClearEmptyParameters(javax.servlet.http.HttpServletRequest request) {
316      }
317      
318    }
319    /***************************************************************************************************
320     *               COPYRIGHT (C) KIURU -PROJECT GROUP
321     *               Limited rights granted. Please refer to license.
322     **************************************************************************************************/