DefinitionJoiner.java

  1. /*
  2.  * Copyright © 2017, Salesforce.com, Inc
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions are met:
  7.  *     * Redistributions of source code must retain the above copyright
  8.  *       notice, this list of conditions and the following disclaimer.
  9.  *     * Redistributions in binary form must reproduce the above copyright
  10.  *       notice, this list of conditions and the following disclaimer in the
  11.  *       documentation and/or other materials provided with the distribution.
  12.  *     * Neither the name of the <organization> nor the
  13.  *       names of its contributors may be used to endorse or promote products
  14.  *       derived from this software without specific prior written permission.
  15.  *
  16.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  17.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19.  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  20.  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21.  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23.  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26.  */
  27. package com.salesforce.apt.graph.processing;

  28. import java.util.ArrayList;
  29. import java.util.Arrays;
  30. import java.util.Collection;
  31. import java.util.List;
  32. import java.util.Map;
  33. import java.util.function.Consumer;
  34. import java.util.function.Function;
  35. import java.util.stream.Collectors;

  36. import com.salesforce.apt.graph.model.DefinitionModel;
  37. import com.salesforce.apt.graph.model.errors.ErrorModel;
  38. import com.salesforce.apt.graph.model.errors.ErrorType;
  39. import com.salesforce.apt.graph.model.storage.DefinitionModelStore;

  40. public class DefinitionJoiner {
  41.  
  42.   /**
  43.    * Mutates DefinitionModels, links them to their dependencies.
  44.    *
  45.    * @param definitions definitions to join.
  46.    * @param store a means to lookup and store definitions that have been, and have just completed being processed.
  47.    * @param el error listeners, pass error models to it to report them to users.
  48.    */
  49.   public void joinDefinitions(Collection<DefinitionModel> definitions,
  50.       DefinitionModelStore store, Consumer<ErrorModel> el) {
  51.    
  52.     //Map of identity to models for passed in Definitions
  53.     Map<String, DefinitionModel> idToDefinition = definitions.stream()
  54.         .collect(Collectors.toMap(dm -> dm.getIdentity(), Function.identity()));
  55.    
  56.     //Function that merges the available models pre-computed on the system with the models found during processing.
  57.     Function<String, List<DefinitionModel>> availableModels = s -> {
  58.       DefinitionModel definied = idToDefinition.get(s);
  59.       if (definied != null) {
  60.         return Arrays.asList(definied); //it was asked to be processed in this build.
  61.       }
  62.       if (store == null || store.lookup(s) == null) {
  63.         return null;
  64.       }
  65.       return store.lookup(s).stream().filter(item -> item != null).collect(Collectors.toList());
  66.     };
  67.    
  68.     for (DefinitionModel model : definitions) {
  69.       errorIfModelBeingProcessedExistsMoreThanOnce(model, availableModels, el);
  70.       errorModelIfMissingOrDupDependencies(model, availableModels, el);
  71.     }    
  72.   }

  73.   /**
  74.    * Looks for depends of the model in available models in the availableModels function (data source)
  75.    * Register errors if available models are too many, too few, or if the array returned is null
  76.    * we assume a read error occurred.
  77.    *
  78.    * @param model who's dependencies we'll look up
  79.    * @param availableModels a function returning all models with the same id as a list
  80.    * @param el where we register errors.
  81.    */
  82.   private void errorModelIfMissingOrDupDependencies(DefinitionModel model,
  83.       Function<String, List<DefinitionModel>> availableModels, Consumer<ErrorModel> el) {
  84.     List<String> missingDependencies = new ArrayList<>();
  85.     for (String dependencyIdentity : model.getDependencyNames()) {
  86.       List<DefinitionModel> options = availableModels.apply(dependencyIdentity);
  87.       if (options == null) {
  88.         el.accept(new ErrorModel(ErrorType.COULD_NOT_READ,
  89.             Arrays.asList(new DefinitionModel(dependencyIdentity)),
  90.             Arrays.asList(model)));
  91.       } else {
  92.         switch (options.size()) {
  93.           case 0:
  94.             missingDependencies.add(dependencyIdentity);
  95.             break;
  96.           case 1:
  97.             if (options.get(0).isRootNode()) {
  98.               el.accept(new ErrorModel(ErrorType.ROOT_NODE_IMPORTED,
  99.                   Arrays.asList(options.get(0)),
  100.                   Arrays.asList(model)));
  101.             }
  102.             model.addDependency(options.get(0));
  103.             break;
  104.           default:
  105.             el.accept(new ErrorModel(ErrorType.DUPLICATED_MATCHING_DEPENDENCIES,
  106.                 availableModels.apply(dependencyIdentity),
  107.                 Arrays.asList(model)));
  108.             break;
  109.         }
  110.       }
  111.     }
  112.     if (missingDependencies.size() > 0) {
  113.       el.accept(new ErrorModel(ErrorType.MISSING_NECESSARY_ANNOTATIONS,
  114.             missingDependencies.stream()
  115.               .map(s -> new DefinitionModel(s)).collect(Collectors.toList()),
  116.             Arrays.asList(model)));
  117.     }
  118.   }

  119.   /**
  120.    * Checks whether the model has a conflicting source of data, if so, errors out,
  121.    * if the array returned by availableModels is null by we assume a read error occurred.
  122.    *
  123.    * @param model in question
  124.    * @param availableModels a function returning all models with the same id as a list
  125.    * @param el where we register errors.
  126.    */
  127.   private void errorIfModelBeingProcessedExistsMoreThanOnce(DefinitionModel model, Function<String,
  128.       List<DefinitionModel>> availableModels, Consumer<ErrorModel> el) {
  129.     List<DefinitionModel> allModelsOfIdentity =  availableModels.apply(model.getIdentity());
  130.     if (allModelsOfIdentity == null) {
  131.       el.accept(new ErrorModel(ErrorType.COULD_NOT_READ,
  132.           Arrays.asList(new DefinitionModel(model.getIdentity())),
  133.           Arrays.asList(model)));
  134.     } else {
  135.       if (allModelsOfIdentity.size() > 1) {
  136.         el.accept(new ErrorModel(ErrorType.DUPLICATED_MATCHING_DEFINITIONS,
  137.             allModelsOfIdentity,
  138.             Arrays.asList(model)));
  139.       }
  140.     }
  141.   }
  142. }