001    package railo.runtime.search;
002    
003    import java.net.MalformedURLException;
004    import java.net.URL;
005    import java.util.Iterator;
006    import java.util.Map;
007    import java.util.Map.Entry;
008    
009    import org.w3c.dom.Element;
010    
011    import railo.commons.collections.HashTable;
012    import railo.commons.io.FileUtil;
013    import railo.commons.io.log.Log;
014    import railo.commons.io.res.Resource;
015    import railo.commons.io.res.util.ResourceUtil;
016    import railo.commons.lang.StringUtil;
017    import railo.commons.lock.KeyLock;
018    import railo.commons.lock.Lock;
019    import railo.commons.net.HTTPUtil;
020    import railo.runtime.PageContext;
021    import railo.runtime.exp.DatabaseException;
022    import railo.runtime.exp.PageException;
023    import railo.runtime.op.Caster;
024    import railo.runtime.type.ArrayImpl;
025    import railo.runtime.type.Collection.Key;
026    import railo.runtime.type.KeyImpl;
027    import railo.runtime.type.List;
028    import railo.runtime.type.Query;
029    import railo.runtime.type.QueryColumn;
030    import railo.runtime.type.QueryImpl;
031    import railo.runtime.type.dt.DateTime;
032    import railo.runtime.type.dt.DateTimeImpl;
033    import railo.runtime.type.util.ArrayUtil;
034    
035    /**
036     * represent a single Collection
037     */
038    public abstract class SearchCollectionSupport implements SearchCollectionPlus {
039    
040            private static final long serialVersionUID = 8089312879341384114L;
041            
042            private static final int LOCK_TIMEOUT = 10*60*1000; // ten minutes
043        private final String name;
044            private final Resource path;
045            private String language;
046            private DateTime lastUpdate;
047        private SearchEngineSupport searchEngine;
048            //TODO change visibility to private
049        protected Map<String,SearchIndex> indexes=new HashTable();
050    
051        private DateTime created;
052    
053        private Log log;
054            //private static LockManager manager=Lock Manager Impl.getInstance();
055        private KeyLock<String> lock=new KeyLock<String>();
056        
057        
058            /**
059             * constructor of the class
060             * @param searchEngine
061             * @param name name of the Collection
062             * @param path
063             * @param language
064             * @param count total count of documents in the collection
065             * @param lastUpdate
066             * @param created 
067             */
068            public SearchCollectionSupport(SearchEngineSupport searchEngine, String name,Resource path, String language, DateTime lastUpdate, DateTime created) {
069                    this.searchEngine=searchEngine;
070                this.name=name;
071                    this.path=path;
072                    this.language=SearchUtil.translateLanguage(language);
073            this.lastUpdate=lastUpdate;
074            this.created=created;
075                    this.log = searchEngine.getLogger();
076            }
077    
078            /**
079         * @see railo.runtime.search.SearchCollection#create()
080         */
081            public final void create() throws SearchException {
082                    Lock l = lock();
083                    try {
084                _create();
085            }
086                    finally {
087                            unlock(l);
088                    }
089            }
090    
091            /**
092             * create a collection
093             * @throws SearchException
094             */
095            protected abstract void _create() throws SearchException;
096    
097        /**
098         * @see railo.runtime.search.SearchCollection#optimize()
099         */
100        public final void optimize() throws SearchException  {
101            Lock l = lock();
102             try {
103             _optimize();
104            changeLastUpdate();
105        }
106                    finally {
107                            unlock(l);
108                    }
109        }
110    
111        /**
112         * optimize a Collection
113         * @throws SearchException
114         */
115        protected abstract void _optimize() throws SearchException ;
116    
117        /**
118         * @see railo.runtime.search.SearchCollection#map(railo.commons.io.res.Resource)
119         */
120        public final void map(Resource path) throws SearchException  {
121            Lock l = lock();
122            try {
123            _map(path);
124            changeLastUpdate();
125        }
126                    finally {
127                            unlock(l);
128                    }
129        }
130    
131        /**
132         * map a Collection
133         * @param path
134         * @throws SearchException
135         */ 
136        protected abstract void _map(Resource path) throws SearchException ;
137    
138        /**
139         * @see railo.runtime.search.SearchCollection#repair()
140         */
141        public final void repair() throws SearchException  {
142            Lock l = lock();
143            try {
144            _repair();
145            changeLastUpdate();
146        }
147                    finally {
148                            unlock(l);
149                    }
150        }
151    
152        /**
153         * repair a Collection
154         * @throws SearchException
155         */
156        protected abstract void _repair() throws SearchException ;
157        
158        /**
159         * @return 
160         * @see railo.runtime.search.SearchCollection#index(railo.runtime.PageContext, java.lang.String, short, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String)
161         */
162        public IndexResult index(PageContext pc, String key, short type, String urlpath, String title, String body, String language, 
163                String[] extensions, String query, boolean recurse,String categoryTree, String[] categories,
164                String custom1, String custom2, String custom3, String custom4) throws PageException, MalformedURLException, SearchException {
165            return index(pc, key, type, urlpath, title, body, language, extensions, query, recurse, categoryTree, categories, 10000, custom1, custom2, custom3, custom4);
166        }
167                    
168        // FUTURE add this to interface
169        public IndexResult index(PageContext pc, String key, short type, String urlpath, String title, String body, String language, 
170                String[] extensions, String query, boolean recurse,String categoryTree, String[] categories, long timeout,
171                String custom1, String custom2, String custom3, String custom4) throws PageException, MalformedURLException, SearchException {
172            language=SearchUtil.translateLanguage(language);
173            Lock l = lock();
174            try {
175            SearchIndex si = new SearchIndex(title,key,type,query,extensions,language,urlpath,categoryTree,categories,custom1,custom2,custom3,custom4);
176            String id=si.getId();
177            IndexResult ir=IndexResultImpl.EMPTY;
178            if(type==SearchIndex.TYPE_FILE){
179                    Resource file=ResourceUtil.toResourceNotExisting(pc,key);
180                if(!file.isFile())throw new SearchException("value of attribute key must specify a existing file, ["+key+"] is invalid");
181                 ir=indexFile(id,title,file,language);
182            }
183            else if(type==SearchIndex.TYPE_PATH){
184                    Resource dir=ResourceUtil.toResourceNotExisting(pc,key);
185                if(!dir.isDirectory())throw new SearchException("value of attribute key must specify a existing directory, ["+key+"] is invalid");
186                ir=indexPath(id,title,dir,extensions,recurse,language);
187            }
188            else if(type==SearchIndex.TYPE_URL) {
189                    ir=indexURL(id,title,new URL(key),extensions,recurse,language,timeout);
190            }
191            else if(type==SearchIndex.TYPE_CUSTOM) {
192                    Query qv;
193                    if(StringUtil.isEmpty(query)){
194                
195                    // set columns
196                            railo.runtime.type.Array columns=new ArrayImpl();
197                    columns.append("key");
198                    columns.append("body");
199                    if(!StringUtil.isEmpty(title))columns.append("title");
200                    if(!StringUtil.isEmpty(urlpath))columns.append("urlpath");
201                    if(!StringUtil.isEmpty(custom1))columns.append("custom1");
202                    if(!StringUtil.isEmpty(custom2))columns.append("custom2");
203                    if(!StringUtil.isEmpty(custom3))columns.append("custom3");
204                    if(!StringUtil.isEmpty(custom4))columns.append("custom4");
205                    
206                // populate query with a single row
207                    qv=new QueryImpl(columns,1,"query");
208                    // body
209                    qv.setAt(KeyImpl.KEY, 1, key);
210                    key="key";
211    
212                    // body
213                    qv.setAt(KeyImpl.BODY, 1, body);
214                    body="body";
215    
216                    // title
217                    if(!StringUtil.isEmpty(title)){
218                            qv.setAt(KeyImpl.TITLE, 1, title);
219                            title="title";
220                    }
221    
222                    // urlpath
223                    if(!StringUtil.isEmpty(urlpath)){
224                            qv.setAt("urlpath", 1, urlpath);
225                            urlpath="urlpath";
226                    }
227    
228                    // custom1
229                    if(!StringUtil.isEmpty(custom1)){
230                            qv.setAt("custom1", 1, custom1);
231                            custom1="custom1";
232                    }
233                    // custom2
234                    if(!StringUtil.isEmpty(custom2)){
235                            qv.setAt("custom2", 1, custom2);
236                            custom2="custom2";
237                    }
238                    // custom3
239                    if(!StringUtil.isEmpty(custom3)){
240                            qv.setAt("custom3", 1, custom3);
241                            custom3="custom3";
242                    }
243                    // custom4
244                    if(!StringUtil.isEmpty(custom4)){
245                            qv.setAt("custom4", 1, custom4);
246                            custom4="custom4";
247                    }
248                }
249                    else qv = Caster.toQuery(pc.getVariable(query));
250                
251                    QueryColumn keyColumn=qv.getColumn(key);
252                
253                String[] strBodies=List.toStringArrayTrim(List.listToArrayRemoveEmpty(body,','));
254                QueryColumn[] bodyColumns=new QueryColumn[strBodies.length];
255                for(int i=0;i<bodyColumns.length;i++) {
256                    bodyColumns[i]=qv.getColumn(strBodies[i]);
257                }
258                
259                ir= indexCustom(id,
260                            getValue(qv,title),
261                        keyColumn,
262                        bodyColumns,
263                        language,
264                        getValue(qv,urlpath),
265                        getValue(qv,custom1),
266                        getValue(qv,custom2),
267                        getValue(qv,custom3),
268                        getValue(qv,custom4));
269            }
270            createIndex(si);
271            return ir;
272        }
273                    finally {
274                            unlock(l);
275                    }
276        }
277     
278        private QueryColumn getColumnEL(Query query, String column) {
279            if(StringUtil.isEmpty(column)) return null;
280            QueryColumn c = query.getColumn(column,null);
281            
282            return c;
283        }
284     
285        private Object getValue(Query query, String column) {
286            if(StringUtil.isEmpty(column)) return null;
287            QueryColumn c = query.getColumn(column,null);
288            if(c==null) return column;
289            return c;
290        }
291    
292        /**
293         * @see railo.runtime.search.SearchCollection#indexFile(java.lang.String, java.lang.String, railo.commons.io.res.Resource, java.lang.String)
294         */
295        public final IndexResult indexFile(String id,String title, Resource res, String language) throws SearchException {
296            IndexResult ir=_indexFile(id,title,res,language);
297            changeLastUpdate();
298            return ir;
299        }
300    
301        /**
302         * updates a collection with a file
303         * @param id
304         * @param title
305         * @param file
306         * @param language
307         * @throws SearchException
308         */
309        protected abstract IndexResult _indexFile(String id, String title, Resource file, String language)  throws SearchException;
310    
311        /**
312         * @see railo.runtime.search.SearchCollection#indexPath(java.lang.String, java.lang.String, railo.commons.io.res.Resource, java.lang.String[], boolean, java.lang.String)
313         */
314        public final IndexResult indexPath(String id, String title, Resource dir, String[] extensions, boolean recurse, String language) throws SearchException {
315            IndexResult ir=_indexPath(id,title,dir,extensions,recurse,language);
316            changeLastUpdate();
317            return ir;
318        }
319    
320    
321        /**
322         * updates a collection with a path
323         * @param id
324         * @param title
325         * @param dir
326         * @param recurse
327         * @param extensions
328         * @param language
329         * @throws SearchException
330         */
331        protected abstract IndexResult _indexPath(String id,String title, Resource dir, String[] extensions, boolean recurse, String language) throws SearchException;
332    
333        /**
334         * @see railo.runtime.search.SearchCollection#indexURL(java.lang.String, java.lang.String, java.net.URL, java.lang.String[], boolean, java.lang.String)
335         */
336        public final IndexResult indexURL(String id,String title, URL url, String[] extensions, boolean recurse, String language) throws SearchException {
337            return indexURL(id, title, url, extensions, recurse, language, 10000);
338        }
339            // FUTURE replace this in interface with method above
340        public final IndexResult indexURL(String id,String title, URL url, String[] extensions, boolean recurse, String language,long timeout) throws SearchException {
341            IndexResult ir=_indexURL(id,title,url,extensions,recurse,language,timeout);
342            changeLastUpdate();
343            return ir;
344        } 
345    
346        /**
347         * updates a collection with a url
348         * @param id
349         * @param title
350         * @param recurse
351         * @param extensions
352         * @param url
353         * @param language
354         * @throws SearchException
355         */
356        protected abstract IndexResult _indexURL(String id,String title, URL url, String[] extensions, boolean recurse, String language,long timeout) throws SearchException ;
357        
358        protected abstract IndexResult _indexURL(String id,String title, URL url, String[] extensions, boolean recurse, String language) throws SearchException ;
359    
360        /**
361         * @see railo.runtime.search.SearchCollection#indexCustom(java.lang.String, railo.runtime.type.QueryColumn, railo.runtime.type.QueryColumn, railo.runtime.type.QueryColumn[], java.lang.String, railo.runtime.type.QueryColumn, railo.runtime.type.QueryColumn, railo.runtime.type.QueryColumn, railo.runtime.type.QueryColumn)
362         */
363        public final IndexResult indexCustom(String id, QueryColumn title, QueryColumn keyColumn, QueryColumn[] bodyColumns, String language, 
364                    QueryColumn custom1, QueryColumn custom2, QueryColumn custom3, QueryColumn custom4) throws SearchException {
365            IndexResult ir=_indexCustom(id,title,keyColumn,bodyColumns,language,null,custom1,custom2,custom3,custom4);
366            changeLastUpdate();
367            return ir;
368        }
369        /*public final IndexResult indexCustom(String id, QueryColumn title, QueryColumn keyColumn, QueryColumn[] bodyColumns, String language, 
370                    QueryColumn urlpath,QueryColumn custom1, QueryColumn custom2, QueryColumn custom3, QueryColumn custom4) throws SearchException {
371            IndexResult ir=_indexCustom(id,title,keyColumn,bodyColumns,language,urlpath,custom1,custom2,custom3,custom4);
372            changeLastUpdate();
373            return ir;
374        }*/
375        public final IndexResult indexCustom(String id, Object title, QueryColumn keyColumn, QueryColumn[] bodyColumns, String language, 
376                    Object urlpath,Object custom1, Object custom2, Object custom3, Object custom4) throws SearchException {
377            IndexResult ir=_indexCustom(id,title,keyColumn,bodyColumns,language,urlpath,custom1,custom2,custom3,custom4);
378            changeLastUpdate();
379            return ir;
380        }
381        
382        
383        public final IndexResult deleteCustom(String id, QueryColumn keyColumn) throws SearchException {
384            IndexResult ir = _deleteCustom(id,keyColumn);
385            changeLastUpdate();
386            return ir;
387        }
388        
389        protected abstract IndexResult _deleteCustom(String id, QueryColumn keyColumn) throws SearchException;
390    
391            /**
392         * updates a collection with a custom
393         * @param id
394         * @param title Title for the Index
395         * @param keyColumn Key Column
396         * @param bodyColumns Body Column Array
397         * @param language Language for index
398         * @param custom1 
399         * @param custom2 
400         * @param custom3 
401         * @param custom4 
402         * @throws SearchException
403         */
404        //protected abstract IndexResult _indexCustom(String id,QueryColumn title, QueryColumn keyColumn, QueryColumn[] bodyColumns, String language, QueryColumn custom1, QueryColumn custom2, QueryColumn custom3, QueryColumn custom4) throws SearchException;
405        protected abstract IndexResult _indexCustom(
406                    String id,Object title, QueryColumn keyColumn, QueryColumn[] bodyColumns, String language, Object urlpath
407                    , Object custom1, Object custom2, Object custom3, Object custom4) throws SearchException;
408    
409        /**
410         * @param index
411         * @throws SearchException
412         */
413        private void createIndex(SearchIndex index) throws SearchException {
414            Iterator<String> it = indexes.keySet().iterator();
415            SearchIndex otherIndex=null;
416            
417            while(it.hasNext()) {
418                Object key=it.next();
419                if(key.equals(index.getId())) {
420                    otherIndex=indexes.get(key);
421                    break;
422                }
423            }
424            
425            Element collElement=searchEngine.getCollectionElement(name);
426            
427            // Insert
428            if(otherIndex==null) {
429                addIndex(index);
430                collElement.appendChild(searchEngine.toElement(index));
431            }
432            // Update
433            else {
434                addIndex(index);
435                Element el=searchEngine.getIndexElement(collElement,index.getId());
436                searchEngine.setAttributes(el,index);
437            }
438            changeLastUpdate();
439        }
440    
441        /**
442         * @param index
443         */
444        public void addIndex(SearchIndex index) {
445            indexes.put(index.getId(),index);
446        }
447    
448            /**
449         * @see railo.runtime.search.SearchCollection#getLanguage()
450         */
451            public final String getLanguage() {
452                    return language;
453            }
454        
455            /**
456         * @see railo.runtime.search.SearchCollection#purge()
457         */
458            public final IndexResult purge() throws SearchException {
459                    Lock l = lock();
460            try {
461            indexes.clear();
462            IndexResult ir=_purge();
463            searchEngine.purgeCollection(this);
464            changeLastUpdate();
465            return ir;
466            }
467                    finally {
468                            unlock(l);
469                    }
470            }
471    
472            /**
473             * purge a collection
474             * @throws SearchException
475             */
476            protected abstract IndexResult _purge() throws SearchException;
477    
478        /**
479         * @see railo.runtime.search.SearchCollection#delete()
480         */
481        public final IndexResult delete() throws SearchException {
482            Lock l = lock();
483            try {
484            IndexResult ir=_delete();
485                searchEngine.removeCollection(this);
486                return ir;
487        }
488                    finally {
489                            unlock(l);
490                    }
491        }
492    
493        /**
494         * delete the collection from a file
495             * @throws SearchException
496         */
497        protected abstract IndexResult _delete() throws SearchException;
498    
499        /**
500         * @see railo.runtime.search.SearchCollection#deleteIndex(railo.runtime.PageContext, java.lang.String, short, java.lang.String)
501         */
502        public final IndexResult deleteIndex(PageContext pc,String key,short type,String queryName) throws SearchException {
503            //if(queryName==null) queryName="";
504            Key k;
505            
506            if(type==SearchIndex.TYPE_CUSTOM) {
507                    // delete all when no key is defined
508                    if(StringUtil.isEmpty(key,true))
509                            return deleteIndexNotCustom(pc, key, type, queryName);
510                    
511                    try{
512                            Query qv;
513                    if(!StringUtil.isEmpty(queryName)) {
514                            k=KeyImpl.init(key);
515                            qv = Caster.toQuery(pc.getVariable(queryName));
516                    }
517                    else {
518                            k=KeyImpl.ID;
519                        Key[] cols = new Key[]{k};
520                        String[] types = new String[]{"VARCHAR"};
521                            qv=new QueryImpl(cols,types, 1,"query");
522                            qv.setAtEL(k, 1, key);
523                    }
524                            
525                            
526                            
527                        QueryColumn keyColumn=qv.getColumn(k);
528                            return deleteCustom("custom", keyColumn);
529                    }
530                    catch(PageException pe){
531                            throw new SearchException(pe);
532                    }
533            }
534            return deleteIndexNotCustom(pc, key, type, queryName);
535            
536            
537        }
538        
539    
540        public final IndexResult deleteIndexNotCustom(PageContext pc,String key,short type,String queryName) throws SearchException {
541            Iterator<String> it = indexes.keySet().iterator();
542            while(it.hasNext()) {
543                String id = it.next();
544                if(id.equals(SearchIndex.toId(type,key,queryName))) {
545                    SearchIndex index=indexes.get(id);
546                    
547                    IndexResult ir=_deleteIndex(index.getId());
548                    Element indexEl=searchEngine.getIndexElement(searchEngine.getCollectionElement(name),index.getId());
549                    if(indexEl!=null)indexEl.getParentNode().removeChild(indexEl);
550                    changeLastUpdate();
551                        return ir; 
552                }
553            }
554            return new IndexResultImpl(0,0,0);
555        }
556    
557        /**
558         * delete a Index from collection
559             * @param id id ofthe Index to delete
560         * @throws SearchException
561         */ 
562        protected abstract IndexResult _deleteIndex(String id) throws SearchException;
563        
564            /**
565         * @see railo.runtime.search.SearchCollection#getPath()
566         */
567            public final Resource getPath() {
568                    return path;
569            }
570    
571        /**
572         * @see railo.runtime.search.SearchCollection#getCreated()
573         */
574        public DateTime getCreated() {
575            return created;
576        }
577        
578            /**
579         * @see railo.runtime.search.SearchCollection#getLastUpdate()
580         */
581            public final DateTime getLastUpdate() {
582                    return lastUpdate;
583            }
584    
585            /**
586         * @see railo.runtime.search.SearchCollection#getName()
587         */
588            public final String getName() {
589                    return name;
590            } 
591            
592        /**
593         * @see railo.runtime.search.SearchCollection#getLogger()
594         */
595        public final Log getLogger() {
596            return log;
597        }
598        
599        /**
600         * @see railo.runtime.search.SearchCollection#getSearchEngine()
601         */
602        public final SearchEngine getSearchEngine() {
603            return searchEngine;
604        }
605    
606        /**
607         * change the last update attribute and store it
608         * @throws SearchException
609         */
610        private void changeLastUpdate() throws SearchException {
611            lastUpdate=new DateTimeImpl();
612            searchEngine.store();
613        }
614        
615        /**
616         * @see railo.runtime.search.SearchCollection#created()
617         */
618        public Object created() {
619            return created;
620        }
621    
622        /**
623         * @see railo.runtime.search.SearchCollection#search(railo.runtime.search.SearchData, railo.runtime.type.Query, java.lang.String, java.lang.String, short, int, int, java.lang.String, java.lang.String[])
624         */
625        public final int search(SearchData data, Query qry,String criteria, String language, short type,int startrow,int maxrow,String categoryTree, String[] categories) throws SearchException, PageException {
626            int len=qry.getRecordcount();
627            SearchResulItem[] records;
628            
629            AddionalAttrs aa = AddionalAttrs.getAddionlAttrs();
630            boolean hasRowHandling=false;
631            aa.setStartrow(startrow);
632            if(maxrow!=-1)aa.setMaxrows(maxrow-len);
633            
634            Lock l = lock();
635            try {
636                    records = _search(data, criteria,language,type,categoryTree,categories);
637            }
638            finally {
639                    unlock(l);
640                    if(hasRowHandling=aa.hasRowHandling())
641                            startrow = aa.getStartrow();
642                    
643            }
644            
645            
646            
647            // Startrow
648            if(!hasRowHandling && startrow>1) {
649                
650                    if(startrow>records.length) {
651                    return startrow-records.length;
652                }
653                int start=startrow-1;
654                
655                SearchResulItem[] tmpRecords=new SearchResulItem[records.length-start];
656                for(int i=start;i<records.length;i++) {
657                    tmpRecords[i-start]=records[i];
658                }
659                records=tmpRecords;
660                startrow=1;
661            }
662            
663            
664            if(!ArrayUtil.isEmpty(records)) {
665                
666                int to=(!hasRowHandling && maxrow>-1 && len+records.length>maxrow)?maxrow-len:records.length;
667                qry.addRow(to);
668                
669                String title;
670                String custom1;
671                String custom2;
672                String custom3;
673                String custom4;
674                String url;
675                SearchResultItemPro record;
676                SearchIndex si;
677                boolean hasContextSummary=false;
678                for(int y=0;y<to;y++) {
679                            
680                    int row=len+y+1;
681                    record = SearchResulItemImpl.toSearchResultItemPro(records[y]);
682                    if(y==0)hasContextSummary=record instanceof SearchResultItemPro;
683                    si=(SearchIndex)indexes.get(record.getId());
684    
685                    title=record.getTitle();
686                    custom1=record.getCustom1();
687                    custom2=record.getCustom2();
688                    custom3=record.getCustom3();
689                    custom4=record.getCustom4();
690                    url=record.getUrl();
691                    
692                    qry.setAt(KeyImpl.TITLE,row,title);
693                    qry.setAt("custom1",row,custom1);
694                    qry.setAt("custom2",row,custom2);
695                    qry.setAt("custom3",row,custom3);
696                    qry.setAt("custom4",row,custom4);
697                    qry.setAt("categoryTree",row,record.getCategoryTree());
698                    qry.setAt("category",row,record.getCategory());
699                    qry.setAt(KeyImpl.TYPE,row,record.getMimeType());
700                    qry.setAt("author",row,record.getAuthor());
701                    qry.setAt(KeyImpl.SIZE,row,record.getSize());
702    
703                    qry.setAt("summary",row,record.getSummary());
704                    if(hasContextSummary)qry.setAt("context",row,((SearchResultItemPro)record).getContextSummary());
705                    qry.setAt("score",row,new Float(record.getScore()));
706                    qry.setAt(KeyImpl.KEY,row,record.getKey());
707                    qry.setAt(KeyImpl.URL,row,url);
708                    qry.setAt("collection",row,getName());
709                    qry.setAt("rank",row,new Double(row));
710                    String rootPath,file;
711                    String urlPath;
712                    if(si!=null) {
713                            switch(si.getType()){
714                            case SearchIndex.TYPE_PATH:
715                                    rootPath = si.getKey();
716                                    rootPath=rootPath.replace(FileUtil.FILE_ANTI_SEPERATOR,FileUtil.FILE_SEPERATOR);
717                                    file=record.getKey();
718                                    file=file.replace(FileUtil.FILE_ANTI_SEPERATOR,FileUtil.FILE_SEPERATOR);
719                                    qry.setAt(KeyImpl.URL,row,toURL(si.getUrlpath(),StringUtil.replace(file, rootPath, "", true)));
720                                    
721                                    
722                            break;
723                            case SearchIndex.TYPE_URL:
724                                    rootPath = si.getKey();
725                                    urlPath = si.getUrlpath();
726                                    try {
727                                            rootPath = getDirectory(si.getKey());
728                                                    } 
729                                    catch (MalformedURLException e) {}
730                                    if(StringUtil.isEmpty(urlPath))urlPath=rootPath;
731                                    file=record.getKey();
732                                    qry.setAt(KeyImpl.URL,row,toURL(urlPath,StringUtil.replace(file, rootPath, "", true)));
733                                    
734                                    
735                            break;
736                            case SearchIndex.TYPE_CUSTOM:
737                                    qry.setAt(KeyImpl.URL,row,url);
738                            break;
739                            default:
740                                    qry.setAt(KeyImpl.URL,row,toURL(si.getUrlpath(),url));
741                            break;
742                            }
743                            
744                            
745                        if(StringUtil.isEmpty(title))      qry.setAt(KeyImpl.TITLE,row,si.getTitle());
746                        if(StringUtil.isEmpty(custom1))    qry.setAt("custom1",row,si.getCustom1());
747                        if(StringUtil.isEmpty(custom2))    qry.setAt("custom2",row,si.getCustom2());
748                        if(StringUtil.isEmpty(custom3))    qry.setAt("custom3",row,si.getCustom3());
749                        if(StringUtil.isEmpty(custom4))    qry.setAt("custom4",row,si.getCustom4());
750                        
751                    }
752                }
753            }
754            return startrow;
755        }
756    
757        public static String getDirectory(String strUrl) throws MalformedURLException {
758            URL url = new URL(strUrl);
759            String path=url.getPath(); 
760            int slashIndex = path.lastIndexOf('/');
761            int dotIndex = path.lastIndexOf('.');
762            // no dot
763            if(dotIndex==-1){
764                    if(path.endsWith("/"))return HTTPUtil.removeRef(url).toExternalForm();
765                    return HTTPUtil.removeRef(new URL(
766                                    url.getProtocol(),
767                                    url.getHost(),
768                                    url.getPort(),path+"/")).toExternalForm();
769            }
770            if(slashIndex>dotIndex){
771                    path=path.substring(0,dotIndex);
772                    slashIndex = path.lastIndexOf('/');
773            }
774            
775            return HTTPUtil.removeRef(new URL(
776                                    url.getProtocol(),
777                                    url.getHost(),
778                                    url.getPort(),path.substring(0,slashIndex+1))).toExternalForm();
779            }
780    
781            private static String toURL(String url, String path) {
782            if(StringUtil.isEmpty(url)) return path;
783            if(StringUtil.isEmpty(path)) return url;
784            
785            url=url.replace('\\','/');
786            path=path.replace('\\','/');
787            if(StringUtil.startsWith(path, '/'))path=path.substring(1);
788            if(StringUtil.endsWith(url, '/'))url=url.substring(0,url.length()-1);
789            
790            if(StringUtil.startsWithIgnoreCase(path, url))
791                    return path;
792            return url+"/"+path;
793        }
794        
795            
796            
797            
798    
799        protected SearchIndex[] getIndexes() {
800            Iterator<Entry<String, SearchIndex>> it = indexes.entrySet().iterator();
801                    int len=indexes.size();
802                    SearchIndex[] rtn=new SearchIndex[len];
803                    int count=0;
804                    while(it.hasNext()) {
805                            rtn[count++]=it.next().getValue();
806                    }
807                    return rtn;
808            }
809    
810    
811            private Lock lock() throws SearchException {
812                    try {
813                            return lock.lock(getId(),LOCK_TIMEOUT);
814                            //manager.lock(LockManager.TYPE_EXCLUSIVE,getId(),LOCK_TIMEOUT,ThreadLocalPageContext.get().getId());
815                    } 
816                    catch (Exception e) {
817                            throw new SearchException(e);
818                    }
819                    
820            }
821    
822            private void unlock(Lock l) {
823                    lock.unlock(l);
824                    //manager.unlock(ThreadLocalPageContext.get().getId());
825            }
826    
827    
828            private String getId() {
829                    return path.getRealResource(name).getAbsolutePath();
830            }
831    
832            // FUTURE
833            public Object getIndexesAsQuery() {
834                    Iterator<Entry<String, SearchIndex>> it = indexes.entrySet().iterator();
835                    
836                    final String v="VARCHAR";
837            Query query=null;
838            String[] cols = new String[]{
839                            "categories","categoryTree","custom1","custom2","custom3","custom4","extensions",
840                            "key","language","query","title","urlpath","type"};
841            String[] types = new String[]{
842                            v,v,v,v,v,v,v,
843                            v,v,v,v,v,v};
844            try {
845                query=new QueryImpl(cols,types, 0,"query");
846            } catch (DatabaseException e) {
847                query=new QueryImpl(cols, 0,"query");
848            }
849            
850            Entry<String, SearchIndex> entry;
851            SearchIndex index;
852            int row=0;
853                    while(it.hasNext()) {
854                            query.addRow();
855                            row++;
856                    entry = it.next();
857                    index= entry.getValue();
858                    if(index==null)continue;
859                    try {
860                            
861                    query.setAt("categories",row,List.arrayToList(index.getCategories(),""));
862                    query.setAt("categoryTree",row,index.getCategoryTree());
863                    
864                    query.setAt("custom1",row,index.getCustom1());
865                    query.setAt("custom2",row,index.getCustom2());
866                    query.setAt("custom3",row,index.getCustom3());
867                    query.setAt("custom4",row,index.getCustom4());
868                    
869                    query.setAt("extensions",row,List.arrayToList(index.getExtensions(),","));
870                    query.setAt(KeyImpl.KEY,row,index.getKey());
871                    query.setAt("language",row,index.getLanguage());
872                    query.setAt("query",row,index.getQuery());
873                    query.setAt(KeyImpl.TITLE,row,index.getTitle());
874                    query.setAt("urlpath",row,index.getUrlpath());
875                    query.setAt(KeyImpl.TYPE,row,SearchIndex.toStringTypeEL(index.getType()));
876                    
877                    }
878                        catch(PageException pe) {}
879                }
880                    return query;
881            }
882    
883    }