1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.currysrc.api.process.ast; 17 18 import java.util.LinkedHashMap; 19 import java.util.Map; 20 import java.util.Map.Entry; 21 import org.eclipse.jdt.core.dom.BodyDeclaration; 22 23 /** 24 * Associates a {@link BodyDeclaration} (identified by a {@link BodyDeclarationLocator}) with some 25 * data, represented as a {@link Mapping}. 26 * 27 * @param <T> the type of data that can be associated with a {@link BodyDeclarationLocator}. 28 */ 29 public class BodyDeclarationLocatorStore<T> { 30 31 private final Map<BodyDeclarationLocator, T> locator2Data; 32 BodyDeclarationLocatorStore()33 public BodyDeclarationLocatorStore() { 34 locator2Data = new LinkedHashMap<>(); 35 } 36 37 /** 38 * Adds a mapping between a {@link BodyDeclarationLocator} and some data. 39 * 40 * <p>If two of more {@link BodyDeclarationLocator} instances are added that will match the 41 * same {@link BodyDeclaration} then the first one added will take precedence. 42 * 43 * <p>It is an error to add the same {@link BodyDeclarationLocator} more than once. 44 */ add(BodyDeclarationLocator locator, T data)45 public void add(BodyDeclarationLocator locator, T data) { 46 T existing = locator2Data.putIfAbsent(locator, data); 47 if (existing != null) { 48 throw new IllegalStateException( 49 "Locator " + locator + " already has existing mapping: " + existing); 50 } 51 } 52 53 /** 54 * Finds the data associated with the supplied {@link BodyDeclaration}. 55 * 56 * <p>This iterates through the list of mappings (in insertion order). For each mapping the 57 * {@link BodyDeclarationLocator} is checked to see if it matches the {@code declaration}. If it 58 * does match then the associated data is returned, otherwise the next mapping is checked. If no 59 * mappings matched then null is returned. 60 * 61 * @param declaration the {@link BodyDeclaration} whose associated data is required. 62 * @return null if there is no data associated with the supplied {@link BodyDeclaration}, 63 * otherwise the data provided with the matching {@link BodyDeclarationLocator}. 64 */ find(BodyDeclaration declaration)65 public T find(BodyDeclaration declaration) { 66 for (Entry<BodyDeclarationLocator, T> entry : locator2Data.entrySet()) { 67 if (entry.getKey().matches(declaration)) { 68 return entry.getValue(); 69 } 70 } 71 72 return null; 73 } 74 75 public static class Mapping<T> { 76 private final BodyDeclarationLocator locator; 77 private final T value; 78 Mapping(BodyDeclarationLocator locator, T value)79 private Mapping(BodyDeclarationLocator locator, T value) { 80 this.locator = locator; 81 this.value = value; 82 } 83 getLocator()84 public BodyDeclarationLocator getLocator() { 85 return locator; 86 } 87 getValue()88 public T getValue() { 89 return value; 90 } 91 } 92 93 /** 94 * Finds the mapping associated with the supplied {@link BodyDeclaration}. 95 * 96 * <p>This iterates through the list of mappings (in insertion order). For each mapping the 97 * {@link BodyDeclarationLocator} is checked to see if it matches the {@code declaration}. If it 98 * does match then the mapping is returned, otherwise the next mapping is checked. If no mappings 99 * matched then null is returned. 100 * 101 * @param declaration the {@link BodyDeclaration} whose associated data is required. 102 * @return null if there is no mapping associated with the supplied {@link BodyDeclaration}, 103 * otherwise the mapping provided with the matching {@link BodyDeclarationLocator}. 104 */ findMapping(BodyDeclaration declaration)105 public Mapping<T> findMapping(BodyDeclaration declaration) { 106 for (Entry<BodyDeclarationLocator, T> entry : locator2Data.entrySet()) { 107 if (entry.getKey().matches(declaration)) { 108 return new Mapping<>(entry.getKey(), entry.getValue()); 109 } 110 } 111 112 return null; 113 } 114 } 115