1 /*
   2  * Copyright 2004 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package com.sun.mirror.util;
  27 
  28 
  29 import java.util.ArrayList;
  30 import java.util.Collection;
  31 
  32 import com.sun.mirror.declaration.Declaration;
  33 import com.sun.mirror.declaration.Modifier;
  34 
  35 import static com.sun.mirror.declaration.Modifier.*;
  36 
  37 
  38 /**
  39  * A filter for selecting just the items of interest
  40  * from a collection of declarations.
  41  * The filter is said to <i>select</i> or to <i>match</i> those declarations.
  42  * Filters can be created in several ways:
  43  * by the static methods described below,
  44  * by negating or composing existing filters,
  45  * or by subclasses that implement arbitrary matching rules.
  46  *
  47  * <p> A subclass can create an arbitrary filter simply by implementing
  48  * the {@link #matches(Declaration)} method.
  49  *
  50  * <p> Examples.
  51  * <p> Selecting the <tt>public</tt> declarations from a collection:
  52  * <blockquote><pre>
  53  *     result = FILTER_PUBLIC.filter(decls);            </pre></blockquote>
  54  * Selecting class declarations (including enums):
  55  * <blockquote><pre>
  56  *     classFilter = DeclarationFilter.getFilter(ClassDeclaration.class);
  57  *     result = classFilter.filter(decls);              </pre></blockquote>
  58  * Selecting class declarations but excluding enums:
  59  * <blockquote><pre>
  60  *     enumFilter = DeclarationFilter.getFilter(EnumDeclaration.class);
  61  *     compoundFilter = classFilter.and(enumFilter.not());
  62  *     result = compoundFilter.filter(decls);           </pre></blockquote>
  63  * Selecting declarations named "Bob":
  64  * <blockquote><pre>
  65  *     nameFilter = new DeclarationFilter() {
  66  *                      public boolean matches(Declaration d) {
  67  *                          return d.getSimpleName().equals("Bob");
  68  *                      }
  69  *                  };
  70  *     result = nameFilter.filter(decls);               </pre></blockquote>
  71  *
  72  * @author Joseph D. Darcy
  73  * @author Scott Seligman
  74  * @since 1.5
  75  */
  76 
  77 public class DeclarationFilter {
  78 
  79     // Predefined filters for convenience.
  80 
  81     /**
  82      * A filter that selects only <tt>public</tt> declarations.
  83      */
  84     public static final DeclarationFilter FILTER_PUBLIC =
  85             new AccessFilter(PUBLIC);
  86 
  87     /**
  88      * A filter that selects only <tt>protected</tt> declarations.
  89      */
  90     public static final DeclarationFilter FILTER_PROTECTED =
  91             new AccessFilter(PROTECTED);
  92 
  93     /**
  94      * A filter that selects only <tt>public</tt> or <tt>protected</tt>
  95      * declarations.
  96      */
  97     public static final DeclarationFilter FILTER_PUBLIC_OR_PROTECTED =
  98             new AccessFilter(PUBLIC, PROTECTED);
  99 
 100     /**
 101      * A filter that selects only package-private (<i>default</i>)
 102      * declarations.
 103      */
 104     public static final DeclarationFilter FILTER_PACKAGE =
 105             new AccessFilter();
 106 
 107     /**
 108      * A filter that selects only <tt>private</tt> declarations.
 109      */
 110     public static final DeclarationFilter FILTER_PRIVATE =
 111             new AccessFilter(PRIVATE);
 112 
 113 
 114     /**
 115      * Constructs an identity filter:  one that selects all declarations.
 116      */
 117     public DeclarationFilter() {
 118     }
 119 
 120 
 121 
 122     // Methods to create a filter.
 123 
 124     /**
 125      * Returns a filter that selects declarations containing all of a
 126      * collection of modifiers.
 127      *
 128      * @param mods  the modifiers to match (non-null)
 129      * @return a filter that matches declarations containing <tt>mods</tt>
 130      */
 131     public static DeclarationFilter getFilter(
 132                                              final Collection<Modifier> mods) {
 133         return new DeclarationFilter() {
 134             public boolean matches(Declaration d) {
 135                 return d.getModifiers().containsAll(mods);
 136             }
 137         };
 138     }
 139 
 140     /**
 141      * Returns a filter that selects declarations of a particular kind.
 142      * For example, there may be a filter that selects only class
 143      * declarations, or only fields.
 144      * The filter will select declarations of the specified kind,
 145      * and also any subtypes of that kind; for example, a field filter
 146      * will also select enum constants.
 147      *
 148      * @param kind  the kind of declarations to select
 149      * @return a filter that selects declarations of a particular kind
 150      */
 151     public static DeclarationFilter getFilter(
 152                                      final Class<? extends Declaration> kind) {
 153         return new DeclarationFilter() {
 154             public boolean matches(Declaration d) {
 155                 return kind.isInstance(d);
 156             }
 157         };
 158     }
 159 
 160     /**
 161      * Returns a filter that selects those declarations selected
 162      * by both this filter and another.
 163      *
 164      * @param f  filter to be composed with this one
 165      * @return a filter that selects those declarations selected by
 166      *          both this filter and another
 167      */
 168     public DeclarationFilter and(DeclarationFilter f) {
 169         final DeclarationFilter f1 = this;
 170         final DeclarationFilter f2 = f;
 171         return new DeclarationFilter() {
 172             public boolean matches(Declaration d) {
 173                 return f1.matches(d) && f2.matches(d);
 174             }
 175         };
 176     }
 177 
 178     /**
 179      * Returns a filter that selects those declarations selected
 180      * by either this filter or another.
 181      *
 182      * @param f  filter to be composed with this one
 183      * @return a filter that selects those declarations selected by
 184      *          either this filter or another
 185      */
 186     public DeclarationFilter or(DeclarationFilter f) {
 187         final DeclarationFilter f1 = this;
 188         final DeclarationFilter f2 = f;
 189         return new DeclarationFilter() {
 190             public boolean matches(Declaration d) {
 191                 return f1.matches(d) || f2.matches(d);
 192             }
 193         };
 194     }
 195 
 196     /**
 197      * Returns a filter that selects those declarations not selected
 198      * by this filter.
 199      *
 200      * @return a filter that selects those declarations not selected
 201      * by this filter
 202      */
 203     public DeclarationFilter not() {
 204         return new DeclarationFilter() {
 205             public boolean matches(Declaration d) {
 206                 return !DeclarationFilter.this.matches(d);
 207             }
 208         };
 209     }
 210 
 211 
 212 
 213     // Methods to apply a filter.
 214 
 215     /**
 216      * Tests whether this filter matches a given declaration.
 217      * The default implementation always returns <tt>true</tt>;
 218      * subclasses should override this.
 219      *
 220      * @param decl  the declaration to match
 221      * @return <tt>true</tt> if this filter matches the given declaration
 222      */
 223     public boolean matches(Declaration decl) {
 224         return true;
 225     }
 226 
 227     /**
 228      * Returns the declarations matched by this filter.
 229      * The result is a collection of the same type as the argument;
 230      * the {@linkplain #filter(Collection, Class) two-parameter version}
 231      * of <tt>filter</tt> offers control over the result type.
 232      *
 233      * @param <D>    type of the declarations being filtered
 234      * @param decls  declarations being filtered
 235      * @return the declarations matched by this filter
 236      */
 237     public <D extends Declaration> Collection<D> filter(Collection<D> decls) {
 238         ArrayList<D> res = new ArrayList<D>(decls.size());
 239         for (D d : decls) {
 240             if (matches(d)) {
 241                 res.add(d);
 242             }
 243         }
 244         return res;
 245     }
 246 
 247     /**
 248      * Returns the declarations matched by this filter, with the result
 249      * being restricted to declarations of a given kind.
 250      * Similar to the simpler
 251      * {@linkplain #filter(Collection) single-parameter version}
 252      * of <tt>filter</tt>, but the result type is specified explicitly.
 253      *
 254      * @param <D>      type of the declarations being returned
 255      * @param decls    declarations being filtered
 256      * @param resType  type of the declarations being returned --
 257      *                  the reflective view of <tt>D</tt>
 258      * @return the declarations matched by this filter, restricted to those
 259      *                  of the specified type
 260      */
 261     public <D extends Declaration> Collection<D>
 262             filter(Collection<? extends Declaration> decls, Class<D> resType) {
 263         ArrayList<D> res = new ArrayList<D>(decls.size());
 264         for (Declaration d : decls) {
 265             if (resType.isInstance(d) && matches(d)) {
 266                 res.add(resType.cast(d));
 267             }
 268         }
 269         return res;
 270     }
 271 
 272 
 273 
 274     /*
 275      * A filter based on access modifiers.
 276      */
 277     private static class AccessFilter extends DeclarationFilter {
 278 
 279         // The first access modifier to filter on, or null if we're looking
 280         // for declarations with no access modifiers.
 281         private Modifier mod1 = null;
 282 
 283         // The second access modifier to filter on, or null if none.
 284         private Modifier mod2 = null;
 285 
 286         // Returns a filter that matches declarations with no access
 287         // modifiers.
 288         AccessFilter() {
 289         }
 290 
 291         // Returns a filter that matches m.
 292         AccessFilter(Modifier m) {
 293             mod1 = m;
 294         }
 295 
 296         // Returns a filter that matches either m1 or m2.
 297         AccessFilter(Modifier m1, Modifier m2) {
 298             mod1 = m1;
 299             mod2 = m2;
 300         }
 301 
 302         public boolean matches(Declaration d) {
 303             Collection<Modifier> mods = d.getModifiers();
 304             if (mod1 == null) { // looking for package private
 305                 return !(mods.contains(PUBLIC) ||
 306                          mods.contains(PROTECTED) ||
 307                          mods.contains(PRIVATE));
 308             }
 309             return mods.contains(mod1) &&
 310                    (mod2 == null || mods.contains(mod2));
 311         }
 312     }
 313 }