001 /*
002 *
003 * $Revision: 15330 $ $Date: 2009-06-08 15:47:06 +0200 (Mon, 08 Jun 2009) $
004 *
005 * This file is part of *** M y C o R e ***
006 * See http://www.mycore.de/ for details.
007 *
008 * This program is free software; you can use it, redistribute it
009 * and / or modify it under the terms of the GNU General Public License
010 * (GPL) as published by the Free Software Foundation; either version 2
011 * of the License or (at your option) any later version.
012 *
013 * This program is distributed in the hope that it will be useful, but
014 * WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program, in a file called gpl.txt or license.txt.
020 * If not, write to the Free Software Foundation Inc.,
021 * 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
022 */
023
024 package org.mycore.frontend.indexbrowser.sql;
025
026 import java.util.List;
027 import java.util.Vector;
028
029 import org.jdom.Document;
030 import org.jdom.Element;
031 import org.mycore.backend.sql.MCRSQLConnection;
032 import org.mycore.backend.sql.MCRSQLConnectionPool;
033 import org.mycore.common.MCRCache;
034 import org.mycore.common.MCRNormalizer;
035 import org.mycore.common.xml.MCRXMLHelper;
036 import org.mycore.frontend.servlets.MCRServlet;
037 import org.mycore.frontend.servlets.MCRServletJob;
038
039 /**
040 * @author Frank Lützenkirchen
041 *
042 * TODO: Refactoring Java, XML, XSL TODO: case insensitiv machen TODO:
043 * Sortierung fixen
044 */
045 public class MCRIndexServlet extends MCRServlet {
046 protected void doGetPost(MCRServletJob job) throws Exception {
047 MCRBrowseRequest br = new MCRBrowseRequest(job.getRequest());
048 MCRIndexConfiguration ic = getConfiguration(br.getIndex());
049 String query = buildSqlQuery(br, ic);
050
051 // Build output index page
052 Element page = new Element("indexpage");
053 page.setAttribute("path", br.getCanonicalRequestPath());
054
055 Element eIndex = new Element("index");
056 page.addContent(eIndex);
057 eIndex.setAttribute("id", br.getIndex());
058
059 // Execute SQL Query
060 MCRSQLConnection mcrConn = MCRSQLConnectionPool.instance().getConnection();
061
062 try {
063 java.sql.Connection conn = mcrConn.getJDBCConnection();
064
065 java.sql.Statement stmt = conn.createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE, java.sql.ResultSet.CONCUR_READ_ONLY);
066 java.sql.ResultSet rs = stmt.executeQuery(query);
067
068 try {
069 rs.last();
070
071 int numRows = rs.getRow();
072 rs.beforeFirst();
073
074 Element results = new Element("results");
075 page.addContent(results);
076 results.setAttribute("numHits", String.valueOf(numRows));
077
078 if (br.search != null) {
079 results.setAttribute("search", br.search);
080 results.setAttribute("mode", br.mode);
081 }
082
083 int from = Math.max(1, br.getFrom());
084 int to = Math.min(numRows, br.getTo());
085
086 int numSelectedRows = to - from + 1;
087
088 if (numSelectedRows <= ic.maxPerPage) {
089 for (int i = from; i <= to; i++) {
090 rs.absolute(i);
091
092 Element v = new Element("value");
093 v.setAttribute("pos", String.valueOf(i));
094
095 Element ev = new Element("idx");
096 ev.addContent(rs.getString("idxvalue"));
097 v.addContent(ev);
098
099 for (int j = 0; j < ic.extraFields.length; j++) {
100 String value = rs.getString(ic.extraFields[j]);
101 Element col = new Element("col");
102 col.setAttribute("name", ic.extraFields[j]);
103 col.addContent(MCRXMLHelper.removeIllegalChars(value));
104 v.addContent(col);
105 }
106
107 results.addContent(v);
108 }
109 } else {
110 int stepSize = calculateStepSize(numSelectedRows, ic.maxPerPage);
111 List delims = buildDelimList(from, to, stepSize, rs);
112 buildPrefixDifference(delims);
113 buildXML(results, delims);
114 }
115 } finally {
116 try {
117 rs.close();
118 stmt.close();
119 } catch (Exception ignored) {
120 }
121 }
122 } finally {
123 mcrConn.release();
124 }
125
126 sendXML(job, page, ic.style);
127 }
128
129 // **************************************************************************
130 private List buildDelimList(int from, int to, int steps, java.sql.ResultSet rs) throws Exception {
131 List delims = new Vector();
132
133 for (int i = from; i <= to; i++) {
134 rs.absolute(i);
135 delims.add(new MCRRangeDelim(i, rs.getString("idxvalue")));
136
137 i = Math.min((i + steps) - 1, to);
138 rs.absolute(i);
139
140 String value = rs.getString("idxvalue");
141
142 while ((i < to) && rs.next() && value.equals(rs.getString("idxvalue")))
143 i++;
144
145 if ((i < to) && ((to - i) < 3)) {
146 i = to;
147 rs.absolute(i);
148 value = rs.getString("idxvalue");
149 }
150
151 delims.add(new MCRRangeDelim(i, value));
152 }
153
154 return delims;
155 }
156
157 // **************************************************************************
158 private void buildPrefixDifference(List delims) {
159 for (int i = 0; i < delims.size(); i++) {
160 MCRRangeDelim curr = (MCRRangeDelim) (delims.get(i));
161 MCRRangeDelim prev = (MCRRangeDelim) (delims.get(Math.max(0, i - 1)));
162 MCRRangeDelim next = (MCRRangeDelim) (delims.get(Math.min(i + 1, delims.size() - 1)));
163
164 String vCurr = curr.value;
165 String vPrev = ((i > 0) ? prev.value : "");
166 String vNext = ((i < (delims.size() - 1)) ? next.value : "");
167
168 String a = buildPrefixDifference(vCurr, vPrev);
169 String b = buildPrefixDifference(vCurr, vNext);
170 curr.diff = ((a.length() > b.length()) ? a : b);
171 }
172 }
173
174 // **************************************************************************
175 private void buildXML(Element results, List delims) {
176 for (int i = 0; i < delims.size(); i += 2) {
177 MCRRangeDelim start = (MCRRangeDelim) (delims.get(i));
178 MCRRangeDelim end = (MCRRangeDelim) (delims.get(i + 1));
179
180 Element range = new Element("range");
181 results.addContent(range);
182
183 Element eFrom = new Element("from");
184 eFrom.setAttribute("pos", String.valueOf(start.pos));
185 eFrom.setAttribute("short", MCRXMLHelper.removeIllegalChars(start.diff));
186 eFrom.addContent(MCRXMLHelper.removeIllegalChars(start.value.trim()));
187 range.addContent(eFrom);
188
189 Element eTo = new Element("to");
190 eTo.setAttribute("pos", String.valueOf(end.pos));
191 eTo.setAttribute("short", MCRXMLHelper.removeIllegalChars(end.diff));
192 eTo.addContent(MCRXMLHelper.removeIllegalChars(end.value));
193 range.addContent(eTo);
194 }
195 }
196
197 // **************************************************************************
198 private void sendXML(MCRServletJob job, Element root, String style) throws Exception {
199 Document jdom = new Document(root);
200 job.getRequest().setAttribute("XSL.Style", style);
201 getLayoutService().doLayout(job.getRequest(),job.getResponse(),jdom);
202 }
203
204 // **************************************************************************
205 private MCRCache configurations = new MCRCache(20, "IndexServlet IndexConfigurations");
206
207 private synchronized MCRIndexConfiguration getConfiguration(String ID) {
208 MCRIndexConfiguration ic = (MCRIndexConfiguration) (configurations.get(ID));
209
210 if (ic == null) {
211 ic = new MCRIndexConfiguration(ID);
212 configurations.put(ID, ic);
213 }
214
215 return ic;
216 }
217
218 // **************************************************************************
219 private String buildPrefixDifference(String a, String b) {
220 if (a.equals(b)) {
221 return a;
222 }
223
224 StringBuffer pdiff = new StringBuffer();
225
226 for (int i = 0; i < Math.min(a.length(), b.length()); i++) {
227 pdiff.append(a.charAt(i));
228
229 if (a.charAt(i) != b.charAt(i)) {
230 break;
231 }
232 }
233
234 if ((a.length() > b.length()) && (b.equals(pdiff.toString()))) {
235 pdiff.append(a.charAt(pdiff.length()));
236 }
237
238 return pdiff.toString();
239 }
240
241 // **************************************************************************
242 private String buildSqlQuery(MCRBrowseRequest br, MCRIndexConfiguration ic) {
243 StringBuffer sql = new StringBuffer("select ");
244
245 if (ic.distinct) {
246 sql.append("distinct ");
247 }
248
249 sql.append(ic.browseField).append(" as idxvalue");
250
251 if ((ic.fields != null) && (ic.fields.trim().length() > 0)) {
252 sql.append(", ").append(ic.fields.trim());
253 }
254
255 sql.append(" from ").append(ic.table);
256
257 Vector conditions = new Vector();
258
259 if (br.search != null) {
260 String toSearch = ( ic.normalizeSearch ? MCRNormalizer.normalizeString( br.search, true ) : br.search );
261 if (br.mode.equals("contains")) {
262 conditions.addElement(ic.browseField + " like '%" + toSearch + "%'");
263 } else if (br.mode.equals("prefix")) {
264 conditions.addElement(ic.browseField + " like '" + toSearch + "%'");
265 }
266 }
267
268 if (ic.filter != null) {
269 conditions.addElement(ic.filter);
270 }
271
272 for (int i = 0; i < conditions.size(); i++) {
273 String cond = (String) (conditions.get(i));
274 sql.append((i == 0) ? " where " : " and ");
275 sql.append("(").append(cond).append(")");
276 }
277
278 sql.append(" order by idxvalue ").append(ic.order);
279
280 return sql.toString();
281 }
282
283 // **************************************************************************
284 private int calculateStepSize(int numSelectedRows, int maxPerPage) {
285 for (int i = 1;; i++) {
286 double dNum = numSelectedRows;
287 double dI = 1.0 / i;
288 double root = Math.pow(dNum, dI);
289
290 if (root <= maxPerPage) {
291 return (int) (Math.floor(dNum / root));
292 }
293 }
294 }
295 }