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 **************************************************************************************************/