/*
* Constellation - An open source and standard compliant SDI
* http://www.constellation-sdi.org
*
* Copyright 2014 Geomatys.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.constellation.metadata.index.generic;
// J2SE dependencies
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.constellation.metadata.index.AbstractCSWIndexer;
import org.constellation.metadata.io.MetadataIoException;
import org.constellation.metadata.io.MetadataReader;
import org.constellation.metadata.io.MetadataType;
import org.constellation.metadata.utils.Utils;
import org.constellation.util.NodeUtilities;
import org.geotoolkit.lucene.IndexingException;
import org.w3c.dom.Node;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
// Apache Lucene dependencies
// constellation dependencies
// geotoolkit dependencies
// GeoAPI dependencies
/**
* A Lucene Index Handler for a generic Database.
* @author Guilhem Legal
*/
public class NodeIndexer extends AbstractCSWIndexer<Node> {
/**
* The Reader of this lucene index (generic DB mode).
*/
protected final MetadataReader reader;
/**
* Creates a new Lucene Index into the specified directory with the specified generic database reader.
*
* @param reader A node reader to request the metadata dataSource.
* @param configurationDirectory The directory where the index can write indexation file.
* @param indexID The identifier, if there is one, of the index/service.
* @param additionalQueryable A map of additional queryable element.
* @param create {@code true} if the index need to be created.
*
* @throws org.geotoolkit.lucene.IndexingException If an erro roccurs during the index creation.
*/
public NodeIndexer(final MetadataReader reader, final File configurationDirectory, final String indexID,
final Map<String, List<String>> additionalQueryable, final boolean create) throws IndexingException {
super(indexID, configurationDirectory, additionalQueryable);
this.reader = reader;
if (create && needCreation()) {
createIndex();
}
}
/**
* Creates a new Lucene Index into the specified directory with the specified list of object to index.
*
* @param toIndex A list of Object
* @param additionalQueryable A Map of additionable queryable to add to the index (name - List of Xpath)
* @param configDirectory A directory where the index can write indexation file.
* @param indexID The identifier, if there is one, of the index.
* @param analyzer The lucene analyzer used.
* @param logLevel A log level for info information.
* @param create {@code true} if the index need to be created.
*
* @throws org.geotoolkit.lucene.IndexingException If an erro roccurs during the index creation.
*/
public NodeIndexer(final List<Node> toIndex, final Map<String, List<String>> additionalQueryable, final File configDirectory,
final String indexID, final Analyzer analyzer, final Level logLevel, final boolean create) throws IndexingException {
super(indexID, configDirectory, analyzer, additionalQueryable);
this.logLevel = logLevel;
this.reader = null;
if (create && needCreation()) {
createIndex(toIndex);
}
}
/**
* Creates a new Lucene Index into the specified directory with the specified list of object to index.
*
* @param toIndex A list of Object
* @param additionalQueryable A Map of additionable queryable to add to the index (name - List of Xpath)
* @param configDirectory A directory where the index can write indexation file.
* @param indexID The identifier, if there is one, of the index.
* @param create {@code true} if the index need to be created.
*
* @throws org.geotoolkit.lucene.IndexingException If an erro roccurs during the index creation.
*/
public NodeIndexer(final List<Node> toIndex, final Map<String, List<String>> additionalQueryable, final File configDirectory,
final String indexID, final boolean create) throws IndexingException {
super(indexID, configDirectory, additionalQueryable);
this.reader = null;
if (create && needCreation()) {
createIndex(toIndex);
}
}
/**
* {@inheritDoc}
*/
@Override
protected List<String> getAllIdentifiers() throws IndexingException {
try {
return reader.getAllIdentifiers();
} catch (MetadataIoException ex) {
throw new IndexingException("Metadata_IOException while reading all identifiers", ex);
}
}
/**
* {@inheritDoc}
*/
@Override
protected Iterator<String> getIdentifierIterator() throws IndexingException {
try {
return reader.getIdentifierIterator();
} catch (MetadataIoException ex) {
throw new IndexingException("Metadata_IOException while reading identifier iterator", ex);
}
}
/**
* {@inheritDoc}
*/
@Override
protected Node getEntry(final String identifier) throws IndexingException {
try {
return (Node) reader.getMetadata(identifier, MetadataType.NATIVE);
} catch (MetadataIoException ex) {
throw new IndexingException("Metadata_IOException while reading entry for:" + identifier, ex);
}
}
/**
* {@inheritDoc}
*/
@Override
protected void indexSpecialField(final Node metadata, final Document doc) throws IndexingException {
final String identifier = getIdentifier(metadata);
if ("unknow".equals(identifier)) {
throw new IndexingException("unexpected metadata type.");
}
doc.add(new Field("id", identifier, ID_TYPE));
}
/**
* {@inheritDoc}
*/
@Override
protected String getType(final Node metadata) {
return metadata.getLocalName();
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isISO19139(final Node meta) {
return "MD_Metadata".equals(meta.getLocalName()) ||
"MI_Metadata".equals(meta.getLocalName());
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isDublinCore(final Node meta) {
return "Record".equals(meta.getLocalName());
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isEbrim25(final Node meta) {
// TODO list rootElement
return "RegistryObject".equals(meta.getLocalName());
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isEbrim30(final Node meta) {
// TODO list rootElement
return "Identifiable".equals(meta.getLocalName());
}
/**
* {@inheritDoc}
*/
@Override
protected boolean isFeatureCatalogue(Node meta) {
return "FC_FeatureCatalogue".equals(meta.getLocalName());
}
/**
* {@inheritDoc}
*/
@Override
protected void indexQueryableSet(final Document doc, final Node metadata, final Map<String, List<String>> queryableSet, final StringBuilder anyText) throws IndexingException {
for (final String term : queryableSet.keySet()) {
final TermValue tm = new TermValue(term, NodeUtilities.extractValues(metadata, queryableSet.get(term)));
final NodeIndexer.TermValue values = formatStringValue(tm);
indexFields(values.value, values.term, anyText, doc);
}
}
/**
* Format the value part in case of a "date" term.
* @param values
* @return
*/
private TermValue formatStringValue(final TermValue values) {
if ("date".equals(values.term)) {
final List<Object> newValues = new ArrayList<>();
for (Object value : values.value) {
if (value instanceof String) {
String stringValue = (String) value;
if (stringValue.endsWith("z") || stringValue.endsWith("Z")) {
stringValue = stringValue.substring(0, stringValue.length() - 1);
}
if (stringValue != null) {
stringValue = stringValue.replace("-", "");
//add time if there is no
if (stringValue.length() == 8) {
stringValue = stringValue + "000000";
}
value = stringValue;
}
}
newValues.add(value);
}
values.value = newValues;
}
return values;
}
/**
* {@inheritDoc}
*/
@Override
protected String getIdentifier(final Node metadata) {
return Utils.findIdentifier(metadata);
}
/**
* {@inheritDoc}
*/
@Override
@Deprecated
protected String getValues(final Node metadata, final List<String> paths) {
final List<Object> values = NodeUtilities.extractValues(metadata, paths);
final StringBuilder sb = new StringBuilder();
for (Object value : values) {
sb.append(value).append(',');
}
if (!sb.toString().isEmpty()) {
// we remove the last ','
sb.delete(sb.length() - 1, sb.length());
}
return sb.toString();
}
@Override
protected Iterator<Node> getEntryIterator() throws IndexingException {
try {
return (Iterator<Node>) reader.getEntryIterator();
} catch (MetadataIoException ex) {
throw new IndexingException("Error while getting entry iterator", ex);
}
}
@Override
protected boolean useEntryIterator() {
return reader.useEntryIterator();
}
@Override
public void destroy() {
LOGGER.log(logLevel, "shutting down Node indexer");
super.destroy();
}
private static class TermValue {
public String term;
public List<Object> value;
public TermValue(String term, List<Object> value) {
this.term = term;
this.value = value;
}
}
}