| /* |
| * |
| * 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.royale.compiler.internal.scopes; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import org.apache.royale.compiler.definitions.IDefinition; |
| import org.apache.royale.compiler.internal.definitions.DefinitionBase; |
| import org.apache.royale.compiler.scopes.IDefinitionSet; |
| |
| /** |
| * An abstract base class for implementations of {@link IDefinitionStore} |
| * that use a small number of fields, rather than a map, to store definition sets. |
| */ |
| public abstract class SmallDefinitionStoreBase implements IDefinitionStore |
| { |
| protected static final String[] EMPTY_STRING_ARRAY = new String[0]; |
| |
| /** |
| * Gets the base name of the definitions stored in the specified definition set. |
| * |
| * @param definitionSet An {@link IDefinitionSet} or <code>null</code>. |
| * @return The base name, or <code>null</code> if the definition set |
| * was <code>null</code> or empty. |
| */ |
| protected static String getBaseName(IDefinitionSet definitionSet) |
| { |
| if (definitionSet == null || definitionSet.getSize() == 0) |
| return null; |
| |
| IDefinition firstDefinition = definitionSet.getDefinition(0); |
| return firstDefinition.getBaseName(); |
| } |
| |
| /** |
| * Adds a new definition to a definition set, possibly creating a new |
| * definition set in the process (if the old definition set was |
| * <code>null</code> or couldn't hold the new definition). |
| * <p> |
| * This method handles the fact that we use different optimized implementations |
| * of {@link IDefinitionSet} for different set sizes. |
| * |
| * @param oldDefinitionSet An {@link IDefinitionSet} containing definitions. |
| * This may be null if we are adding the first definition with a particular base name. |
| * @param definition The {@link IDefinition} to add to the set. |
| * @return Either the definition set that was passed in or a new definition set. |
| * In either case, the definition has been added. |
| */ |
| public static IDefinitionSet addDefinitionToSet(IDefinitionSet oldDefinitionSet, |
| IDefinition definition) |
| { |
| assert definition != null: "Definition cannot be null"; |
| assert getBaseName(oldDefinitionSet) == null || getBaseName(oldDefinitionSet).equals(definition.getBaseName()) : "Base name must match"; |
| |
| // We will probably have to create a new set, |
| // but we might be able to re-use the old one. |
| IDefinitionSet newDefinitionSet = oldDefinitionSet; |
| |
| if (oldDefinitionSet == null) |
| { |
| // There was no old set. Use the new definition itself as a new set-of-size-1. |
| // This is possible because DefinitionBase implements IDefinitionSet. |
| newDefinitionSet = (DefinitionBase)definition; |
| } |
| else if (oldDefinitionSet.getMaxSize() == 1) |
| { |
| // There is one old definition acting as a set-of-size-1. |
| // Replace it with a SmallDefinitionSet of size 2 |
| // containing the old definition and the new definition. |
| newDefinitionSet = new SmallDefinitionSet((IDefinition)oldDefinitionSet, definition); |
| } |
| else if (oldDefinitionSet.getMaxSize() == 2) |
| { |
| // There is a SmallDefinition set with 2 (or perhaps 1 or 0) definitions. |
| // If the SmallDefinitionSet is full, replace it with an LargeDefinitionSet |
| // which can hold any number of definitions. |
| if (oldDefinitionSet.getSize() == oldDefinitionSet.getMaxSize()) |
| newDefinitionSet = new LargeDefinitionSet(oldDefinitionSet); |
| ((IMutableDefinitionSet)newDefinitionSet).addDefinition(definition); |
| } |
| else |
| { |
| // We already have an LargeDefinitionSet that can hold any number of definitions. |
| // Just add the new one. |
| ((LargeDefinitionSet)newDefinitionSet).addDefinition(definition); |
| } |
| |
| // Return the possibly-new definition set with the new definition. |
| return newDefinitionSet; |
| } |
| |
| /** |
| * Removes a definition from a definition set. |
| * <p> |
| * This method handles the fact that we use different optimized implementations |
| * of {@link IDefinitionSet} for different set sizes. |
| * |
| * @param definitionSet An {@link IDefinitionSet} containing definitions. |
| * @param definition The {@link IDefinition} to be removed. |
| * @return <code>true</code> if the definition set is now empty |
| * and must be removed from the store. |
| */ |
| public static boolean removeDefinitionFromSet(IDefinitionSet definitionSet, |
| IDefinition definition) |
| { |
| assert definitionSet != null : "Definition set cannot be null"; |
| assert definition != null: "Definition cannot be null"; |
| assert definition.getBaseName().equals(getBaseName(definitionSet)) : "Base name must match"; |
| |
| // If the definition set is a single definition, we can't remove the |
| // definition from itself, but by returning true we indicate that it |
| // must removed from the store. |
| if (definitionSet.getMaxSize() == 1) |
| return true; |
| |
| // If the definition set is a SmallDefinitionSet or an LargeDefinitionSet, |
| // remove the definition from the set, and, if the set is then empty, |
| // return true to indicate that the empty set should be removed from |
| //the store. |
| else |
| { |
| ((IMutableDefinitionSet)definitionSet).removeDefinition(definition); |
| if (definitionSet.isEmpty()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * If the specified definition set is non-null and contains definitions, |
| * add the base name of those definitions to the specified list. |
| * <p> |
| * This is a helper function used by subclasses to implement {@link #getAllNames}(). |
| * Subclasses call this method for each field that can store a definition set. |
| * |
| * @param list A list of base names being built up. |
| * @param definitionSet An {@link IDefinitionSet}, which may be null or empty. |
| */ |
| protected static void addBaseNameToList(List<String> list, IDefinitionSet definitionSet) |
| { |
| String baseName = getBaseName(definitionSet); |
| if (baseName != null) |
| list.add(baseName); |
| } |
| |
| /** |
| * If the specified definition set is non-null, add it to the specified list. |
| * <p> |
| * This is a helper function used by subclasses to implement {@link #getAllDefinitionSets}(). |
| * Subclasses call this method for each field that can store a definition set. |
| * The field may be null or may contain an empty definition set. |
| * |
| * @param list A list of definition sets being built up. |
| * @param definitionSet An {@link IDefinitionSet}, which may be null or empty. |
| */ |
| protected static void addDefinitionSetToList(List<IDefinitionSet> list, IDefinitionSet definitionSet) |
| { |
| if (definitionSet != null) |
| list.add(definitionSet); |
| } |
| |
| /** |
| * If the specified definition set is non-null and contains definitions, |
| * add those definitions to the specified list. |
| * <p> |
| * This is a helper function used by subclasses to implement {@link #getAllDefinitions}(). |
| * Subclasses call this method for each field that can store a definition set. |
| * |
| * @param list A list of definitions being built up. |
| * @param definitionSet An {@link IDefinitionSet}, which may be null or empty. |
| */ |
| protected static void addDefinitionsToList(List<IDefinition> list, IDefinitionSet definitionSet) |
| { |
| if (definitionSet == null) |
| return; |
| |
| int n = definitionSet.getSize(); |
| for (int i = 0; i < n; i++) |
| { |
| IDefinition definition = definitionSet.getDefinition(i); |
| list.add(definition); |
| } |
| } |
| |
| /** |
| * Constructor. |
| */ |
| public SmallDefinitionStoreBase() |
| { |
| } |
| |
| @Override |
| public boolean add(IDefinition definition) |
| { |
| assert definition != null : "A null definition cannot be added to a store"; |
| |
| // Look for a field storing a definition set with the same base name. |
| String baseName = definition.getBaseName(); |
| int i = findIndexForBaseName(baseName); |
| |
| // If not found, find a field where we can store a new definition set. |
| if (i == -1) |
| i = findAvailableIndex(); |
| |
| // If there are no more fields available, |
| // return false to indicate that this store is full. |
| if (i == -1) |
| return false; |
| |
| // Get the definition set from the field we've found. It might be null. |
| IDefinitionSet oldDefinitionSet = getDefinitionSet(i); |
| |
| // Add the new definition to the old set. This may create a new set. |
| IDefinitionSet newDefinitionSet = addDefinitionToSet(oldDefinitionSet, definition); |
| |
| // Store the new set into the field. |
| if (newDefinitionSet != oldDefinitionSet) |
| setDefinitionSet(i, newDefinitionSet); |
| |
| return true; |
| } |
| |
| @Override |
| public boolean remove(IDefinition definition) |
| { |
| assert definition != null : "A null definition cannot be removed from a store"; |
| |
| // Look for a field storing a definition set with the same base name. |
| String baseName = definition.getBaseName(); |
| int i = findIndexForBaseName(baseName); |
| |
| // If not found, return false to indicate that the definition |
| // doesn't exist in this store. |
| if (i == -1) |
| return false; |
| |
| // Get the definition set from the field we've found. It will not be null. |
| IDefinitionSet oldDefinitionSet = getDefinitionSet(i); |
| |
| // Remove the definition from the set, |
| // and perhaps remove the set from this store. |
| if (removeDefinitionFromSet(oldDefinitionSet, definition)) |
| setDefinitionSet(i, null); |
| |
| return true; |
| } |
| |
| @Override |
| public abstract IDefinitionSet getDefinitionSetByName(String baseName); |
| |
| /** |
| * Gets the number of fields for storing definition sets. |
| * |
| * @return The number of fields. |
| */ |
| public abstract int getCapacity(); |
| |
| /** |
| * Gets the definition set stored in the specified field. |
| * |
| * @param i The index of the field. |
| * @return The {@link IDefinitionSet} store there. |
| */ |
| protected abstract IDefinitionSet getDefinitionSet(int i); |
| |
| /** |
| * Sets the specified definition set into the specified field. |
| * |
| * @param i The index of the field. |
| * @param definitionSet The {@link IDefinitionSet} to store there. |
| */ |
| protected abstract void setDefinitionSet(int i, IDefinitionSet definitionSet); |
| |
| /** |
| * Returns the index of the field storing a definition set with the |
| * specified base name, or -1 if there is no such field. |
| * |
| * @param baseName The base name to look for. |
| * @return The index of the field whose definition set has this base name. |
| */ |
| protected abstract int findIndexForBaseName(String baseName); |
| |
| /** |
| * Returns the index of the first null field where a new definition set |
| * can be stored. |
| * |
| * @return The index of a null field, or - is there is no such field. |
| */ |
| protected abstract int findAvailableIndex(); |
| |
| |
| /** |
| * For debugging only. |
| */ |
| @Override |
| public String toString() |
| { |
| StringBuilder sb = new StringBuilder(); |
| |
| // Display base names in alphabetical order. |
| String[] names = getAllNames().toArray(EMPTY_STRING_ARRAY); |
| Arrays.sort(names); |
| |
| for (String name : names) |
| { |
| sb.append(name); |
| sb.append('\n'); |
| |
| IDefinitionSet set = getDefinitionSetByName(name); |
| int n = set.getSize(); |
| for (int i = 0; i < n; i++) |
| { |
| IDefinition d = set.getDefinition(i); |
| sb.append(" "); |
| sb.append(d.toString()); |
| sb.append('\n'); |
| } |
| } |
| |
| return sb.toString(); |
| } |
| } |