content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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.apache.ambari.server.api.query;
  
  
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import org.apache.ambari.server.controller.internal.ResourceImpl;
  import org.apache.ambari.server.controller.predicate.AlwaysPredicate;
  import org.apache.ambari.server.controller.predicate.ArrayPredicate;
  import org.apache.ambari.server.controller.predicate.CategoryPredicate;
  import org.apache.ambari.server.controller.predicate.ComparisonPredicate;
  import org.apache.ambari.server.controller.predicate.PredicateVisitor;
  import org.apache.ambari.server.controller.predicate.UnaryPredicate;
  import org.apache.ambari.server.controller.spi.Predicate;
  import org.apache.ambari.server.controller.spi.Resource;
  import org.apache.ambari.server.controller.utilities.PredicateHelper;
  
  /**
   * A predicate visitor used to generate a new predicate so that the resources
   * referenced by the predicate will be extended to include the joined properties
   * of their sub-resources.
   */
  public class ExtendedResourcePredicateVisitor implements PredicateVisitor {
    /**
     * The last visited predicate.
     */
    private Predicate lastVisited = null;
  
    /**
     * The joined resource map.
     */
    private final Map<Resource, Set<Map<String, Object>>> joinedResources;
  
  
    // ----- Constructors ------------------------------------------------------
  
    /**
     * Constructor.
     *
     * @param extendedProperties the map of sets of extended properties
     */
    public ExtendedResourcePredicateVisitor(Map<Resource,
        Set<Map<String, Object>>> extendedProperties) {
      this.joinedResources = extendedProperties;
    }
  
    // ----- PredicateVisitor --------------------------------------------------
  
    @Override
    public void acceptComparisonPredicate(ComparisonPredicate predicate) {
      lastVisited = new ExtendedResourcePredicate(predicate, joinedResources);
    }
  
    @Override
    public void acceptArrayPredicate(ArrayPredicate arrayPredicate) {
      List<Predicate> predicateList = new LinkedList<>();
  
      Predicate[] predicates = arrayPredicate.getPredicates();
      if (predicates.length > 0) {
        for (Predicate predicate : predicates) {
          PredicateHelper.visit(predicate, this);
          predicateList.add(lastVisited);
        }
      }
      lastVisited = arrayPredicate.create(predicateList.toArray(new Predicate[predicateList.size()]));
    }
  
    @Override
    public void acceptUnaryPredicate(UnaryPredicate predicate) {
      lastVisited = new ExtendedResourcePredicate(predicate, joinedResources);
    }
  
    @Override
    public void acceptAlwaysPredicate(AlwaysPredicate predicate) {
      lastVisited = predicate;
    }
  
    @Override
    public void acceptCategoryPredicate(CategoryPredicate predicate) {
      lastVisited = new ExtendedResourcePredicate(predicate, joinedResources);
    }
  
  
    // ----- utility methods -------------------------------------------------
  
    /**
     * Get the extended predicate.
     *
     * @return the predicate
     */
    public Predicate getExtendedPredicate() {
      return lastVisited;
    }
  
  
    // ----- inner classes -----------------------------------------------------
  
    // ----- ExtendedResourcePredicate -----------------------------------------
  
    /**
     * Predicate implementation used to replace any existing predicate and
     * extend the resource being evaluated with an extended set of property
     * values.
     */
    private static class ExtendedResourcePredicate implements Predicate {
  
      /**
       * The predicate being extended.
       */
      private final Predicate predicate;
  
      /**
       * The map of extended property sets keyed by resource.
       */
      private final Map<Resource, Set<Map<String, Object>>> joinedResources;
  
  
      // ----- Constructors ----------------------------------------------------
  
      /**
       * Constructor
       *
       * @param predicate        the predicate being extended
       * @param joinedResources  the map of extended sets of property values
       */
      public ExtendedResourcePredicate(Predicate predicate,
                                       Map<Resource, Set<Map<String, Object>>> joinedResources) {
        this.predicate       = predicate;
        this.joinedResources = joinedResources;
      }
  
      // ----- Predicate -------------------------------------------------------
  
      @Override
      public boolean evaluate(Resource resource) {
  
        Set<Map<String, Object>> extendedPropertySet = joinedResources.get(resource);
  
        if (extendedPropertySet == null) {
          return predicate.evaluate(resource);
        }
  
        for (Map<String, Object> extendedProperties : extendedPropertySet) {
          Resource extendedResource = new ExtendedResourceImpl(resource, extendedProperties);
  
          if (predicate.evaluate(extendedResource)) {
            return true;
          }
        }
        return false;
      }
    }
  
    // ----- ExtendedResourceImpl ----------------------------------------------
  
    /**
     * A resource that extends a given resource by copying it and adding additional
     * properties.
     */
    private static class ExtendedResourceImpl extends ResourceImpl {
  
      // ----- Constructors ----------------------------------------------------
  
      /**
       * Constructor
       *
       * @param resource            the resource to copy
       * @param extendedProperties  the map of extended properties
       */
      public ExtendedResourceImpl(Resource resource, Map<String, Object> extendedProperties) {
        super(resource);
        initProperties(extendedProperties);
      }
  
      // ----- utility methods -------------------------------------------------
  
      /**
       *  Initialize this resource by setting the extended properties.
       */
      private void initProperties(Map<String, Object> extendedProperties) {
        for (Map.Entry<String, Object> entry : extendedProperties.entrySet()) {
          setProperty(entry.getKey(), entry.getValue());
        }
      }
    }
  }
  