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    }