001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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 */
016package org.kuali.rice.kim.impl.common.attribute;
017
018import com.google.common.collect.Iterables;
019import org.kuali.rice.core.api.criteria.AndPredicate;
020import org.kuali.rice.core.api.criteria.CompositePredicate;
021import org.kuali.rice.core.api.criteria.CriteriaValue;
022import org.kuali.rice.core.api.criteria.MultiValuedPredicate;
023import org.kuali.rice.core.api.criteria.OrPredicate;
024import org.kuali.rice.core.api.criteria.Predicate;
025import org.kuali.rice.core.api.criteria.PropertyPathPredicate;
026import org.kuali.rice.core.api.criteria.QueryByCriteria;
027import org.kuali.rice.core.api.criteria.SingleValuedPredicate;
028import org.kuali.rice.core.api.criteria.Transform;
029
030import java.util.ArrayList;
031import java.util.HashSet;
032import java.util.List;
033import java.util.Set;
034import java.util.regex.Matcher;
035import java.util.regex.Pattern;
036
037import static org.kuali.rice.core.api.criteria.PredicateFactory.*;
038
039/**
040 * this is an internal class used by kim service implementations that have generic lookup methods for classes
041 * with "attributes".
042 */
043public final class AttributeTransform implements Transform<QueryByCriteria, QueryByCriteria> {
044
045    private static final Transform<QueryByCriteria, QueryByCriteria> INSTANCE = new AttributeTransform();
046    private static final String ATTRIBUTE_DETAILS_ATTRIBUTE_VALUE = "attributeDetails.attributeValue";
047    private static final String ATTRIBUTE_DETAILS_ATTRIBUTE_NAME = "attributeDetails.kimAttribute.attributeName";
048    private static final String ATTRIBUTES_REGEX = "^attributes\\[\\w*\\]$";
049    private static final Pattern ATTRIBUTES_PATTERN = Pattern.compile(ATTRIBUTES_REGEX);
050
051    private AttributeTransform() {
052
053    }
054
055    @Override
056    public QueryByCriteria apply(final QueryByCriteria input) {
057        QueryByCriteria.Builder builder = QueryByCriteria.Builder.create(input);
058        builder.setPredicates(applyPredicate(input.getPredicate()));
059        return builder.build();
060    }
061
062    private Predicate applyPredicate(final Predicate input) {
063        if (input instanceof PropertyPathPredicate) {
064            String pp = ((PropertyPathPredicate) input).getPropertyPath();
065            if (isAttributesPredicate(pp)) {
066                final String attributeName = pp.substring(pp.indexOf('[') + 1, pp.indexOf(']'));
067
068                final Predicate attrValue;
069                if (input instanceof SingleValuedPredicate) {
070                    final CriteriaValue<?> value = ((SingleValuedPredicate) input).getValue();
071                    attrValue = dynConstruct(input.getClass().getSimpleName(), ATTRIBUTE_DETAILS_ATTRIBUTE_VALUE, value.getValue());
072                } else if (input instanceof MultiValuedPredicate) {
073                    final Set<? extends CriteriaValue<?>> values = ((MultiValuedPredicate) input).getValues();
074                    List<Object> l = new ArrayList<Object>();
075                    for (CriteriaValue<?> v : values) {
076                        l.add(v.getValue());
077                    }
078
079                    attrValue = dynConstruct(input.getClass().getSimpleName(), ATTRIBUTE_DETAILS_ATTRIBUTE_VALUE, l.toArray());
080                } else {
081                    attrValue = dynConstruct(input.getClass().getSimpleName(), ATTRIBUTE_DETAILS_ATTRIBUTE_VALUE);
082                }
083                return and(equal(ATTRIBUTE_DETAILS_ATTRIBUTE_NAME, attributeName), attrValue);
084            }
085        } else if (input instanceof CompositePredicate) {
086            return applyCompositePredicate((CompositePredicate) input);
087        }
088
089        return input;
090    }
091
092    private Predicate applyCompositePredicate(final CompositePredicate input) {
093        Set<Predicate> appliedPredicates = new HashSet<Predicate>();
094
095        for (Predicate predicate : input.getPredicates()) {
096            appliedPredicates.add(applyPredicate(predicate));
097        }
098
099        Predicate[] appliedPredicatesArray = Iterables.toArray(appliedPredicates, Predicate.class);
100
101        if (input instanceof AndPredicate) {
102            return and(appliedPredicatesArray);
103        } else if (input instanceof OrPredicate) {
104            return or(appliedPredicatesArray);
105        }
106
107        return input;
108    }
109
110    private boolean isAttributesPredicate(String pp) {
111        Matcher matcher = ATTRIBUTES_PATTERN.matcher(pp);
112        return matcher.matches();
113    }
114
115    public static Transform<QueryByCriteria, QueryByCriteria> getInstance() {
116        return INSTANCE;
117    }
118}