View Javadoc
1   package org.apache.fulcrum.json.jackson;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.List;
23  import java.util.concurrent.CopyOnWriteArrayList;
24  import java.util.concurrent.atomic.AtomicBoolean;
25  
26  import org.apache.avalon.framework.logger.LogEnabled;
27  import org.apache.avalon.framework.logger.Logger;
28  
29  import com.fasterxml.jackson.databind.introspect.Annotated;
30  import com.fasterxml.jackson.databind.introspect.AnnotatedClass;
31  import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
32  import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
33  
34  /**
35   * The intent of this custom introspector is to provide filtering capabilities
36   * by using String parameters (properties and class types), which could be
37   * adjusted e.g. from a scriptable context (velocity template). 
38   * Class Type Filtering currently not supported except for Exclude Filter: {@link Jackson2MapperService#serializeAllExceptFilter(Object, Class, Boolean, String...)}.
39   * 
40   * 
41   * @author gk
42   * @version $Id$
43   * 
44   */
45  public class SimpleNameIntrospector extends NopAnnotationIntrospector implements LogEnabled {
46      /**
47       * 
48       */
49      private static final long serialVersionUID = 1L;
50      
51      private List<Class<?>> filteredClasses = new CopyOnWriteArrayList<>();
52      private List<String> externalFilterExcludeClasses = new CopyOnWriteArrayList<>();
53      private List<String> externalFilterIncludeClasses = new CopyOnWriteArrayList<>();
54      // is used only for filtering by class
55      private AtomicBoolean isExludeType = new AtomicBoolean(false);
56      
57      private static Logger logger;
58  
59      /**
60       * Filtering on method types.
61       * 
62       */
63      @Override
64      public Boolean isIgnorableType(AnnotatedClass ac) {
65          Boolean isIgnorable = super.isIgnorableType(ac);
66          if (isIgnorable == null || !isIgnorable) {
67              if (getIsExludeType()) { // could be removed, if cleaning after call ?
68                  if (!externalFilterExcludeClasses.isEmpty()
69                          && externalFilterExcludeClasses.contains(ac.getName())) {
70                      isIgnorable = true;
71                  }
72              } else {
73                  // not yet used
74                  if (!externalFilterIncludeClasses.isEmpty()
75                          && !externalFilterIncludeClasses.contains(ac.getName())) {
76                          try {
77                              Class.forName(ac.getName());
78                              isIgnorable = true;
79                          } catch (ClassNotFoundException e) {
80                              // no clazz ignore, could NOT ignore as no filterable clazz
81                          }
82                  }
83              }
84          }
85          return isIgnorable;
86      }
87      /**
88       * @return Object Filtering on properties returns an object, if
89       *         {@link #filteredClasses} contains the class provided. The
90       *         filter itself currently is {@link SimpleFilterProvider}.
91       */
92      @Override
93      public Object findFilterId(Annotated ac) {
94          Object id = super.findFilterId(ac);
95          // Let's default to current behavior if annotation is found:
96          // Object id = super.findFilterId(ac);
97          // but use simple class name if not
98          if (id == null) {
99              String name = ac.getName();
100             Class<?> targetClazz = ac.getRawType();
101             if (!filteredClasses.isEmpty()
102                     && filteredClasses.contains(targetClazz)
103                     ) {
104                 logger.debug("filter applying to " +name);
105                 id = name;
106             } else {
107                 // check if target class is a child from filter class -> apply filter 
108                 for (Class<?> filterClazz : filteredClasses) {
109                     // the currently checked instance of type targetClazz is a child of the filter class filterClazz ->  filter child 
110                     if (filterClazz.isAssignableFrom(targetClazz)) {
111                         logger.debug("filter applying to parent " +filterClazz +" matching child class "+name );
112                         id = name;
113                         break;
114                     }
115                     // the currently checked instance of type targetClazz is a parent of the filter class filterClazz -> filter parent
116                     if (targetClazz.isAssignableFrom(filterClazz)) {
117                         logger.debug("filter applying to child " +filterClazz+" matching parent class "+name);
118                         id = name;
119                         break;
120                     }
121                 }
122             }
123         }
124         return id;
125     }
126 
127     public List<Class<?>> getFilteredClasses() {
128         return filteredClasses;
129     }
130 
131     public void setFilteredClass(Class<?> filteredClass) {
132         if (!filteredClasses.contains(filteredClass)) {
133             filteredClasses.add(filteredClass);
134         }
135     }
136 
137     public void setFilteredClasses(Class<?>... classes) {
138 
139         for (int i = 0; i < classes.length; i++) {
140             if (!filteredClasses.contains(classes[i])) {
141                 filteredClasses.add(classes[i]);
142             }
143 //            if (classes[i].getSuperclass() != null) {
144 //                Class superClazz = classes[i].getSuperclass();
145 //                if (!externalFilterClasses.contains(superClazz)) {
146 //                    externalFilterClasses.add(superClazz);
147 //                }  
148 //            }
149         }
150     }
151 
152     public void removeFilteredClass(Class<?> filteredClass) {
153             if (filteredClasses.contains(filteredClass)) {
154                 filteredClasses.remove(filteredClass);
155             }
156     }
157     
158     public void setExternalFilterExcludeClasses(Class<?>... classes) {
159 
160         for (int i = 0; i < classes.length; i++) {
161             if (!externalFilterExcludeClasses.contains(classes[i].getName())) {
162 
163                 externalFilterExcludeClasses.add(classes[i].getName());
164             }
165         }
166     }
167     
168     public void removeExternalFilterExcludeClass(Class<?> externalFilterClass) {
169         if (externalFilterExcludeClasses.contains(externalFilterClass.getName())) {
170             externalFilterExcludeClasses.remove(externalFilterClass.getName());
171         }
172     }
173     
174     public void setExternalFilterIncludeClasses(Class<?>... classes) {
175 
176         for (int i = 0; i < classes.length; i++) {
177             if (!externalFilterIncludeClasses.contains(classes[i].getName())) {
178 
179                 externalFilterIncludeClasses.add(classes[i].getName());
180             }
181         }
182     }
183     
184     public void removeExternalFilterIncludeClasses(Class<?> externalFilterClass) {
185         if (externalFilterIncludeClasses.contains(externalFilterClass.getName())) {
186             externalFilterIncludeClasses.remove(externalFilterClass.getName());
187         }
188     }
189     
190     public boolean getIsExludeType() {
191         return isExludeType.get();
192     }
193     public void setIsExludeType(boolean isExludeType) {
194         this.isExludeType.getAndSet(isExludeType);
195     }
196     @Override
197     public void enableLogging(Logger logger) {
198         SimpleNameIntrospector.logger = logger;        
199     }
200 
201 }