/* * Copyright 2012 NGDATA nv * * 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.lilyproject.indexer.model.indexerconf; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import com.google.common.collect.Sets; import org.lilyproject.repository.api.FieldType; import org.lilyproject.repository.api.QName; import org.lilyproject.repository.api.Record; import org.lilyproject.repository.api.RepositoryException; import org.lilyproject.repository.api.SchemaId; import org.lilyproject.repository.api.TypeManager; import org.lilyproject.util.repo.RecordUtil; /** * Matches records based on certain conditions, i.e. a predicate on record objects. */ public class RecordMatcher { public enum FieldComparator {EQUAL, NOT_EQUAL} private WildcardPattern recordTypeNamespace; private WildcardPattern recordTypeName; private QName instanceOfType; private FieldType fieldType; private FieldComparator fieldComparator; private Object fieldValue; private Set<String> tableNames; private TypeManager typeManager; /** * The variant properties the record should have. Evaluation rules: a key named * "*" (star symbol) is a wildcard meaning that any variant dimensions not specified * are accepted. Otherwise the variant dimension count should match exactly. The other * keys in the map are required variant dimensions. If their value is not null, the * values should match. */ private final Map<String, String> variantPropsPattern; public RecordMatcher(WildcardPattern recordTypeNamespace, WildcardPattern recordTypeName, QName instanceOfType, FieldType fieldType, FieldComparator fieldComparator, Object fieldValue, Map<String, String> variantPropsPattern, List<String> tableNames, TypeManager typeManager) { this.recordTypeNamespace = recordTypeNamespace; this.recordTypeName = recordTypeName; this.instanceOfType = instanceOfType; this.fieldType = fieldType; this.fieldComparator = fieldComparator != null ? fieldComparator : FieldComparator.EQUAL; this.fieldValue = fieldValue; this.variantPropsPattern = variantPropsPattern; this.tableNames = tableNames == null ? Sets.<String>newHashSet() : Sets.newHashSet(tableNames); this.typeManager = typeManager; } public boolean matches(String table, Record record) { QName recordTypeName = record.getRecordTypeName(); Map<String, String> varProps = record.getId().getVariantProperties(); // About "recordTypeName == null": normally record type name cannot be null, but it can // be in the case of IndexAwareMQFeeder if (this.recordTypeNamespace != null && (recordTypeName == null || !this.recordTypeNamespace.lightMatch(recordTypeName.getNamespace()))) { return false; } if (!tableNames.isEmpty() && !tableNames.contains(table)) { return false; } if (this.recordTypeName != null && (recordTypeName == null || !this.recordTypeName.lightMatch(recordTypeName.getName()))) { return false; } try { if (this.instanceOfType != null && (recordTypeName == null || !RecordUtil.instanceOf(record, instanceOfType, typeManager))) { return false; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } catch (RepositoryException e) { throw new RuntimeException(e); } if (variantPropsPattern != null) { if (variantPropsPattern.size() != varProps.size() && !variantPropsPattern.containsKey("*")) { return false; } for (Map.Entry<String, String> entry : variantPropsPattern.entrySet()) { if (entry.getKey().equals("*")) { continue; } String dimVal = varProps.get(entry.getKey()); if (dimVal == null) { // this record does not have a required variant property return false; } if (entry.getValue() != null && !entry.getValue().equals(dimVal)) { // the variant property does not have the required value return false; } } } if (fieldType != null) { if (fieldComparator == FieldComparator.EQUAL) { return record.hasField(fieldType.getName()) && fieldValue.equals(record.getField(fieldType.getName())); } else if (fieldComparator == FieldComparator.NOT_EQUAL) { // not-equal should evaluate to true if field value is null return !record.hasField(fieldType.getName()) || !fieldValue.equals(record.getField(fieldType.getName())); } else { throw new RuntimeException("Unexpected comparison operator: " + fieldComparator); } } return true; } public Set<QName> getFieldDependencies() { return fieldType != null ? Collections.singleton(fieldType.getName()) : Collections.<QName>emptySet(); } public Set<SchemaId> getFieldDependencyIds() { return fieldType != null ? Collections.singleton(fieldType.getId()) : Collections.<SchemaId>emptySet(); } public boolean dependsOnRecordType() { return recordTypeName != null || recordTypeNamespace != null; } }