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