0001: /**********************************************************************************
0002: * $URL: https://source.sakaiproject.org/svn/citations/tags/sakai_2-4-1/citations-impl/impl/src/java/org/sakaiproject/citation/impl/BaseSearchManager.java $
0003: * $Id: BaseSearchManager.java 29904 2007-05-03 13:15:30Z ajpoland@iupui.edu $
0004: ***********************************************************************************
0005: *
0006: * Copyright (c) 2006 The Sakai Foundation.
0007: *
0008: * Licensed under the Educational Community License, Version 1.0 (the "License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.opensource.org/licenses/ecl1.php
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: **********************************************************************************/package org.sakaiproject.citation.impl;
0021:
0022: import java.io.IOException;
0023: import java.util.Enumeration;
0024: import java.util.Hashtable;
0025: import java.util.Iterator;
0026: import java.util.List;
0027: import java.util.Map;
0028: import java.util.Random;
0029: import java.util.Set;
0030: import java.util.TreeSet;
0031: import java.util.Vector;
0032:
0033: import org.apache.commons.logging.Log;
0034: import org.apache.commons.logging.LogFactory;
0035: import org.osid.OsidContext;
0036: import org.osid.OsidException;
0037: import org.osid.repository.Asset;
0038: import org.osid.repository.AssetIterator;
0039: import org.osid.repository.Repository;
0040: import org.osid.repository.RepositoryException;
0041: import org.osid.repository.RepositoryIterator;
0042: import org.osid.repository.RepositoryManager;
0043: import org.osid.shared.ObjectIterator;
0044: import org.osid.shared.SharedException;
0045: import org.osid.shared.Type;
0046: import org.osid.shared.TypeIterator;
0047: import org.sakaiproject.citation.util.api.CQLSearchQuery;
0048: import org.sakaiproject.citation.util.api.SearchQuery;
0049: import org.sakaiproject.citation.api.ActiveSearch;
0050: import org.sakaiproject.citation.api.Citation;
0051: import org.sakaiproject.citation.api.CitationCollection;
0052: import org.sakaiproject.citation.api.CitationIterator;
0053: import org.sakaiproject.citation.api.ConfigurationService;
0054: import org.sakaiproject.citation.api.SearchCategory;
0055: import org.sakaiproject.citation.api.SearchDatabase;
0056: import org.sakaiproject.citation.api.SearchDatabaseHierarchy;
0057: import org.sakaiproject.citation.api.SearchManager;
0058: import org.sakaiproject.citation.cover.CitationService;
0059: import org.sakaiproject.citation.util.api.SearchException;
0060: import org.sakaiproject.component.api.ServerConfigurationService;
0061: import org.sakaiproject.component.cover.ComponentManager;
0062: import org.sakaiproject.entity.api.Entity;
0063: import org.sakaiproject.exception.IdUnusedException;
0064: import org.sakaiproject.time.cover.TimeService;
0065: import org.sakaiproject.tool.api.SessionManager;
0066:
0067: import org.xml.sax.Attributes;
0068: import org.xml.sax.SAXException;
0069: import org.xml.sax.SAXParseException;
0070:
0071: import javax.xml.parsers.SAXParserFactory;
0072: import javax.xml.parsers.ParserConfigurationException;
0073: import javax.xml.parsers.SAXParser;
0074:
0075: /**
0076: *
0077: */
0078: public class BaseSearchManager implements SearchManager {
0079: public class BasicObjectIterator implements ObjectIterator {
0080: protected int i = 0;
0081: protected Vector vector = new Vector();
0082:
0083: public BasicObjectIterator(List keys) throws SharedException {
0084: this .vector = new Vector(keys);
0085: }
0086:
0087: public boolean hasNextObject() throws SharedException {
0088: return i < vector.size();
0089: }
0090:
0091: public java.io.Serializable nextObject() throws SharedException {
0092: if (i < vector.size()) {
0093: return (java.io.Serializable) vector.elementAt(i++);
0094: } else {
0095: throw new SharedException(
0096: SharedException.NO_MORE_ITERATOR_ELEMENTS);
0097: }
0098: }
0099: }
0100:
0101: public class BasicSearch implements ActiveSearch {
0102: protected List m_assets;
0103: protected List m_pageOrder;
0104: protected Set m_duplicateCheck;
0105: protected boolean m_firstPage;
0106: protected String m_searchId;
0107: protected String m_searchType;
0108: protected boolean m_lastPage;
0109: protected boolean m_newSearch;
0110: protected Integer m_pageSize;
0111: protected Integer m_startRecord;
0112:
0113: protected AssetIterator m_assetIterator;
0114: protected Integer m_numRecordsFetched;
0115: protected Integer m_numRecordsFound;
0116: protected Integer m_numRecordsMerged;
0117: protected Repository m_repository;
0118: protected String m_repositoryId;
0119: protected String m_repositoryName;
0120: protected SearchQuery m_basicQuery;
0121: protected SearchQuery m_advancedQuery;
0122: protected String m_sortBy;
0123:
0124: protected CitationCollection m_searchResults;
0125: protected CitationCollection m_savedResults;
0126: protected CitationIterator m_resultsIterator;
0127: protected Map m_index;
0128: protected int m_lastPageViewed = -1;
0129: protected CitationIterator m_searchIterator;
0130: protected int start = 1;
0131: protected int end = DEFAULT_PAGE_SIZE;
0132: protected int m_viewPageSize = DEFAULT_PAGE_SIZE;
0133: protected String statusMessage = null;
0134:
0135: // saves the thread that the current search is running in
0136: protected Thread m_searchThread;
0137:
0138: /**
0139: * Constructor
0140: */
0141: public BasicSearch() {
0142: this .m_searchId = newSearchId();
0143: this .m_assets = new Vector();
0144: m_pageOrder = new Vector();
0145: this .m_index = new Hashtable();
0146: m_duplicateCheck = new TreeSet();
0147: m_savedResults = CitationService.getTemporaryCollection();
0148: m_newSearch = true;
0149: m_firstPage = true;
0150: m_lastPage = false;
0151: m_pageSize = new Integer(DEFAULT_PAGE_SIZE);
0152: m_startRecord = new Integer(DEFAULT_START_RECORD);
0153: m_sortBy = DEFAULT_SORT_BY;
0154:
0155: }
0156:
0157: /**
0158: */
0159: public BasicSearch(CitationCollection searchResults) {
0160: this .m_searchId = newSearchId();
0161: this .m_assets = new Vector();
0162: m_pageOrder = new Vector();
0163: this .m_index = new Hashtable();
0164: m_duplicateCheck = new TreeSet();
0165: m_savedResults = CitationService.getTemporaryCollection();
0166: m_newSearch = true;
0167: m_firstPage = true;
0168: m_lastPage = false;
0169: m_pageSize = new Integer(DEFAULT_PAGE_SIZE);
0170: m_startRecord = new Integer(DEFAULT_START_RECORD);
0171: m_sortBy = DEFAULT_SORT_BY;
0172:
0173: }
0174:
0175: /* (non-Javadoc)
0176: * @see org.sakaiproject.citation.api.ActiveSearch#getAssetIterator()
0177: */
0178: protected AssetIterator getAssetIterator() {
0179: return m_assetIterator;
0180: }
0181:
0182: /* (non-Javadoc)
0183: * @see org.sakaiproject.citation.api.ActiveSearch#getAssets()
0184: */
0185: public List getAssets() {
0186: return m_assets;
0187: }
0188:
0189: /* (non-Javadoc)
0190: * @see org.sakaiproject.citation.api.ActiveSearch#getGuid()
0191: */
0192: public String getSearchId() {
0193: return m_searchId;
0194: }
0195:
0196: /* (non-Javadoc)
0197: * @see org.sakaiproject.citation.api.ActiveSearch#getNumRecordsFetched()
0198: */
0199: public Integer getNumRecordsFetched() {
0200: return m_numRecordsFetched;
0201: }
0202:
0203: /* (non-Javadoc)
0204: * @see org.sakaiproject.citation.api.ActiveSearch#getNumRecordsFound()
0205: */
0206: public Integer getNumRecordsFound() {
0207: return m_numRecordsFound;
0208: }
0209:
0210: /* (non-Javadoc)
0211: * @see org.sakaiproject.citation.api.ActiveSearch#getNumRecordsMerged()
0212: */
0213: public Integer getNumRecordsMerged() {
0214: return m_numRecordsMerged;
0215: }
0216:
0217: protected void setPageLimits(int page) throws SearchException {
0218:
0219: }
0220:
0221: /**
0222: * @param page
0223: * @return
0224: * @throws SearchException
0225: */
0226: public List viewPage(int page) throws SearchException {
0227: List citations = new Vector();
0228:
0229: if (page < 0) {
0230: page = 0;
0231: }
0232:
0233: int oldStart = this .start;
0234: int oldEnd = this .end;
0235: this .start = page * m_viewPageSize;
0236: this .end = start + m_viewPageSize;
0237: if (start > this .m_pageOrder.size() + 1) {
0238: throw new SearchException("Request beyond next page");
0239: } else {
0240: if (this .m_pageOrder.isEmpty()) {
0241: doSearch(this );
0242: } else if (end > this .m_pageOrder.size()) {
0243: try {
0244: doNextPage(this );
0245: } catch (SearchException e) {
0246: this .start = oldStart;
0247: this .end = oldEnd;
0248: setStatusMessage(m_repository);
0249: throw new SearchException(e.getMessage());
0250: }
0251: }
0252: }
0253:
0254: if (end > m_pageOrder.size()) {
0255: end = m_pageOrder.size();
0256: }
0257:
0258: Citation citation = null;
0259: for (int i = start; i < end; i++) {
0260: String id = (String) m_pageOrder.get(i);
0261: try {
0262: citation = m_searchResults.getCitation(id);
0263: citations.add(citation);
0264: } catch (IdUnusedException e) {
0265: m_log
0266: .info("BasicSearch.getPage() unable to retrieve ciataion: "
0267: + id);
0268: }
0269: }
0270: m_lastPageViewed = page;
0271:
0272: return citations;
0273: }
0274:
0275: /**
0276: *
0277: */
0278: protected void setStatusMessage(Repository repository) {
0279: try {
0280: this .statusMessage = getSearchStatusMessage(repository);
0281: } catch (SearchException e) {
0282: this .statusMessage = e.getMessage();
0283: }
0284: }
0285:
0286: public void setStatusMessage(String msg) {
0287: this .statusMessage = msg;
0288: }
0289:
0290: public void setStatusMessage() {
0291: this .statusMessage = null;
0292: }
0293:
0294: public String getStatusMessage() {
0295: return this .statusMessage;
0296: }
0297:
0298: /* (non-Javadoc)
0299: * @see org.sakaiproject.citation.api.ActiveSearch#getPageSize()
0300: */
0301: public Integer getPageSize() {
0302: return m_pageSize;
0303: }
0304:
0305: /* (non-Javadoc)
0306: * @see org.sakaiproject.citation.api.ActiveSearch#getRepository()
0307: */
0308: public Repository getRepository() {
0309: return m_repository;
0310: }
0311:
0312: /* (non-Javadoc)
0313: * @see org.sakaiproject.citation.api.ActiveSearch#getRepositoryId()
0314: */
0315: public String getRepositoryId() {
0316: return m_repositoryId;
0317: }
0318:
0319: /* (non-Javadoc)
0320: * @see org.sakaiproject.citation.api.ActiveSearch#getRepositoryName()
0321: */
0322: public String getRepositoryName() {
0323: return m_repositoryName;
0324: }
0325:
0326: /* (non-Javadoc)
0327: * @see org.sakaiproject.citation.api.ActiveSearch#getSearchCriteria()
0328: */
0329: public SearchQuery getBasicQuery() {
0330: return m_basicQuery;
0331: }
0332:
0333: /* (non-Javadoc)
0334: * @see org.sakaiproject.citation.api.ActiveSearch#getSortBy()
0335: */
0336: public String getSortBy() {
0337: return m_sortBy.toLowerCase();
0338: }
0339:
0340: /* (non-Javadoc)
0341: * @see org.sakaiproject.citation.api.ActiveSearch#getStartRecord()
0342: */
0343: public Integer getStartRecord() {
0344: if (m_startRecord.intValue() < MIN_START_RECORD) {
0345: m_startRecord = new Integer(MIN_START_RECORD);
0346: }
0347: return m_startRecord;
0348: }
0349:
0350: /**
0351: * @return the firstPage
0352: */
0353: public boolean isFirstPage() {
0354: return m_firstPage;
0355: }
0356:
0357: /**
0358: * @return the lastPage
0359: */
0360: public boolean isLastPage() {
0361: return m_lastPage;
0362: }
0363:
0364: /**
0365: * @return the newSearch
0366: */
0367: public boolean isNewSearch() {
0368: return m_newSearch;
0369: }
0370:
0371: /* (non-Javadoc)
0372: * @see org.sakaiproject.citation.api.ActiveSearch#setAssetIterator(org.osid.repository.AssetIterator)
0373: */
0374: public void setAssetIterator(AssetIterator assetIterator) {
0375: this .m_assetIterator = assetIterator;
0376: }
0377:
0378: /* (non-Javadoc)
0379: * @see org.sakaiproject.citation.api.ActiveSearch#setAssets(java.util.List)
0380: */
0381: public void setAssets(List assets) {
0382: this .m_assets = assets;
0383: }
0384:
0385: /* (non-Javadoc)
0386: * @see org.sakaiproject.citation.api.ActiveSearch#setFirstPage(boolean)
0387: */
0388: public void setFirstPage(boolean firstPage) {
0389: this .m_firstPage = firstPage;
0390: }
0391:
0392: /* (non-Javadoc)
0393: * @see org.sakaiproject.citation.api.ActiveSearch#setGuid(java.lang.String)
0394: */
0395: public void setGuid(String guid) {
0396: this .m_searchId = guid;
0397: }
0398:
0399: /* (non-Javadoc)
0400: * @see org.sakaiproject.citation.api.ActiveSearch#setLastPage(boolean)
0401: */
0402: public void setLastPage(boolean lastPage) {
0403: this .m_lastPage = lastPage;
0404: }
0405:
0406: /* (non-Javadoc)
0407: * @see org.sakaiproject.citation.api.ActiveSearch#setNewSearch(boolean)
0408: */
0409: public void setNewSearch(boolean newSearch) {
0410: this .m_newSearch = newSearch;
0411: }
0412:
0413: /**
0414: * @param numRecordsFetched the numRecordsFetched to set
0415: */
0416: public void setNumRecordsFetched(Integer numRecordsFetched) {
0417: m_numRecordsFetched = numRecordsFetched;
0418: }
0419:
0420: /* (non-Javadoc)
0421: * @see org.sakaiproject.citation.api.ActiveSearch#setNumRecordsFound(java.lang.Integer)
0422: */
0423: public void setNumRecordsFound(Integer numRecordsFound) {
0424: this .m_numRecordsFound = numRecordsFound;
0425: }
0426:
0427: /* (non-Javadoc)
0428: * @see org.sakaiproject.citation.api.ActiveSearch#setNumRecordsMerged(java.lang.Integer)
0429: */
0430: public void setNumRecordsMerged(Integer numRecordsMerged) {
0431: this .m_numRecordsMerged = numRecordsMerged;
0432: }
0433:
0434: /**
0435: * @param pageSize the pageSize to set
0436: */
0437: public void setPageSize(Integer pageSize) {
0438: if (pageSize == null || pageSize.intValue() < 1) {
0439: m_pageSize = new Integer(DEFAULT_PAGE_SIZE);
0440: } else {
0441: m_pageSize = pageSize;
0442: }
0443: }
0444:
0445: /**
0446: * @param pageSize the pageSize to set
0447: */
0448: public void setPageSize(String pageSize) {
0449: if (pageSize == null || pageSize.trim().equals("")) {
0450: m_pageSize = new Integer(DEFAULT_PAGE_SIZE);
0451: } else {
0452: try {
0453: m_pageSize = new Integer(pageSize);
0454: } catch (NumberFormatException e) {
0455: m_pageSize = new Integer(DEFAULT_PAGE_SIZE);
0456: }
0457: }
0458: }
0459:
0460: /* (non-Javadoc)
0461: * @see org.sakaiproject.citation.api.ActiveSearch#setRepository(org.osid.repository.Repository)
0462: */
0463: public void setRepository(Repository repository) {
0464: this .m_repository = repository;
0465: }
0466:
0467: /* (non-Javadoc)
0468: * @see org.sakaiproject.citation.api.ActiveSearch#setRepositoryId(java.lang.String)
0469: */
0470: public void setRepositoryId(String repositoryId) {
0471: this .m_repositoryId = repositoryId;
0472: }
0473:
0474: /* (non-Javadoc)
0475: * @see org.sakaiproject.citation.api.ActiveSearch#setRepositoryName(java.lang.String)
0476: */
0477: public void setRepositoryName(String repositoryName) {
0478: this .m_repositoryName = repositoryName;
0479: }
0480:
0481: /* (non-Javadoc)
0482: * @see org.sakaiproject.citation.api.ActiveSearch#setSearchCriteria(java.lang.String)
0483: */
0484: public void setBasicQuery(SearchQuery basicQuery) {
0485: this .m_basicQuery = basicQuery;
0486: }
0487:
0488: /* (non-Javadoc)
0489: * @see org.sakaiproject.citation.api.ActiveSearch#setSortBy(java.lang.String)
0490: */
0491: public void setSortBy(String sortBy) {
0492: this .m_sortBy = sortBy;
0493: }
0494:
0495: /* (non-Javadoc)
0496: * @see org.sakaiproject.citation.api.ActiveSearch#setStartRecord(java.lang.Integer)
0497: */
0498: public void setStartRecord(Integer startRecord) {
0499: if (startRecord.intValue() < MIN_START_RECORD) {
0500: this .m_startRecord = new Integer(MIN_START_RECORD);
0501: } else {
0502: this .m_startRecord = startRecord;
0503: }
0504: }
0505:
0506: /* (non-Javadoc)
0507: * @see org.sakaiproject.citation.api.ActiveSearch#getSearchResults()
0508: */
0509: public CitationCollection getSearchResults() {
0510: return m_searchResults;
0511: }
0512:
0513: /**
0514: * @param searchResults the searchResults to set
0515: */
0516: public void setSearchResults(CitationCollection searchResults) {
0517: m_searchResults = searchResults;
0518: }
0519:
0520: /* (non-Javadoc)
0521: * @see org.sakaiproject.citation.api.ActiveSearch#setStartRecord(java.lang.String)
0522: */
0523: public void setStartRecord(String startRecord) {
0524: if (startRecord == null || startRecord.trim().equals("")) {
0525: m_startRecord = new Integer(DEFAULT_START_RECORD);
0526: } else {
0527: try {
0528: m_startRecord = new Integer(startRecord);
0529: } catch (NumberFormatException e) {
0530: m_startRecord = new Integer(DEFAULT_START_RECORD);
0531: }
0532: }
0533: }
0534:
0535: /* (non-Javadoc)
0536: * @see org.sakaiproject.citation.api.ActiveSearch#getIndex()
0537: */
0538: public Map getIndex() {
0539: return m_index;
0540: }
0541:
0542: /* (non-Javadoc)
0543: * @see org.sakaiproject.citation.api.ActiveSearch#setIndex(java.util.Map)
0544: */
0545: public void setIndex(Map index) {
0546: m_index = new Hashtable(index);
0547: }
0548:
0549: /**
0550: * @return
0551: */
0552: public Set getDuplicateCheck() {
0553: if (m_duplicateCheck == null) {
0554: m_duplicateCheck = new TreeSet();
0555: }
0556: return m_duplicateCheck;
0557: }
0558:
0559: /* (non-Javadoc)
0560: * @see org.sakaiproject.citation.api.ActiveSearch#prepareForNextPage()
0561: */
0562: public void prepareForNextPage() {
0563: Iterator it = m_searchResults.getCitations().iterator();
0564: while (it.hasNext()) {
0565: Citation citation = (Citation) it.next();
0566: if (!m_pageOrder.contains(citation.getId())) {
0567: m_pageOrder.add(citation.getId());
0568: }
0569: }
0570:
0571: this .m_savedResults.addAll(this .m_searchResults);
0572: this .m_searchResults.clear();
0573: }
0574:
0575: /* (non-Javadoc)
0576: * @see org.sakaiproject.citation.api.ActiveSearch#getLastPageViewed()
0577: */
0578: public int getViewPageNumber() {
0579: return m_lastPageViewed;
0580: }
0581:
0582: /* (non-Javadoc)
0583: * @see org.sakaiproject.citation.api.ActiveSearch#getLastPageViewed()
0584: */
0585: public int getViewPageSize() {
0586: return m_viewPageSize;
0587: }
0588:
0589: /* (non-Javadoc)
0590: * @see org.sakaiproject.citation.api.ActiveSearch#viewPage()
0591: */
0592: public List viewPage() {
0593: try {
0594: return viewPage(0);
0595: } catch (SearchException e) {
0596: m_log.warn("viewPage ", e);
0597: }
0598: return null;
0599: }
0600:
0601: public int getFirstRecordIndex() {
0602: return start;
0603: }
0604:
0605: public int getLastRecordIndex() {
0606: return end;
0607: }
0608:
0609: public void setViewPageSize(int size) {
0610: m_viewPageSize = size;
0611: }
0612:
0613: public String getSearchType() {
0614: return m_searchType;
0615: }
0616:
0617: public void setSearchType(String searchType) {
0618: m_searchType = searchType;
0619: }
0620:
0621: public SearchQuery getAdvancedQuery() {
0622: return m_advancedQuery;
0623: }
0624:
0625: public void setAdvancedQuery(SearchQuery advancedQuery) {
0626: m_advancedQuery = advancedQuery;
0627: }
0628:
0629: public Thread getSearchThread() {
0630: return m_searchThread;
0631: }
0632:
0633: public void setSearchThread(Thread searchThread) {
0634: m_searchThread = searchThread;
0635: }
0636:
0637: }
0638:
0639: public class BasicSearchProperties implements
0640: org.osid.shared.Properties {
0641: protected List keys;
0642: protected java.util.Properties properties;
0643: protected Type type = new BasicType("sakaibrary", "properties",
0644: "asynchMetasearch");
0645:
0646: public BasicSearchProperties(java.util.Properties properties) {
0647: this .keys = new Vector();
0648: this .properties = properties;
0649:
0650: Enumeration keyNames = properties.keys();
0651: while (keyNames.hasMoreElements()) {
0652: this .keys.add((java.io.Serializable) keyNames
0653: .nextElement());
0654: }
0655: }
0656:
0657: public ObjectIterator getKeys() throws SharedException {
0658: return new BasicObjectIterator(keys);
0659: }
0660:
0661: public java.io.Serializable getProperty(java.io.Serializable key)
0662: throws SharedException {
0663: return (java.io.Serializable) properties.get(key);
0664: }
0665:
0666: public Type getType() throws SharedException {
0667: return type;
0668: }
0669: }
0670:
0671: public class BasicType extends org.osid.shared.Type {
0672:
0673: protected BasicType(String authority, String domain,
0674: String keyword) {
0675: super (authority, domain, keyword);
0676: }
0677:
0678: public BasicType(String authority, String domain,
0679: String keyword, String description) {
0680: super (authority, domain, keyword, description);
0681: }
0682:
0683: // public final Type CITATION = new BasicType("sakaibrary", "recordStructure", "citation");
0684: // public final Type CREATOR = new BasicType("mit.edu", "partStructure", "creator");
0685: // public final Type DATE = new BasicType("mit.edu", "partStructure", "date");
0686: // public final Type DATE_RETRIEVED = new BasicType("sakaibrary", "partStructure", "dateRetrieved");
0687: // public final Type DOI = new BasicType("sakaibrary", "partStructure", "doi");
0688: // public final Type EDITION = new BasicType("sakaibrary", "partStructure", "edition");
0689: // public final Type END_PAGE = new BasicType("sakaibrary", "partStructure", "endPage");
0690: // public final Type INLINE_CITATION = new BasicType("sakaibrary", "partStructure", "inLineCitation");
0691: // public final Type ISN_IDENTIFIER = new BasicType("sakaibrary", "partStructure", "isnIdentifier");
0692: // public final Type ISSUE = new BasicType("sakaibrary", "partStructure", "issue");
0693: // public final Type LANGUAGE = new BasicType("mit.edu", "partStructure", "language");
0694: // public final Type LOC_IDENTIFIER = new BasicType("sakaibrary", "partStructure", "locIdentifier");
0695: // public final Type NOTE = new BasicType("sakaibrary", "partStructure", "note");
0696: // public final Type OPEN_URL = new BasicType("sakaibrary", "partStructure", "openUrl");
0697: // public final Type PAGES = new BasicType("sakaibrary", "partStructure", "pages");
0698: // public final Type PUB_LOCATION = new BasicType("sakaibrary", "partStructure", "publicationLocation");
0699: // public final Type PUBLISHER = new BasicType("mit.edu", "partStructure", "publisher");
0700: // public final Type RIGHTS = new BasicType("sakaibrary", "partStructure", "rights");
0701: // public final Type SOURCE_TITLE = new BasicType("sakaibrary", "partStructure", "sourceTitle");
0702: // public final Type START_PAGE = new BasicType("sakaibrary", "partStructure", "startPage");
0703: // public final Type SUBJECT = new BasicType("mit.edu", "partStructure", "subject");
0704: // public final Type TYPE = new BasicType("mit.edu", "partStructure", "type");
0705: // public final Type URL = new BasicType("mit.edu", "partStructure", "url");
0706: // public final Type URL_FORMAT = new BasicType("sakaibrary", "partStructure", "urlFormat");
0707: // public final Type URL_LABEL = new BasicType("sakaibrary", "partStructure", "urlLabel");
0708: // public final Type VOLUME = new BasicType("sakaibrary", "partStructure", "volume");
0709: // public final Type YEAR = new BasicType("sakaibrary", "partStructure", "year");
0710:
0711: }
0712:
0713: /**
0714: * @author gbhatnag
0715: *
0716: */
0717: public class BasicSearchDatabaseHierarchy extends
0718: org.xml.sax.helpers.DefaultHandler implements
0719: SearchDatabaseHierarchy {
0720:
0721: public class BasicSearchCategory implements SearchCategory {
0722: private String id;
0723: private String displayName;
0724: private String description;
0725: private boolean defaultStatus;
0726:
0727: // list of sub-categories contained in this category (could be null)
0728: private java.util.List<SearchCategory> subcategoryList;
0729:
0730: // list of database ids contained in this category (could be null)
0731: private java.util.List<String> databaseList;
0732:
0733: // list of database ids that are recommended within this category
0734: // (could be null)
0735: private java.util.List<String> recommendedDatabases;
0736:
0737: // map of databases with alternate metadata within this category
0738: // keyed using database id
0739: private java.util.Map<String, SearchDatabase> altDatabases;
0740:
0741: /**
0742: * BasicSearchCategory constructor creates a BasicSearchCategory
0743: * with the given name and id
0744: *
0745: * @param name display name for this category
0746: * @param id unique identifier for this category
0747: */
0748: protected BasicSearchCategory(String name, String id) {
0749: this .id = id;
0750: this .displayName = name;
0751: this .description = null;
0752: this .defaultStatus = false;
0753: }
0754:
0755: protected void updateDescription(String description) {
0756: this .description = description;
0757: }
0758:
0759: protected void addSubcategory(SearchCategory subcategory) {
0760: if (subcategory != null) {
0761: if (subcategoryList == null) {
0762: subcategoryList = new Vector<SearchCategory>();
0763: }
0764: subcategoryList.add(subcategory);
0765: } else {
0766: m_log
0767: .warn("BasicSearchCategory.addSubCategory() was "
0768: + "passed a null subcategory to add");
0769: }
0770: }
0771:
0772: protected void addDatabase(String databaseId) {
0773: if (databaseId != null) {
0774: if (databaseList == null) {
0775: databaseList = new Vector<String>();
0776: }
0777: databaseList.add(databaseId);
0778: } else {
0779: m_log.warn("BasicSearchCategory.addDatabase() was "
0780: + "passed a null databaseId to add");
0781: }
0782: }
0783:
0784: protected void addRecommendedDatabase(String databaseId) {
0785: if (databaseId != null) {
0786: if (recommendedDatabases == null) {
0787: recommendedDatabases = new Vector<String>();
0788: }
0789: recommendedDatabases.add(databaseId);
0790:
0791: // if this database is not in the overall list of databases,
0792: // add it
0793: if (databaseList == null) {
0794: databaseList = new Vector<String>();
0795: }
0796:
0797: if (!databaseList.contains(databaseId)) {
0798: databaseList.add(databaseId);
0799: }
0800: } else {
0801: m_log
0802: .warn("BasicSearchCategory.addRecommendedDatabase()"
0803: + " was passed a null databaseId to add");
0804: }
0805: }
0806:
0807: protected void addAlternateDatabase(
0808: SearchDatabase altDatabase) {
0809: if (altDatabase != null) {
0810: if (altDatabases == null) {
0811: altDatabases = new Hashtable<String, SearchDatabase>();
0812: }
0813: altDatabases.put(altDatabase.getId(), altDatabase);
0814:
0815: // if this database is not in the overall list of databases,
0816: // add it
0817: if (!databaseList.contains(altDatabase.getId())) {
0818: databaseList.add(altDatabase.getId());
0819: }
0820: } else {
0821: m_log
0822: .warn("BasicSearchCategory.addAlternateDatabase() "
0823: + "was passed a null SearchDatabase to add");
0824: }
0825: }
0826:
0827: protected void setDefault(boolean value) {
0828: this .defaultStatus = value;
0829: }
0830:
0831: protected boolean isDefault() {
0832: return defaultStatus;
0833: }
0834:
0835: /* (non-Javadoc)
0836: * @see org.sakaiproject.citation.api.SearchCategory#hasDatabases()
0837: */
0838: public boolean hasDatabases() {
0839: return (databaseList != null && !databaseList.isEmpty());
0840: }
0841:
0842: /* (non-Javadoc)
0843: * @see org.sakaiproject.citation.api.SearchCategory#getDatabases()
0844: */
0845: public List<SearchDatabase> getDatabases() {
0846: // List to be returned
0847: Vector<SearchDatabase> databases = new Vector<SearchDatabase>();
0848:
0849: for (int i = 0; i < databaseList.size(); i++) {
0850: String databaseId = databaseList.get(i);
0851: SearchDatabase database;
0852:
0853: // check if there is an alternate for this database
0854: if (altDatabases != null
0855: && altDatabases.containsKey(databaseId)) {
0856: database = altDatabases.get(databaseId);
0857: } else {
0858: // get the database from the global map of databases
0859: database = databaseMap.get(databaseId);
0860: }
0861:
0862: // check if the database is a member of authorized groups
0863: for (String groupId : groups) {
0864: if (database.isGroupMember(groupId)) {
0865: // add to the return List
0866: databases.add(database);
0867: break;
0868: }
0869: }
0870: }
0871: return databases;
0872: }
0873:
0874: /* (non-Javadoc)
0875: * @see org.sakaiproject.citation.api.SearchCategory#getDescription()
0876: */
0877: public String getDescription() {
0878: return description;
0879: }
0880:
0881: /* (non-Javadoc)
0882: * @see org.sakaiproject.citation.api.SearchCategory#getDisplayName()
0883: */
0884: public String getDisplayName() {
0885: return displayName;
0886: }
0887:
0888: /* (non-Javadoc)
0889: * @see org.sakaiproject.citation.api.SearchCategory#getId()
0890: */
0891: public String getId() {
0892: return id;
0893: }
0894:
0895: /* (non-Javadoc)
0896: * @see org.sakaiproject.citation.api.SearchCategory#hasSubCategories()
0897: */
0898: public boolean hasSubCategories() {
0899: return (subcategoryList != null && !subcategoryList
0900: .isEmpty());
0901: }
0902:
0903: /* (non-Javadoc)
0904: * @see org.sakaiproject.citation.api.SearchCategory#getSubCategories()
0905: */
0906: public List<SearchCategory> getSubCategories() {
0907: return subcategoryList;
0908: }
0909:
0910: /* (non-Javadoc)
0911: * @see org.sakaiproject.citation.api.SearchCategory#isDatabaseRecommended()
0912: */
0913: public boolean isDatabaseRecommended(String databaseId) {
0914: return recommendedDatabases.contains(databaseId);
0915: }
0916:
0917: } // public class BasicSearchCategory
0918:
0919: public class BasicSearchDatabase implements SearchDatabase {
0920: private String id;
0921: private String displayName;
0922: private String description;
0923:
0924: // groups this database belongs to
0925: private List<String> groups;
0926:
0927: protected BasicSearchDatabase(String name, String id) {
0928: this .displayName = name;
0929: this .id = id;
0930: this .description = null;
0931: }
0932:
0933: protected void updateDescription(String description) {
0934: this .description = description;
0935: }
0936:
0937: protected void addGroup(String groupId) {
0938: if (groupId != null) {
0939: if (groups == null) {
0940: groups = new Vector<String>();
0941: }
0942: groups.add(groupId);
0943: } else {
0944: m_log.warn("BasicSearchDatabase.addGroup() "
0945: + "was passed a null groupId to add");
0946: }
0947: }
0948:
0949: public String getDescription() {
0950: return description;
0951: }
0952:
0953: public String getDisplayName() {
0954: return displayName;
0955: }
0956:
0957: public String getId() {
0958: return id;
0959: }
0960:
0961: public boolean isGroupMember(String groupId) {
0962: return groups.contains(groupId);
0963: }
0964:
0965: } // public class BasicSearchDatabase
0966:
0967: /* org.sakaiproject.citation
0968: * BasicSearchDatabaseHierarchy instance variables
0969: */
0970:
0971: // this user's repository and groups
0972: protected String repositoryPkgName;
0973: protected List<String> groups;
0974:
0975: // root category (contains top level categories)
0976: protected BasicSearchCategory rootCategory;
0977:
0978: // map containing all databases, keyed by database id
0979: protected java.util.Map<String, SearchDatabase> databaseMap;
0980:
0981: // map containing all categories, keyed by category id
0982: protected java.util.Map<String, SearchCategory> categoryMap;
0983:
0984: // default category
0985: protected SearchCategory defaultCategory;
0986:
0987: // configured flag
0988: protected boolean isConfigured;
0989:
0990: // for SAX parsing
0991: protected StringBuffer textBuffer;
0992: protected boolean recommendedDatabaseFlag;
0993: protected int hierarchyDepth;
0994: protected java.util.Stack<BasicSearchCategory> categoryStack;
0995: protected BasicSearchDatabase currentDatabase;
0996: protected String currentDatabaseId;
0997:
0998: protected BasicSearchDatabaseHierarchy() {
0999: /*
1000: * Any basic user authn/authz things we can check to not go
1001: * further than we have to?
1002: */
1003:
1004: // get a ConfigurationService instance
1005: if (m_configService == null) {
1006: m_log
1007: .warn("BasicSearchDatabaseHierarchy() m_configService is "
1008: + "null - components.xml injection did not work... getting instance from cover");
1009: m_configService = org.sakaiproject.citation.cover.ConfigurationService
1010: .getInstance();
1011: }
1012:
1013: try {
1014: /*
1015: * Determine which repository implementation this user should get
1016: * access to
1017: * - ip-based
1018: * - other things?
1019: *
1020: * (currently assuming X-Server)
1021: */
1022: // repositoryPkgName = "org.sakaibrary.osid.repository.xserver";
1023: repositoryPkgName = m_configService
1024: .getSiteConfigOsidPackageName();
1025: if (repositoryPkgName == null) {
1026: // cannot continue
1027: isConfigured = false;
1028: return;
1029: }
1030:
1031: /*
1032: * Now we know which metasearch engine, get the corresponding XML
1033: * for that database
1034: * - XML describes all accessible databases/categories for a given
1035: * metasearch engine
1036: *
1037: * (currently assuming CATEGORIES_XML for all users)
1038: */
1039:
1040: /*
1041: * Determine which groups this user is a member of
1042: * - should get an array of strings with group names/ids
1043: * which should appear in the XML
1044: */
1045: // String[] tempGroups = { "all", "free" };
1046: // groups = tempGroups;
1047: groups = m_configService.getGroupIds();
1048:
1049: /*
1050: * Parse the XML using the group information to build a hierarchy
1051: * of categories and databases this user has access to
1052: */
1053: recommendedDatabaseFlag = false;
1054: hierarchyDepth = 0;
1055: databaseMap = new java.util.Hashtable<String, SearchDatabase>();
1056: categoryMap = new java.util.Hashtable<String, SearchCategory>();
1057: categoryStack = new java.util.Stack<BasicSearchCategory>();
1058:
1059: if (m_configService.isDatabaseHierarchyXmlAvailable()) {
1060: isConfigured = true;
1061: parseXML(m_configService.getDatabaseHierarchyXml());
1062: } else {
1063: isConfigured = false;
1064: }
1065: } catch (org.sakaiproject.citation.util.api.OsidConfigurationException oce) {
1066: m_log
1067: .warn(
1068: "BasicSearchDatabaseHierarchy() ran into problems with the ConfigurationService",
1069: oce);
1070: }
1071: }
1072:
1073: protected void setDefaultCategory(SearchCategory defaultCategory) {
1074: if (defaultCategory != null) {
1075: this .defaultCategory = defaultCategory;
1076: } else {
1077: m_log
1078: .warn("BasicSearchDatabaseHierarchy.setDefaultCategory()"
1079: + " was passed a null SearchCategory to set");
1080: }
1081: }
1082:
1083: protected void addTopLevelCategory(
1084: SearchCategory topLevelCategory) {
1085: if (topLevelCategory != null) {
1086: if (rootCategory == null) {
1087: rootCategory = new BasicSearchCategory(
1088: SearchDatabaseHierarchy.ROOT_CATEGORY_NAME,
1089: SearchDatabaseHierarchy.ROOT_CATEGORY_ID);
1090: }
1091: rootCategory.addSubcategory(topLevelCategory);
1092: } else {
1093: m_log
1094: .warn("BasicSearchDatabaseHierarchy.addTopLevelCategory()"
1095: + " was passed a null SearchCategory to add");
1096: }
1097: }
1098:
1099: protected void parseXML(String categoriesFileName) {
1100: // Use the default (non-validating) parser
1101: SAXParserFactory factory = SAXParserFactory.newInstance();
1102:
1103: try {
1104: // Parse the input
1105: SAXParser saxParser = factory.newSAXParser();
1106: saxParser.parse(categoriesFileName, this );
1107: } catch (SAXParseException spe) {
1108: // Use the contained exception, if any
1109: Exception x = spe;
1110:
1111: if (spe.getException() != null) {
1112: x = spe.getException();
1113: }
1114:
1115: // Error generated by the parser
1116: m_log.warn("parseXML() parsing exception: "
1117: + spe.getMessage() + " - xml line "
1118: + spe.getLineNumber() + ", uri "
1119: + spe.getSystemId(), x);
1120:
1121: // unset configuration flag
1122: isConfigured = false;
1123: } catch (SAXException sxe) {
1124: // Error generated by this application
1125: // (or a parser-initialization error)
1126: Exception x = sxe;
1127:
1128: if (sxe.getException() != null) {
1129: x = sxe.getException();
1130: }
1131:
1132: m_log.warn("parseXML() SAX exception: "
1133: + sxe.getMessage(), x);
1134: // unset configuration flag
1135: isConfigured = false;
1136: } catch (ParserConfigurationException pce) {
1137: // Parser with specified options can't be built
1138: m_log.warn("parseXML() SAX parser cannot be built "
1139: + "with specified options");
1140:
1141: // unset configuration flag
1142: isConfigured = false;
1143: } catch (IOException ioe) {
1144: // I/O error
1145: m_log.warn("parseXML() IO exception", ioe);
1146:
1147: // unset configuration flag
1148: isConfigured = false;
1149: } catch (Throwable t) {
1150: m_log.warn("parseXML() exception", t);
1151:
1152: // unset configuration flag
1153: isConfigured = false;
1154: }
1155: }
1156:
1157: public void startElement(String namespaceURI, String sName,
1158: String qName, Attributes attrs) throws SAXException {
1159: String eName = sName; // element name
1160:
1161: if (eName.equals("")) {
1162: eName = qName; // not namespaceAware
1163: }
1164:
1165: if (eName.equals("category")) {
1166: // create a new category with the given attribute info
1167: BasicSearchCategory newCategory = new BasicSearchCategory(
1168: attrs.getValue("name"), attrs.getValue("id"));
1169:
1170: // check if this is the default category
1171: if (attrs.getValue("default") != null) {
1172: newCategory.setDefault(true);
1173: }
1174:
1175: // add new category to the stack
1176: categoryStack.push(newCategory);
1177: } else if (eName.equals("database")) {
1178: // create a new database with the given attribute info
1179: currentDatabase = new BasicSearchDatabase(attrs
1180: .getValue("name"), attrs.getValue("id"));
1181: } else if (eName.equals("category_database")) {
1182: // determine whether this is a "recommended" database
1183: if (attrs.getValue("recommended") != null) {
1184: recommendedDatabaseFlag = true;
1185: }
1186: }
1187: }
1188:
1189: public void endElement(String namespaceURI, String sName,
1190: String qName) throws SAXException {
1191: String eName = sName; // element name
1192:
1193: if (eName.equals("")) {
1194: eName = qName; // not namespaceAware
1195: }
1196:
1197: parseData(eName);
1198: }
1199:
1200: public void characters(char[] buf, int offset, int len)
1201: throws SAXException {
1202: String s = new String(buf, offset, len);
1203:
1204: if (textBuffer == null) {
1205: textBuffer = new StringBuffer(s);
1206: } else {
1207: textBuffer.append(s);
1208: }
1209: }
1210:
1211: protected String getAttribute(Attributes attrs, String attrName) {
1212: if (attrs != null) {
1213: for (int i = 0; i < attrs.getLength(); i++) {
1214: String name = attrs.getLocalName(i);
1215:
1216: if (name.equals("")) {
1217: name = attrs.getQName(i);
1218: }
1219:
1220: if (name.equals(attrName)) {
1221: return attrs.getValue(i);
1222: }
1223: }
1224: }
1225: return null;
1226: }
1227:
1228: protected void parseData(String endElement) {
1229: String text = null;
1230: if (textBuffer != null) {
1231: text = textBuffer.toString().trim();
1232: }
1233:
1234: /*
1235: * category elements
1236: */
1237: if (endElement.equals("category_description")) {
1238: BasicSearchCategory temp = categoryStack.pop();
1239: temp.updateDescription(text);
1240: categoryStack.push(temp);
1241: } else if (endElement.equals("category")) {
1242: // a category has just ended
1243:
1244: // attach it to its proper hierarchy container
1245: if (!categoryStack.peek().isDefault()) {
1246: // add category to the category map
1247: categoryMap.put(categoryStack.peek().getId(),
1248: categoryStack.peek());
1249:
1250: if (categoryStack.size() == 1) {
1251: // at the top level
1252: addTopLevelCategory(categoryStack.pop());
1253: } else {
1254: // not at the top level
1255: // determine hierarchy depth
1256: if (hierarchyDepth < categoryStack.size()) {
1257: hierarchyDepth = categoryStack.size();
1258: }
1259:
1260: // add current subcategory to parent category
1261: BasicSearchCategory subcategory = categoryStack
1262: .pop();
1263: BasicSearchCategory parentCategory = categoryStack
1264: .pop();
1265: parentCategory.addSubcategory(subcategory);
1266: categoryStack.push(parentCategory);
1267: }
1268: } else {
1269: // TODO this assumes the default category is outside of the
1270: // hierarchy (it is not attached to any parent container)
1271: defaultCategory = categoryStack.pop();
1272: }
1273: }
1274:
1275: /*
1276: * category_database elements
1277: */
1278: else if (endElement.equals("id")) {
1279: currentDatabaseId = text;
1280:
1281: // is this database recommended?
1282: if (recommendedDatabaseFlag) {
1283: recommendedDatabaseFlag = false;
1284: BasicSearchCategory temp = categoryStack.pop();
1285: temp.addRecommendedDatabase(text);
1286: categoryStack.push(temp);
1287: } else {
1288: BasicSearchCategory temp = categoryStack.pop();
1289: temp.addDatabase(text);
1290: categoryStack.push(temp);
1291: }
1292: } else if (endElement.equals("alt_name")) {
1293: currentDatabase = new BasicSearchDatabase(text,
1294: currentDatabaseId);
1295: } else if (endElement.equals("alt_description")) {
1296: currentDatabase.updateDescription(text);
1297: } else if (endElement.equals("category_database")) {
1298: if (currentDatabase != null) {
1299: BasicSearchCategory temp = categoryStack.pop();
1300: temp.addAlternateDatabase(currentDatabase);
1301: categoryStack.push(temp);
1302: currentDatabase = null;
1303: }
1304: }
1305:
1306: /*
1307: * database elements
1308: */
1309: else if (endElement.equals("database_description")) {
1310: currentDatabase.updateDescription(text);
1311: } else if (endElement.equals("database_group")) {
1312: currentDatabase.addGroup(text);
1313: } else if (endElement.equals("database")) {
1314: // a database has just ended - add to databaseMap
1315: databaseMap.put(currentDatabase.getId(),
1316: currentDatabase);
1317: currentDatabase = null;
1318: }
1319:
1320: textBuffer = null;
1321: }
1322:
1323: public SearchCategory getCategory(String categoryId) {
1324: if (categoryId.equals(defaultCategory.getId())) {
1325: return defaultCategory;
1326: } else {
1327: return categoryMap.get(categoryId);
1328: }
1329: }
1330:
1331: /* (non-Javadoc)
1332: * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#getNumLevels()
1333: */
1334: public int getNumLevels() {
1335: return hierarchyDepth;
1336: }
1337:
1338: /* (non-Javadoc)
1339: * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#getNumMaxSearchableDb()
1340: */
1341: public int getNumMaxSearchableDb() {
1342: return 8; // TODO get this from XML
1343: }
1344:
1345: /* (non-Javadoc)
1346: * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#getTopLevelCategories()
1347: */
1348: public List<SearchCategory> getCategoryListing() {
1349: // return list
1350: List<SearchCategory> categoryListing = new java.util.ArrayList<SearchCategory>();
1351:
1352: // add root category to return list
1353: categoryListing.add(rootCategory);
1354:
1355: // iterate through all categories (starting at root)
1356: // and add them to the return list
1357: for (int i = 0; i <= categoryMap.size(); i++) {
1358: SearchCategory category = categoryListing.get(i);
1359:
1360: if (category.hasSubCategories()) {
1361: for (SearchCategory cat : category
1362: .getSubCategories()) {
1363: categoryListing.add(cat);
1364: }
1365: }
1366: }
1367:
1368: return categoryListing;
1369: }
1370:
1371: /* (non-Javadoc)
1372: * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#getRepository()
1373: */
1374: public Repository getRepository() {
1375: // get a RepositoryManager
1376: RepositoryManager repositoryManager = null;
1377: try {
1378: repositoryManager = (RepositoryManager) SakaiOsidLoader
1379: .getManager(
1380: "org.osid.repository.RepositoryManager",
1381: repositoryPkgName, new OsidContext(),
1382: null);
1383:
1384: if (repositoryManager == null) {
1385: m_log.warn("getRepository() failed getting "
1386: + "RepositoryManager from SakaiOsidLoader");
1387: }
1388:
1389: // get repositories of type sakaibrary/repository/metasearch
1390: RepositoryIterator rit = repositoryManager
1391: .getRepositoriesByType(repositoryType);
1392: if (rit == null) {
1393: m_log
1394: .warn("getRepository() failed getting "
1395: + "RepositoryIterator of type sakaibrary/repository/"
1396: + "metasearch from RepositoryManager");
1397: }
1398:
1399: // only one repository should be in the iterator
1400: Repository repository = rit.nextRepository();
1401: if (repository == null) {
1402: m_log
1403: .warn("getRepository() failed getting repository "
1404: + "from RepositoryIterator");
1405: }
1406:
1407: return repository;
1408: } catch (OsidException oe) {
1409: m_log.warn("getRepository threw OsidException: ", oe);
1410: }
1411:
1412: return null;
1413: }
1414:
1415: /* (non-Javadoc)
1416: * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#getDefaultCategory()
1417: */
1418: public SearchCategory getDefaultCategory() {
1419: return defaultCategory;
1420: }
1421:
1422: /* (non-Javadoc)
1423: * @see org.sakaiproject.citation.api.SearchDatabaseHierarchy#isSearchableDatabase(java.lang.String)
1424: */
1425: public boolean isSearchableDatabase(String databaseId) {
1426: return databaseMap.containsKey(databaseId);
1427: }
1428:
1429: public boolean isConfigured() {
1430: return isConfigured;
1431: }
1432:
1433: } // public class BasicSearchDatabaseHierarchy
1434:
1435: /** Our logger. */
1436: private static Log m_log = LogFactory
1437: .getLog(BaseSearchManager.class);
1438:
1439: // our ConfigurationService (gets set in BaseSearchDatabaseHierarchy)
1440:
1441: // google scholar constants
1442: public static final String SAKAI_SESSION = "sakai.session";
1443: public static final String SAKAI_KEY = "sakai.key";
1444: public static final String SAKAI_HOST = "sakai.host";
1445:
1446: public static final String SERVLET_NAME = "savecite";
1447:
1448: // Our types (defined in setupTypes())
1449: protected static BasicType categoryAssetType;
1450: protected static BasicType databaseAssetType;
1451: protected static BasicType searchType;
1452: protected static BasicType repositoryType;
1453:
1454: // String array for databases being searched and database hierarchy
1455: protected String[] m_databaseIds;
1456: protected SearchDatabaseHierarchy hierarchy;
1457:
1458: private static Random m_generator;
1459:
1460: /*
1461: * necessary services and managers (provided by components.xml)
1462: */
1463: protected SessionManager m_sessionManager = null;
1464: protected ConfigurationService m_configService = null;
1465: /** Dependency: ServerConfigurationService. */
1466: protected ServerConfigurationService serverConfigurationService = null;
1467:
1468: public void setSessionManager(SessionManager sessionManager) {
1469: m_sessionManager = sessionManager;
1470: }
1471:
1472: public void setConfigurationService(
1473: ConfigurationService configService) {
1474: m_configService = configService;
1475: }
1476:
1477: public void destroy() {
1478: m_log.info("BaseSearchManager.destroy()");
1479: }
1480:
1481: /* (non-Javadoc)
1482: * @see org.sakaiproject.citation.impl.SearchManager#doNextPage(org.sakaiproject.citation.api.ActiveSearch)
1483: */
1484: public ActiveSearch doNextPage(ActiveSearch search)
1485: throws SearchException {
1486: Repository repository = ((BasicSearch) search).getRepository();
1487: AssetIterator assetIterator = ((BasicSearch) search)
1488: .getAssetIterator();
1489: int last = search.getLastRecordIndex();
1490:
1491: CitationCollection citations = search.getSearchResults();
1492: if (citations == null) {
1493: citations = CitationService.getTemporaryCollection();
1494: ((BasicSearch) search).setSearchResults(citations);
1495: }
1496:
1497: Set duplicateCheck = ((BasicSearch) search).getDuplicateCheck();
1498: boolean done = false;
1499: try {
1500: // poll until you get pageSize results to return
1501: while (assetIterator.hasNextAsset() && !done) {
1502: try {
1503: Asset asset = assetIterator.nextAsset();
1504:
1505: Citation citation = CitationService
1506: .getTemporaryCitation(asset);
1507:
1508: String openUrlParams = citation
1509: .getOpenurlParameters();
1510:
1511: if (duplicateCheck.contains(openUrlParams)) {
1512: continue;
1513: } else {
1514: ((BasicSearch) search).m_pageOrder.add(citation
1515: .getId());
1516: citations.add(citation);
1517: duplicateCheck.add(openUrlParams);
1518: }
1519:
1520: // check if we've got enough to return
1521: done = (citations.size() >= last);
1522: } catch (RepositoryException re) {
1523: if (re.getMessage().equals(SESSION_TIMED_OUT)
1524: || re.getMessage().equals(METASEARCH_ERROR)
1525: || re
1526: .getMessage()
1527: .equals(
1528: SharedException.NO_MORE_ITERATOR_ELEMENTS)
1529: || re.getMessage().equals(
1530: OsidException.OPERATION_FAILED)) {
1531: // search is over, all assets that have been retrieved have been
1532: // optionally check searchStatus Properties for further details or information to present in UI
1533: search.setLastPage(true);
1534:
1535: m_log
1536: .warn("doNextPage -- RepositoryException nextAsset(): "
1537: + re.getMessage());
1538:
1539: String message = getSearchStatusMessage(repository);
1540: if (message == null) {
1541: throw new SearchException(re.getMessage());
1542: }
1543:
1544: throw new SearchException(message);
1545: } else if (re.getMessage()
1546: .equals(ASSET_NOT_FETCHED)) {
1547: // need to wait some time and then try again
1548: try {
1549: Thread.sleep(2500); // sleep 2.5 seconds
1550: } catch (InterruptedException ie) {
1551: search.setLastPage(true);
1552:
1553: m_log
1554: .warn(
1555: "doNextPage -- InterruptedException nextAsset(): ",
1556: ie);
1557:
1558: String message = getSearchStatusMessage(repository);
1559:
1560: throw new SearchException(message);
1561: }
1562: }
1563: }
1564: }
1565: } catch (RepositoryException re) {
1566: if (re.getMessage().equals(SESSION_TIMED_OUT)
1567: || re.getMessage().equals(METASEARCH_ERROR)) {
1568: search.setLastPage(true);
1569:
1570: // search is over, all assets that have been retrieved have been
1571: // optionally check searchStatus Properties for further details or information to present in UI
1572: m_log
1573: .warn("doNextPage -- RepositoryException hasNextAsset(): "
1574: + re.getMessage());
1575:
1576: String message = getSearchStatusMessage(repository);
1577:
1578: throw new SearchException(message);
1579: }
1580: }
1581:
1582: // get search status properties
1583: Type statusType = getPropertyType(repository);
1584: org.osid.shared.Properties statusProperties = null;
1585:
1586: try {
1587: statusProperties = repository
1588: .getPropertiesByType(statusType);
1589: } catch (RepositoryException re) {
1590: search.setLastPage(true);
1591:
1592: String message = getSearchStatusMessage(repository);
1593:
1594: throw new SearchException(message);
1595: }
1596:
1597: Integer numRecordsFound = null;
1598: Integer numRecordsFetched = null;
1599: Integer numRecordsMerged = null;
1600: try {
1601: numRecordsFound = (Integer) statusProperties
1602: .getProperty("numRecordsFound");
1603: numRecordsFetched = (Integer) statusProperties
1604: .getProperty("numRecordsFetched");
1605: numRecordsMerged = (Integer) statusProperties
1606: .getProperty("numRecordsMerged");
1607: } catch (SharedException se) {
1608: search.setLastPage(true);
1609:
1610: String message = getSearchStatusMessage(repository);
1611:
1612: throw new SearchException(message);
1613: }
1614:
1615: search.setNumRecordsFound(numRecordsFound);
1616: search.setNumRecordsFetched(numRecordsFetched);
1617: search.setNumRecordsMerged(numRecordsMerged);
1618: search.setNewSearch(false);
1619: search.setFirstPage(false);
1620: if (done) {
1621: search.setLastPage(false);
1622: } else {
1623: search.setLastPage(true);
1624: }
1625:
1626: return search;
1627: }
1628:
1629: /* (non-Javadoc)
1630: * @see org.sakaiproject.citation.impl.SearchManager#doPrevPage(org.sakaiproject.citation.api.ActiveSearch)
1631: */
1632: public ActiveSearch doPrevPage(ActiveSearch search)
1633: throws SearchException {
1634: return search;
1635: }
1636:
1637: /* (non-Javadoc)
1638: * @see org.sakaiproject.citation.impl.SearchManager#doSearch(org.sakaiproject.citation.api.ActiveSearch)
1639: */
1640: public ActiveSearch doSearch(ActiveSearch search)
1641: throws SearchException {
1642: // search parameters
1643: Repository repository = hierarchy.getRepository();
1644: Integer pageSize = search.getPageSize();
1645: Integer startRecord = search.getStartRecord();
1646: String sortBy = search.getSortBy();
1647: String guid = search.getSearchId();
1648:
1649: // CQL search query setup
1650: String cqlQuery = null;
1651: CQLSearchQuery cqlSearch = new org.sakaiproject.citation.util.impl.CQLSearchQuery();
1652:
1653: // determine whether this is an advanced or basic search
1654: if (search.getSearchType().equalsIgnoreCase(
1655: ActiveSearch.BASIC_SEARCH_TYPE)) {
1656: // get search criteria in CQL
1657: cqlQuery = cqlSearch.getCQLSearchQueryString(search
1658: .getBasicQuery());
1659: } else {
1660: // get search criteria in CQL
1661: cqlQuery = cqlSearch.getCQLSearchQueryString(search
1662: .getAdvancedQuery());
1663: }
1664: m_log.debug("CQL query: " + cqlQuery);
1665:
1666: // initiate the search
1667: try {
1668: if (cqlQuery == null) {
1669: // something went horrible
1670: throw new SearchException("ERROR: could not properly "
1671: + "convert search criteria to CQL.");
1672: }
1673:
1674: // set up search properties
1675: java.util.Properties properties = new java.util.Properties();
1676: properties.put("guid", guid);
1677: properties.put("baseUrl", m_configService
1678: .getSiteConfigMetasearchBaseUrl());
1679: properties.put("username", m_configService
1680: .getSiteConfigMetasearchUsername());
1681: properties.put("password", m_configService
1682: .getSiteConfigMetasearchPassword());
1683: properties.put("sortBy", sortBy);
1684: properties.put("pageSize", pageSize);
1685: properties.put("startRecord", startRecord);
1686:
1687: // put selected databases
1688: List<String> databaseIds = new java.util.ArrayList<String>();
1689: for (String databaseId : m_databaseIds) {
1690: databaseIds.add(databaseId);
1691: }
1692: properties.put("databaseIds", databaseIds);
1693:
1694: // create OSID Properties
1695: org.osid.shared.Properties searchProperties = new BasicSearchProperties(
1696: properties);
1697:
1698: // get "sakaibrary / search / asynchMetasearch" search type
1699: Type searchType = getSearchType(repository);
1700:
1701: // call getAssetsBySearch
1702: AssetIterator assetIterator = repository.getAssetsBySearch(
1703: cqlQuery, searchType, searchProperties);
1704:
1705: CitationCollection citations = search.getSearchResults();
1706: if (citations == null) {
1707: citations = CitationService.getTemporaryCollection();
1708: ((BasicSearch) search).setSearchResults(citations);
1709: }
1710:
1711: Set duplicateCheck = ((BasicSearch) search)
1712: .getDuplicateCheck();
1713: int assetsRetrieved = 0;
1714: boolean done = false;
1715: try {
1716: // poll until you get pageSize results to return
1717: while (!done && assetIterator.hasNextAsset()) {
1718: try {
1719: Asset asset = assetIterator.nextAsset();
1720:
1721: Citation citation = CitationService
1722: .getTemporaryCitation(asset);
1723:
1724: String openUrlParams = citation
1725: .getOpenurlParameters();
1726: if (duplicateCheck.contains(openUrlParams)) {
1727: continue;
1728: } else {
1729: ((BasicSearch) search).m_pageOrder
1730: .add(citation.getId());
1731: citations.add(citation);
1732: duplicateCheck.add(openUrlParams);
1733: }
1734:
1735: // check if we've got enough to return
1736: if (++assetsRetrieved >= pageSize.intValue()) {
1737: done = true;
1738: }
1739: } catch (RepositoryException re) {
1740: if (re.getMessage().equals(SESSION_TIMED_OUT)
1741: || re.getMessage().equals(
1742: METASEARCH_ERROR)
1743: || re
1744: .getMessage()
1745: .equals(
1746: SharedException.NO_MORE_ITERATOR_ELEMENTS)
1747: || re.getMessage().equals(
1748: OsidException.OPERATION_FAILED)) {
1749: search.setLastPage(true);
1750:
1751: // search is over, all assets that have been retrieved have been
1752: // optionally check searchStatus Properties for further details or information to present in UI
1753:
1754: String message = getSearchStatusMessage(repository);
1755:
1756: m_log
1757: .warn("doSearch -- RepositoryException nextAsset(): "
1758: + re.getMessage());
1759: throw new SearchException(message);
1760: } else if (re.getMessage().equals(
1761: ASSET_NOT_FETCHED)) {
1762: // need to wait some time and then try again
1763: try {
1764: Thread.sleep(2500); // sleep 2.5 seconds
1765: } catch (InterruptedException ie) {
1766: // search canceled
1767: throw new SearchException(
1768: "search canceled");
1769: }
1770: }
1771: }
1772: }
1773: } catch (RepositoryException re) {
1774: if (re.getMessage().equals(SESSION_TIMED_OUT)
1775: || re.getMessage().equals(METASEARCH_ERROR)) {
1776: search.setLastPage(true);
1777:
1778: // search is over, all assets that have been retrieved have been
1779: // optionally check searchStatus Properties for further details or information to present in UI
1780: String message = getSearchStatusMessage(repository);
1781:
1782: m_log
1783: .warn("doSearch -- RepositoryException hasNextAsset(): "
1784: + re.getMessage());
1785: throw new SearchException(message);
1786: }
1787: }
1788:
1789: // get search status properties
1790: Type statusType = getPropertyType(repository);
1791: org.osid.shared.Properties statusProperties = repository
1792: .getPropertiesByType(statusType);
1793:
1794: Integer numRecordsFound = null;
1795: Integer numRecordsFetched = null;
1796: Integer numRecordsMerged = null;
1797: try {
1798: numRecordsFetched = (Integer) statusProperties
1799: .getProperty("numRecordsFetched");
1800: numRecordsFound = (Integer) statusProperties
1801: .getProperty("numRecordsFound");
1802: numRecordsMerged = (Integer) statusProperties
1803: .getProperty("numRecordsMerged");
1804: } catch (SharedException se) {
1805: search.setLastPage(true);
1806:
1807: String message = getSearchStatusMessage(repository);
1808:
1809: throw new SearchException(message);
1810: }
1811:
1812: /*
1813: * forward results handling
1814: */
1815:
1816: search.setNumRecordsFound(numRecordsFound);
1817: search.setNumRecordsFetched(numRecordsFetched);
1818: search.setNumRecordsMerged(numRecordsMerged);
1819: search.setNewSearch(false);
1820: search.setFirstPage(false);
1821: search.setLastPage(!done);
1822: ((BasicSearch) search).setRepository(repository);
1823: ((BasicSearch) search).setAssetIterator(assetIterator);
1824:
1825: return search;
1826: } catch (RepositoryException re) {
1827: m_log.warn("doSearch -- RepositoryException: "
1828: + re.getMessage());
1829:
1830: throw new SearchException(re.getMessage());
1831: }
1832: }
1833:
1834: protected String newSearchId() {
1835: /******* A unique ID per-session ********/
1836:
1837: return m_sessionManager.getCurrentSession().getId();
1838:
1839: /******* Unique ID per-search (original)
1840:
1841: String sessionId = m_sessionManager.getCurrentSession().getId();
1842: long number = m_generator.nextLong();
1843: String hexString = Long.toHexString(number);
1844: m_log.debug("getSearchId: " + sessionId + hexString);
1845: return sessionId + hexString;
1846:
1847: *************************************************************************/
1848: }
1849:
1850: protected Type getPropertyType(Repository repository)
1851: throws SearchException {
1852: TypeIterator propertyTypes = null;
1853: Type propertyType = null;
1854:
1855: try {
1856: propertyTypes = repository.getPropertyTypes();
1857:
1858: while (propertyTypes.hasNextType()) {
1859: Type tempType = propertyTypes.nextType();
1860: if (tempType.getAuthority().equals("sakaibrary")
1861: && tempType.getDomain().equals("properties")
1862: && tempType.getKeyword().equals(
1863: "metasearchStatus")) {
1864: propertyType = tempType;
1865: break;
1866: }
1867: }
1868: } catch (OsidException oe) {
1869: m_log.warn("getPropertyType -- OsidException: "
1870: + oe.getMessage());
1871: throw new SearchException("ERROR in getting search types: "
1872: + oe.getMessage());
1873: }
1874:
1875: return propertyType;
1876: }
1877:
1878: protected Type getCategoryType(Repository repository)
1879: throws SearchException {
1880: TypeIterator assetTypes = null;
1881:
1882: try {
1883: assetTypes = repository.getAssetTypes();
1884:
1885: while (assetTypes.hasNextType()) {
1886: Type tempType = assetTypes.nextType();
1887: if (tempType.isEqual(categoryAssetType)) {
1888: return tempType;
1889: }
1890: }
1891: } catch (OsidException oe) {
1892: m_log.warn("getCategoryType -- OsidException: ", oe);
1893: throw new SearchException(
1894: "ERROR in getting category type: "
1895: + oe.getMessage());
1896: }
1897:
1898: return null;
1899: }
1900:
1901: protected Type getSearchType(Repository repository)
1902: throws SearchException {
1903: TypeIterator searchTypes = null;
1904:
1905: try {
1906: searchTypes = repository.getSearchTypes();
1907:
1908: while (searchTypes.hasNextType()) {
1909: Type tempType = searchTypes.nextType();
1910: if (tempType.isEqual(searchType)) {
1911: return tempType;
1912: }
1913: }
1914: } catch (OsidException oe) {
1915: m_log.warn("getSearchType -- OsidException: ", oe);
1916: throw new SearchException("ERROR in getting search types: "
1917: + oe.getMessage());
1918: }
1919:
1920: return null;
1921: }
1922:
1923: protected String getSearchStatusMessage(Repository repository)
1924: throws SearchException {
1925: BasicType statusType = new BasicType("sakaibrary",
1926: "properties", "metasearchStatus");
1927:
1928: String message = null;
1929: try {
1930: org.osid.shared.Properties statusProperties = repository
1931: .getPropertiesByType(statusType);
1932:
1933: message = (String) statusProperties
1934: .getProperty("statusMessage");
1935: } catch (RepositoryException e) {
1936: // let the message remain null but log this exception
1937: m_log
1938: .warn("getSearchStatusMessage RepositoryException getting properties "
1939: + e.getMessage());
1940: } catch (SharedException e) {
1941: // let the message remain null but log this exception
1942: m_log
1943: .warn("getSearchStatusMessage SharedException getting property "
1944: + e.getMessage());
1945: }
1946:
1947: return message;
1948: }
1949:
1950: public void init() {
1951: m_log.info("BaseSearchManager.init()");
1952: long seed = TimeService.newTime().getTime();
1953: m_generator = new Random(seed);
1954: setupTypes();
1955: }
1956:
1957: protected void setupTypes() {
1958: categoryAssetType = new BasicType("sakaibrary", "asset",
1959: "category");
1960: databaseAssetType = new BasicType("sakaibrary", "asset",
1961: "database");
1962: searchType = new BasicType("sakaibrary", "search",
1963: "asynchMetasearch");
1964: repositoryType = new BasicType("sakaibrary", "repository",
1965: "metasearch");
1966: }
1967:
1968: /* (non-Javadoc)
1969: * @see org.sakaiproject.citation.api.SearchManager#listRepositories()
1970: */
1971: public SearchDatabaseHierarchy getSearchHierarchy()
1972: throws SearchException {
1973: this .hierarchy = new BasicSearchDatabaseHierarchy();
1974: return this .hierarchy;
1975: }
1976:
1977: /* (non-Javadoc)
1978: * @see org.sakaiproject.citation.api.SearchManager#newSearch(java.lang.String)
1979: */
1980: public ActiveSearch newSearch() {
1981: return new BasicSearch();
1982: }
1983:
1984: /* (non-Javadoc)
1985: * @see org.sakaiproject.citation.api.SearchManager#newSearch(java.lang.String)
1986: */
1987: public ActiveSearch newSearch(CitationCollection savedResults) {
1988: return new BasicSearch(savedResults);
1989: }
1990:
1991: protected boolean paramIsEmpty(String param) {
1992: return param.trim().equals("");
1993: }
1994:
1995: /**
1996: *
1997: */
1998: public String getGoogleScholarUrl(String resourceId) {
1999: String serverUrl = serverConfigurationService.getServerUrl();
2000: SessionManager sessionManager = (SessionManager) ComponentManager
2001: .get("org.sakaiproject.tool.api.SessionManager");
2002: String sessionId = sessionManager.getCurrentSession().getId();
2003:
2004: try {
2005: return (m_configService.getSiteConfigGoogleBaseUrl()
2006: + "?linkurl_base="
2007: + java.net.URLEncoder.encode(serverUrl
2008: + Entity.SEPARATOR + SERVLET_NAME
2009: + Entity.SEPARATOR + resourceId + "?"
2010: + SAKAI_SESSION + "=" + sessionId + "&",
2011: "UTF-8") + "&linkurl_id=" + java.net.URLEncoder
2012: .encode("sakai."
2013: + m_configService
2014: .getSiteConfigSakaiServerKey(),
2015: "UTF-8"));
2016: } catch (Exception e) {
2017: m_log.warn("getGoogleScholarUrl encoding failed", e);
2018: return null;
2019: }
2020: }
2021:
2022: /**
2023: * @return the serverConfigurationService
2024: */
2025: public ServerConfigurationService getServerConfigurationService() {
2026: return serverConfigurationService;
2027: }
2028:
2029: /**
2030: * @param serverConfigurationService the serverConfigurationService to set
2031: */
2032: public void setServerConfigurationService(
2033: ServerConfigurationService serverConfigurationService) {
2034: this .serverConfigurationService = serverConfigurationService;
2035: }
2036:
2037: public void setDatabaseIds(String[] databaseIds) {
2038: // TODO Auto-generated method stub
2039: this.m_databaseIds = databaseIds;
2040: }
2041:
2042: }
|