001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 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 * Contributors: 017 * Georges Racinet 018 * Florent Guillaume 019 */ 020 021package org.nuxeo.ecm.core.api.impl; 022 023import java.util.Collection; 024import java.util.Collections; 025import java.util.HashSet; 026import java.util.List; 027import java.util.Set; 028 029import org.nuxeo.ecm.core.api.DocumentModel; 030import org.nuxeo.ecm.core.api.Filter; 031 032/** 033 * A filter based on facets. 034 * 035 * @author Georges Racinet 036 * @author Florent Guillaume 037 */ 038public class FacetFilter implements Filter { 039 040 private static final long serialVersionUID = 1L; 041 042 public static final FacetFilter ALLOW = new FacetFilter((List<String>) null, (List<String>) null); 043 044 /** Set of required facets. Never {@code null}. */ 045 public final Set<String> required; 046 047 /** Set of excluded facets. Never {@code null}. */ 048 public final Set<String> excluded; 049 050 public final Boolean shortcut; 051 052 /** 053 * Generic constructor. 054 * 055 * @param required list of facets the models must have to pass the filter 056 * @param excluded list of facets the models must not have to pass the filter 057 */ 058 public FacetFilter(List<String> required, List<String> excluded) { 059 if (required == null) { 060 this.required = Collections.emptySet(); 061 } else { 062 this.required = new HashSet<String>(required); 063 } 064 if (excluded == null) { 065 this.excluded = Collections.emptySet(); 066 } else { 067 this.excluded = new HashSet<String>(excluded); 068 } 069 shortcut = findShortcut(); 070 } 071 072 /** 073 * Simpler constructor to filter on a single facet. 074 * 075 * @param facet the facet to filter on 076 * @param isRequired if true, accepted models must have the facet; if false, accepted models must not have the facet 077 */ 078 public FacetFilter(String facet, boolean isRequired) { 079 if (isRequired) { 080 required = Collections.singleton(facet); 081 excluded = Collections.emptySet(); 082 } else { 083 required = Collections.emptySet(); 084 excluded = Collections.singleton(facet); 085 } 086 shortcut = null; 087 } 088 089 /** 090 * Constructor that ANDs two filters. 091 * 092 * @param filter1 the first filter 093 * @param filter2 the second filter 094 */ 095 public FacetFilter(FacetFilter filter1, FacetFilter filter2) { 096 if (filter1.required.isEmpty() && filter2.required.isEmpty()) { 097 required = Collections.emptySet(); 098 } else { 099 required = new HashSet<String>(filter1.required); 100 required.addAll(filter2.required); 101 } 102 if (filter1.excluded.isEmpty() && filter2.excluded.isEmpty()) { 103 excluded = Collections.emptySet(); 104 } else { 105 excluded = new HashSet<String>(filter1.excluded); 106 excluded.addAll(filter2.excluded); 107 } 108 shortcut = findShortcut(); 109 } 110 111 protected Boolean findShortcut() { 112 if (required.isEmpty() && excluded.isEmpty()) { 113 // no condition, always matches 114 return Boolean.TRUE; 115 } 116 Collection<String> intersection = new HashSet<String>(required); 117 intersection.retainAll(excluded); 118 if (!intersection.isEmpty()) { 119 // non-empty intersection, filter can never match 120 return Boolean.FALSE; 121 } 122 return null; 123 } 124 125 @Override 126 public boolean accept(DocumentModel docModel) { 127 if (shortcut != null) { 128 return shortcut; 129 } 130 for (String exc : excluded) { 131 if (docModel.hasFacet(exc)) { 132 return false; 133 } 134 } 135 for (String req : required) { 136 if (!docModel.hasFacet(req)) { 137 return false; 138 } 139 } 140 return true; 141 } 142 143}