001: /*
002: * Copyright 2004-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.compass.core.lucene;
018:
019: import java.io.Reader;
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.Collections;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.Set;
028:
029: import org.apache.lucene.document.Document;
030: import org.apache.lucene.document.Field;
031: import org.compass.core.Property;
032: import org.compass.core.Resource;
033: import org.compass.core.converter.ResourcePropertyConverter;
034: import org.compass.core.engine.SearchEngineException;
035: import org.compass.core.lucene.engine.LuceneSearchEngineFactory;
036: import org.compass.core.lucene.util.LuceneUtils;
037: import org.compass.core.mapping.ResourceMapping;
038: import org.compass.core.mapping.ResourcePropertyMapping;
039: import org.compass.core.spi.AliasedObject;
040: import org.compass.core.spi.InternalResource;
041: import org.compass.core.spi.ResourceKey;
042: import org.compass.core.util.StringUtils;
043:
044: /**
045: * @author kimchy
046: */
047: public class LuceneResource implements AliasedObject, InternalResource,
048: Map<String, Property[]> {
049:
050: private static final long serialVersionUID = 3904681565727306034L;
051:
052: private Document document;
053:
054: private ArrayList<Property> properties = new ArrayList<Property>();
055:
056: private String aliasProperty;
057:
058: private int docNum;
059:
060: private transient LuceneSearchEngineFactory searchEngineFactory;
061:
062: private transient ResourceMapping resourceMapping;
063:
064: private transient ResourceKey resourceKey;
065:
066: public LuceneResource(String alias,
067: LuceneSearchEngineFactory searchEngineFactory) {
068: this (alias, new Document(), -1, searchEngineFactory);
069: }
070:
071: public LuceneResource(Document document, int docNum,
072: LuceneSearchEngineFactory searchEngineFactory) {
073: this (null, document, docNum, searchEngineFactory);
074: }
075:
076: public LuceneResource(String alias, Document document, int docNum,
077: LuceneSearchEngineFactory searchEngineFactory) {
078: this .document = document;
079: this .searchEngineFactory = searchEngineFactory;
080: this .aliasProperty = searchEngineFactory.getAliasProperty();
081: this .docNum = docNum;
082: if (alias != null) {
083: removeProperties(aliasProperty);
084: Field aliasField = new Field(aliasProperty, alias,
085: Field.Store.YES, Field.Index.UN_TOKENIZED);
086: aliasField.setOmitNorms(true);
087: document.add(aliasField);
088: }
089:
090: verifyResourceMapping();
091:
092: List fields = document.getFields();
093: for (Iterator fieldsIt = fields.iterator(); fieldsIt.hasNext();) {
094: Field field = (Field) fieldsIt.next();
095: LuceneProperty lProperty = new LuceneProperty(field);
096: lProperty.setPropertyMapping(resourceMapping
097: .getResourcePropertyMapping(field.name()));
098: properties.add(lProperty);
099: }
100: }
101:
102: public void copy(Resource resource) {
103: LuceneResource luceneResource = (LuceneResource) resource;
104: this .document = luceneResource.document;
105: this .docNum = luceneResource.docNum;
106: this .properties = luceneResource.properties;
107: this .aliasProperty = luceneResource.aliasProperty;
108: this .searchEngineFactory = luceneResource.searchEngineFactory;
109: this .resourceMapping = luceneResource.resourceMapping;
110: }
111:
112: public Document getDocument() {
113: return this .document;
114: }
115:
116: public ResourceKey resourceKey() {
117: if (resourceKey == null) {
118: resourceKey = new ResourceKey(resourceMapping, this );
119: }
120: return resourceKey;
121: }
122:
123: public String getSubIndex() {
124: return resourceKey().getSubIndex();
125: }
126:
127: public String getValue(String name) {
128: return document.get(name);
129: }
130:
131: public Object getObject(String name) {
132: Property prop = getProperty(name);
133: if (prop == null) {
134: return null;
135: }
136: return prop.getObjectValue();
137: }
138:
139: public String[] getValues(String name) {
140: return document.getValues(name);
141: }
142:
143: public String getAlias() {
144: return getValue(aliasProperty);
145: }
146:
147: public String getUID() {
148: return resourceKey().buildUID();
149: }
150:
151: public String getId() {
152: String[] ids = getIds();
153: return ids[0];
154: }
155:
156: public String[] getIds() {
157: Property[] idProperties = getIdProperties();
158: String[] ids = new String[idProperties.length];
159: for (int i = 0; i < idProperties.length; i++) {
160: if (idProperties[i] != null) {
161: ids[i] = idProperties[i].getStringValue();
162: }
163: }
164: return ids;
165: }
166:
167: public Property getIdProperty() {
168: Property[] idProperties = getIdProperties();
169: return idProperties[0];
170: }
171:
172: public Property[] getIdProperties() {
173: return resourceKey().getIds();
174: }
175:
176: public Resource addProperty(String name, Object value)
177: throws SearchEngineException {
178: String alias = getAlias();
179:
180: ResourcePropertyMapping propertyMapping = resourceMapping
181: .getResourcePropertyMapping(name);
182: if (propertyMapping == null) {
183: throw new SearchEngineException(
184: "No resource property mapping is defined for alias ["
185: + alias + "] and resource property ["
186: + name + "]");
187: }
188: ResourcePropertyConverter converter = (ResourcePropertyConverter) propertyMapping
189: .getConverter();
190: if (converter == null) {
191: converter = (ResourcePropertyConverter) searchEngineFactory
192: .getMapping().getConverterLookup().lookupConverter(
193: value.getClass());
194: }
195: String strValue = converter.toString(value, propertyMapping);
196:
197: Property property = searchEngineFactory.getResourceFactory()
198: .createProperty(strValue, propertyMapping);
199: property.setBoost(propertyMapping.getBoost());
200: return addProperty(property);
201: }
202:
203: public Resource addProperty(String name, Reader value)
204: throws SearchEngineException {
205: String alias = getAlias();
206:
207: ResourcePropertyMapping propertyMapping = resourceMapping
208: .getResourcePropertyMapping(name);
209: if (propertyMapping == null) {
210: throw new SearchEngineException(
211: "No resource property mapping is defined for alias ["
212: + alias + "] and resource property ["
213: + name + "]");
214: }
215:
216: Field.TermVector fieldTermVector = LuceneUtils
217: .getFieldTermVector(propertyMapping.getTermVector());
218: Field field = new Field(name, value, fieldTermVector);
219: LuceneProperty property = new LuceneProperty(field);
220: property.setBoost(propertyMapping.getBoost());
221: property.setPropertyMapping(propertyMapping);
222: return addProperty(property);
223: }
224:
225: public Resource addProperty(Property property) {
226: LuceneProperty lProperty = (LuceneProperty) property;
227: lProperty.setPropertyMapping(resourceMapping
228: .getResourcePropertyMapping(property.getName()));
229: properties.add(property);
230: document.add(lProperty.getField());
231: return this ;
232: }
233:
234: public Resource setProperty(String name, Object value)
235: throws SearchEngineException {
236: removeProperties(name);
237: return addProperty(name, value);
238: }
239:
240: public Resource setProperty(String name, Reader value)
241: throws SearchEngineException {
242: removeProperties(name);
243: return addProperty(name, value);
244: }
245:
246: public Resource setProperty(Property property) {
247: removeProperties(property.getName());
248: return addProperty(property);
249: }
250:
251: public Resource removeProperty(String name) {
252: document.removeField(name);
253: Iterator<Property> it = properties.iterator();
254: while (it.hasNext()) {
255: Property property = it.next();
256: if (property.getName().equals(name)) {
257: it.remove();
258: return this ;
259: }
260: }
261: return this ;
262: }
263:
264: public Resource removeProperties(String name) {
265: document.removeFields(name);
266: Iterator<Property> it = properties.iterator();
267: while (it.hasNext()) {
268: Property property = it.next();
269: if (property.getName().equals(name)) {
270: it.remove();
271: }
272: }
273: return this ;
274: }
275:
276: public Property getProperty(String name) {
277: for (Property property : properties) {
278: if (property.getName().equals(name)) {
279: return property;
280: }
281: }
282: return null;
283: }
284:
285: public Property[] getProperties(String name) {
286: List<Property> result = new ArrayList<Property>();
287: for (int i = 0; i < properties.size(); i++) {
288: Property property = properties.get(i);
289: if (property.getName().equals(name)) {
290: result.add(property);
291: }
292: }
293:
294: if (result.size() == 0)
295: return new Property[0];
296:
297: return result.toArray(new Property[result.size()]);
298: }
299:
300: public Property[] getProperties() {
301: return properties.toArray(new Property[properties.size()]);
302: }
303:
304: public float getBoost() {
305: return document.getBoost();
306: }
307:
308: public Resource setBoost(float boost) {
309: document.setBoost(boost);
310: return this ;
311: }
312:
313: public void setDocNum(int docNum) {
314: this .docNum = docNum;
315: }
316:
317: /**
318: * Returns the Lucene document number. If not set (can be in case the
319: * resource is newly created), than returns -1.
320: */
321: public int getDocNum() {
322: return this .docNum;
323: }
324:
325: public void addUID() {
326: Property uidProp = searchEngineFactory.getResourceFactory()
327: .createProperty(resourceMapping.getUIDPath(),
328: resourceKey().buildUID(), Property.Store.YES,
329: Property.Index.UN_TOKENIZED);
330: uidProp.setOmitNorms(true);
331: addProperty(uidProp);
332: }
333:
334: private void verifyResourceMapping() throws SearchEngineException {
335: String alias = getAlias();
336: if (resourceMapping == null) {
337: if (alias == null) {
338: throw new SearchEngineException(
339: "Can't add a resource property based on resource mapping without an alias associated with the resource first");
340: }
341: if (!searchEngineFactory.getMapping()
342: .hasRootMappingByAlias(alias)) {
343: throw new SearchEngineException(
344: "No mapping is defined for alias [" + alias
345: + "]");
346: }
347: resourceMapping = searchEngineFactory.getMapping()
348: .getRootMappingByAlias(alias);
349: }
350: }
351:
352: public String toString() {
353: return "{"
354: + getAlias()
355: + "} "
356: + StringUtils
357: .arrayToCommaDelimitedString(getProperties());
358: }
359:
360: // methods from the Map interface
361: // ------------------------------
362:
363: public void clear() {
364: throw new UnsupportedOperationException(
365: "Map operations are supported for read operations only");
366: }
367:
368: public boolean containsValue(Object value) {
369: throw new UnsupportedOperationException(
370: "Map operations are supported for read operations only");
371: }
372:
373: public void putAll(Map<? extends String, ? extends Property[]> t) {
374: for (Iterator it = t.entrySet().iterator(); it.hasNext();) {
375: Map.Entry entry = (Entry) it.next();
376: put((String) entry.getKey(), (Property[]) entry.getValue());
377: }
378: }
379:
380: public Property[] remove(Object key) {
381: removeProperties(key.toString());
382: // TODO should return the old value
383: return null;
384: }
385:
386: public Property[] put(String key, Property[] value) {
387: removeProperties(key);
388: for (Property aValue : value) {
389: addProperty(aValue);
390: }
391: // TODO should return the old value
392: return null;
393: }
394:
395: public Set<Map.Entry<String, Property[]>> entrySet() {
396: Set<String> keySey = keySet();
397: Set<Entry<String, Property[]>> entrySet = new HashSet<Entry<String, Property[]>>();
398: for (Iterator it = keySey.iterator(); it.hasNext();) {
399: final String name = it.next().toString();
400: final Property[] props = getProperties(name);
401: entrySet.add(new Map.Entry<String, Property[]>() {
402: public String getKey() {
403: return name;
404: }
405:
406: public Property[] getValue() {
407: return props;
408: }
409:
410: public Property[] setValue(Property[] value) {
411: put(name, value);
412: // TODO should return the old value
413: return null;
414: }
415: });
416: }
417: return Collections.unmodifiableSet(entrySet);
418: }
419:
420: public Set<String> keySet() {
421: Set<String> keySet = new HashSet<String>();
422: for (Property property : properties) {
423: keySet.add((property).getName());
424: }
425: return Collections.unmodifiableSet(keySet);
426: }
427:
428: public boolean containsKey(Object key) {
429: return getProperty(key.toString()) != null;
430: }
431:
432: public int size() {
433: return this .properties.size();
434: }
435:
436: public boolean isEmpty() {
437: return this .properties.isEmpty();
438: }
439:
440: public Collection<Property[]> values() {
441: Set<String> keySet = keySet();
442: List<Property[]> values = new ArrayList<Property[]>();
443: for (String name : keySet) {
444: values.add(getProperties(name));
445: }
446: return Collections.unmodifiableList(values);
447: }
448:
449: public Property[] get(Object key) {
450: return getProperties(key.toString());
451: }
452: }
|