Made DefaultObjectWrapper immtutable (no more setters), also removed public 
constructors. Instead, instances are created with 
DefaultObjectWrapper.Builder.build().
Reworked DefaultObjectWrapper and ClassIntrospector builder classes to be 
Xxx.Builder instead of XxxBuilder. Object builder expressions (used in setting 
values) also recognize this convention now.
Added fluent API setters to DefaultObjectWrapper.Builder.
Added common superclass for builders, which supports fluent API-s. Other 
DefaultObjectWrapper builder related cleanups.
Along the way, removed DefaultObjectWrapper.methodsShadowItems setting; methods 
will always shadow things that the "generic get" (that is, get(String) that 
doesn't come from Map or any known interface) could return.


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/dceec32e
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/dceec32e
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/dceec32e

Branch: refs/heads/3
Commit: dceec32eda765c4d3ef1f46ce1cce93760855d8a
Parents: 529cdd8
Author: ddekany <[email protected]>
Authored: Thu Mar 16 18:12:38 2017 +0100
Committer: ddekany <[email protected]>
Committed: Fri Mar 17 22:12:14 2017 +0100

----------------------------------------------------------------------
 build.xml                                       |   2 +-
 .../core/ASTDirCapturingAssignment.java         |   2 +-
 .../apache/freemarker/core/ASTExpression.java   |   2 +-
 .../apache/freemarker/core/Configurable.java    |   9 +-
 .../apache/freemarker/core/Configuration.java   |  10 +-
 .../apache/freemarker/core/CustomAttribute.java |   2 +-
 .../freemarker/core/DirectiveCallPlace.java     |   2 +-
 .../org/apache/freemarker/core/Environment.java |   6 +-
 .../org/apache/freemarker/core/Template.java    |   2 +-
 .../apache/freemarker/core/TokenMgrError.java   |   2 +-
 .../core/_ObjectBuilderSettingEvaluator.java    |  63 +-
 .../core/_SettingEvaluationEnvironment.java     |   2 +-
 .../core/debug/RmiDebuggedEnvironmentImpl.java  |   3 +-
 .../core/debug/RmiDebuggerListenerImpl.java     |   2 +-
 .../core/model/TemplateMethodModelEx.java       |   2 +-
 .../freemarker/core/model/impl/BeanModel.java   |  28 +-
 .../core/model/impl/ClassIntrospector.java      | 180 +++-
 .../model/impl/ClassIntrospectorBuilder.java    | 190 ----
 .../core/model/impl/DefaultObjectWrapper.java   | 880 +++++++++++--------
 .../model/impl/DefaultObjectWrapperBuilder.java | 159 ----
 .../impl/DefaultObjectWrapperConfiguration.java | 216 -----
 .../core/model/impl/MethodSorter.java           |   2 +-
 .../model/impl/OverloadedMethodsSubset.java     |   2 +-
 .../model/impl/OverloadedVarArgsMethods.java    |   2 +-
 .../model/impl/RestrictedObjectWrapper.java     |  31 +-
 .../freemarker/core/model/impl/SimpleHash.java  |   2 +-
 .../core/model/impl/SimpleSequence.java         |   2 +-
 .../core/model/impl/SingletonCustomizer.java    |   2 +-
 .../freemarker/core/model/impl/_ModelAPI.java   |  68 +-
 .../templateresolver/TemplateLookupContext.java |   2 +-
 ...TemplateLoaderBasedTemplateLookupResult.java |   6 +-
 .../freemarker/core/util/BuilderBase.java       |  34 +
 .../core/util/ProductWrappingBuilder.java       |  39 +
 .../freemarker/core/util/WriteProtectable.java  |  37 -
 .../apache/freemarker/core/util/_DateUtil.java  |   2 +-
 ...AliasTargetTemplateValueFormatException.java |   2 +-
 .../impl/AliasTemplateDateFormatFactory.java    |   2 +-
 .../impl/AliasTemplateNumberFormatFactory.java  |   2 +-
 .../impl/JavaTemplateNumberFormatFactory.java   |   2 +-
 .../freemarker/servlet/FreemarkerServlet.java   |  12 +-
 .../apache/freemarker/servlet/IncludePage.java  |   2 +-
 .../jsp/CustomTagAndELFunctionCombiner.java     |   2 +-
 .../freemarker/servlet/jsp/TaglibFactory.java   |   2 +-
 .../servlet/jsp/_FreeMarkerPageContext21.java   |   2 +-
 src/manual/en_US/FM3-CHANGE-LOG.txt             |   8 +-
 .../org/apache/freemarker/core/ASTPrinter.java  |   2 +-
 .../freemarker/core/ConfigurationTest.java      |   5 +-
 .../freemarker/core/IncludeAndImportTest.java   |   2 +-
 .../freemarker/core/IteratorIssuesTest.java     |   3 +-
 .../apache/freemarker/core/ListErrorsTest.java  |   2 +-
 .../core/ObjectBuilderSettingsTest.java         |  45 +-
 .../core/RestrictedObjectWrapperTest.java       |   4 +-
 .../core/RestrictedObjetWrapperTest.java        | 112 +++
 .../freemarker/core/SimpleObjetWrapperTest.java | 112 ---
 .../core/TagSyntaxVariationsTest.java           |   2 +-
 .../core/TemplateConfigurationTest.java         |   2 +-
 .../impl/AbstractParallelIntrospectionTest.java |   3 +-
 .../model/impl/DefaultObjectWrapperDesc.java    |   5 +-
 .../model/impl/DefaultObjectWrapperInc.java     |   5 +-
 ...jectWrapperModelFactoryRegistrationTest.java |  35 +-
 .../impl/DefaultObjectWrapperReadOnlyTest.java  |  87 --
 .../DefaultObjectWrapperSingletonsTest.java     | 100 +--
 .../model/impl/DefaultObjectWrapperTest.java    |  74 +-
 .../DefaultObjectWrapperWithShortedMethods.java |  41 -
 .../DefaultObjectWrapperWithSortedMethods.java  |  40 -
 .../core/model/impl/EnumModelsTest.java         |   3 +-
 .../core/model/impl/ErrorMessagesTest.java      |   4 +-
 .../impl/FineTuneMethodAppearanceTest.java      |   7 +-
 .../Java7MembersOnlyDefaultObjectWrapper.java   |   5 +-
 ...a8DefaultObjectWrapperBridgeMethodsTest.java |   2 +-
 .../impl/Java8DefaultObjectWrapperTest.java     |   2 +-
 .../model/impl/ModelAPINewInstanceTest.java     |   2 +-
 .../core/model/impl/ModelCacheTest.java         |   6 +-
 .../core/model/impl/StaticModelsTest.java       |   3 +-
 .../core/model/impl/TypeFlagsTest.java          |   2 +-
 .../FileTemplateLoaderTest.java                 |   2 +-
 .../servlet/jsp/RealServletContainertTest.java  |   7 +-
 .../freemarker/servlet/jsp/TLDParsingTest.java  |   3 +-
 .../freemarker/test/ResourcesExtractor.java     |   2 +-
 .../freemarker/test/servlet/WebAppTestCase.java |   2 +-
 .../test/templatesuite/TemplateTestCase.java    |   3 +-
 .../test/templatesuite/models/Listables.java    |   7 +-
 .../test/templatesuite/models/MultiModel1.java  |   4 +-
 .../models/TransformHashWrapper.java            |   4 +-
 .../SimpleMapAndCollectionObjectWrapper.java    |   2 +-
 .../apache/freemarker/test/util/XMLLoader.java  |   2 +-
 86 files changed, 1181 insertions(+), 1605 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/build.xml
----------------------------------------------------------------------
diff --git a/build.xml b/build.xml
index 5127c6d..c4a7471 100644
--- a/build.xml
+++ b/build.xml
@@ -369,7 +369,7 @@
     <delete includeEmptyDirs="yes">
       <fileset dir="build/api" includes="**/*" />
     </delete>
-    <!-- javadoc with <fileset> has bugs, so we create a filtered copy: -->
+    <!-- javadoc with <fileset> has bugs, so we invoke a filtered copy: -->
     <copy todir="build/javadoc-sources">
       <fileset dir="src/main/java">
         <exclude name="**/_*.java" />

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java 
b/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java
index 1df39d3..9aa5eab 100644
--- a/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java
+++ b/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java
@@ -99,7 +99,7 @@ final class ASTDirCapturingAssignment extends ASTDirective {
                         result = capturedStringToModel(toString());
                     } catch (TemplateModelException e) {
                         // [Java 1.6] e to cause
-                        throw new IOException("Failed to create FTL value from 
captured string: " + e);
+                        throw new IOException("Failed to invoke FTL value from 
captured string: " + e);
                     }
                     switch(scope) {
                         case ASTDirAssignment.NAMESPACE: {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/ASTExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/ASTExpression.java 
b/src/main/java/org/apache/freemarker/core/ASTExpression.java
index 01ede1e..be00f66 100644
--- a/src/main/java/org/apache/freemarker/core/ASTExpression.java
+++ b/src/main/java/org/apache/freemarker/core/ASTExpression.java
@@ -163,7 +163,7 @@ abstract class ASTExpression extends ASTNode {
     
     static class ReplacemenetState {
         /**
-         * If the replacement expression is not in use yet, we don't have to 
clone it.
+         * If the replacement expression is not in use yet, we don't have to 
deepClone it.
          */
         boolean replacementAlreadyInUse; 
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/Configurable.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Configurable.java 
b/src/main/java/org/apache/freemarker/core/Configurable.java
index c2aeb85..b6c73eb 100644
--- a/src/main/java/org/apache/freemarker/core/Configurable.java
+++ b/src/main/java/org/apache/freemarker/core/Configurable.java
@@ -45,7 +45,6 @@ import 
org.apache.freemarker.core.arithmetic.impl.ConservativeArithmeticEngine;
 import org.apache.freemarker.core.model.ObjectWrapper;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
 import org.apache.freemarker.core.model.impl.RestrictedObjectWrapper;
 import org.apache.freemarker.core.outputformat.OutputFormat;
 import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat;
@@ -1173,7 +1172,7 @@ public class Configurable {
 
     /**
      * Sets the object wrapper used to wrap objects to {@link TemplateModel}-s.
-     * The default is {@link DefaultObjectWrapperBuilder#build()}.
+     * The default is {@link DefaultObjectWrapper.Builder#build()}.
      */
     public void setObjectWrapper(ObjectWrapper objectWrapper) {
         _NullArgumentException.check("objectWrapper", objectWrapper);
@@ -1503,7 +1502,7 @@ public class Configurable {
     /**
      * Adds an invisible <code>#import <i>templateName</i> as 
<i>namespaceVarName</i></code> at the beginning of the
      * main template (that's the top-level template that wasn't 
included/imported from another template). While it only
-     * affects the main template directly, as the imports will create a global 
variable there, the imports will be
+     * affects the main template directly, as the imports will invoke a global 
variable there, the imports will be
      * visible from the further imported templates too (note that {@link 
Configuration#getIncompatibleImprovements()}
      * set to 2.3.24 fixes a rarely surfacing bug with that).
      * 
@@ -2051,7 +2050,7 @@ public class Configurable {
      *       <i>propName1</i>=<i>propValue1</i>, 
<i>propName2</i>=<i>propValue2</i>, ...
      *       <i>propNameN</i>=<i>propValueN</i>)</tt>,
      *       where
-     *       <tt><i>className</i></tt> is the fully qualified class name of 
the instance to create (except if we have
+     *       <tt><i>className</i></tt> is the fully qualified class name of 
the instance to invoke (except if we have
      *       builder class or <tt>INSTANCE</tt> field around, but see that 
later),
      *       <tt><i>constrArg</i></tt>-s are the values of constructor 
arguments,
      *       and <tt><i>propName</i>=<i>propValue</i></tt>-s set JavaBean 
properties (like <tt>x=1</tt> means
@@ -2215,7 +2214,7 @@ public class Configurable {
                         
setObjectWrapper(Configuration.getDefaultObjectWrapper(Configuration.VERSION_3_0_0));
                     }
                 } else if ("restricted".equalsIgnoreCase(value)) {
-                    setObjectWrapper(new 
RestrictedObjectWrapper(Configuration.VERSION_3_0_0));
+                    setObjectWrapper(new 
RestrictedObjectWrapper.Builder(Configuration.VERSION_3_0_0).build());
                 } else {
                     setObjectWrapper((ObjectWrapper) 
_ObjectBuilderSettingEvaluator.eval(
                                     value, ObjectWrapper.class, false, 
_SettingEvaluationEnvironment.getCurrent()));

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/Configuration.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Configuration.java 
b/src/main/java/org/apache/freemarker/core/Configuration.java
index 73e85f1..3d1665a 100644
--- a/src/main/java/org/apache/freemarker/core/Configuration.java
+++ b/src/main/java/org/apache/freemarker/core/Configuration.java
@@ -49,7 +49,7 @@ import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.TemplateModelIterator;
 import org.apache.freemarker.core.model.TemplateScalarModel;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
+import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
 import org.apache.freemarker.core.outputformat.MarkupOutputFormat;
 import org.apache.freemarker.core.outputformat.OutputFormat;
 import 
org.apache.freemarker.core.outputformat.UnregisteredOutputFormatException;
@@ -97,7 +97,7 @@ import 
org.apache.freemarker.core.util._UnmodifiableCompositeSet;
  * <b>The main entry point into the FreeMarker API</b>; encapsulates the 
configuration settings of FreeMarker,
  * also serves as a central template-loading and caching service.
  *
- * <p>This class is meant to be used in a singleton pattern. That is, you 
create an instance of this at the beginning of
+ * <p>This class is meant to be used in a singleton pattern. That is, you 
invoke an instance of this at the beginning of
  * the application life-cycle, set its {@link #setSetting(String, String) 
configuration settings} there (either with the
  * setter methods like {@link #setTemplateLoader(TemplateLoader)} or by 
loading a {@code .properties} file), and then
  * use that single instance everywhere in your application. Frequently 
re-creating {@link Configuration} is a typical
@@ -900,7 +900,7 @@ public class Configuration extends Configurable implements 
Cloneable, ParserConf
      * {@link MruCacheStorage} instead might be advisable. If you don't want 
caching at
      * all, use {@link 
org.apache.freemarker.core.templateresolver.impl.NullCacheStorage} (you can't 
use {@code null}).
      * 
-     * <p>Note that setting the templateResolver storage will re-create the 
template templateResolver, so
+     * <p>Note that setting the templateResolver storage will re-invoke the 
template templateResolver, so
      * all its content will be lost.
      */
     public void setCacheStorage(CacheStorage cacheStorage) {
@@ -2266,7 +2266,7 @@ public class Configuration extends Configurable 
implements Cloneable, ParserConf
      *
      * <p>The values in the map must be thread safe, if you are running 
templates from multiple threads with
      * this configuration. This means that both the plain Java object and the 
{@link TemplateModel}-s created from them
-     * by the {@link ObjectWrapper} must be thread safe. (The standard {@link 
ObjectWrapper}-s of FreeMarker create
+     * by the {@link ObjectWrapper} must be thread safe. (The standard {@link 
ObjectWrapper}-s of FreeMarker invoke
      * thread safe {@link TemplateModel}-s.) The {@link Map} itself need not 
be thread-safe.
      * 
      * <p>This setter method has no getter pair because of the tricky relation 
ship with
@@ -2801,7 +2801,7 @@ public class Configuration extends Configurable 
implements Cloneable, ParserConf
      * @since 2.3.21
      */
     public static ObjectWrapperAndUnwrapper getDefaultObjectWrapper(Version 
incompatibleImprovements) {
-        return new 
DefaultObjectWrapperBuilder(incompatibleImprovements).build();
+        return new 
DefaultObjectWrapper.Builder(incompatibleImprovements).build();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/CustomAttribute.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/CustomAttribute.java 
b/src/main/java/org/apache/freemarker/core/CustomAttribute.java
index e0f5627..37d7db9 100644
--- a/src/main/java/org/apache/freemarker/core/CustomAttribute.java
+++ b/src/main/java/org/apache/freemarker/core/CustomAttribute.java
@@ -79,7 +79,7 @@ public class CustomAttribute {
     /**
      * This method is invoked when {@link #get()} is invoked without 
      * {@link #set(Object)} being invoked before it to define the value in the 
-     * current scope. Override it to create the attribute value on-demand.  
+     * current scope. Override it to invoke the attribute value on-demand.
      * @return the initial value for the custom attribute. By default returns 
null.
      */
     protected Object create() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java 
b/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java
index a8a02e4..5793ad3 100644
--- a/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java
+++ b/src/main/java/org/apache/freemarker/core/DirectiveCallPlace.java
@@ -108,7 +108,7 @@ public interface DirectiveCallPlace {
      *            {@link IdentityHashMap}, but then this feature would be 
slower, while {@code providerIdentity}
      *            mismatches aren't occurring in most applications.)
      * @param objectFactory
-     *            Called when the custom data wasn't yet set, to create its 
initial value. If this parameter is
+     *            Called when the custom data wasn't yet set, to invoke its 
initial value. If this parameter is
      *            {@code null} and the custom data wasn't set yet, then {@code 
null} will be returned. The returned
      *            value of {@link ObjectFactory#createObject()} can be any 
kind of object, but can't be {@code null}.
      * 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Environment.java 
b/src/main/java/org/apache/freemarker/core/Environment.java
index d6cfa91..1d1bafc 100644
--- a/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/src/main/java/org/apache/freemarker/core/Environment.java
@@ -105,7 +105,7 @@ public final class Environment extends Configurable {
     private static final Logger LOG = _CoreLogs.RUNTIME;
     private static final Logger LOG_ATTEMPT = _CoreLogs.ATTEMPT;
 
-    // Do not use this object directly; clone it first! DecimalFormat isn't
+    // Do not use this object directly; deepClone it first! DecimalFormat isn't
     // thread-safe.
     private static final DecimalFormat C_NUMBER_FORMAT = new DecimalFormat(
             "0.################",
@@ -1573,7 +1573,7 @@ public final class Environment extends Configurable {
             throw 
MessageUtil.newCantFormatUnknownTypeDateException(blamedDateSourceExp, e);
         } catch (TemplateValueFormatException e) {
             _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
-                    "Can't create date/time/datetime format based on format 
string ",
+                    "Can't invoke date/time/datetime format based on format 
string ",
                     new _DelayedJQuote(formatString), ". Reason given: ",
                     e.getMessage())
                     .blame(blamedFormatterExp);
@@ -2209,7 +2209,7 @@ public final class Environment extends Configurable {
     /**
      * Returns the read-only hash of globally visible variables. This is the 
correspondent of FTL <code>.globals</code>
      * hash. That is, you see the variables created with <code>&lt;#global 
...&gt;</code>, and the variables of the
-     * data-model. To create new global variables, use {@link 
#setGlobalVariable setGlobalVariable}.
+     * data-model. To invoke new global variables, use {@link 
#setGlobalVariable setGlobalVariable}.
      */
     public TemplateHashModel getGlobalVariables() {
         return new TemplateHashModel() {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/Template.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/Template.java 
b/src/main/java/org/apache/freemarker/core/Template.java
index 7b981af..c6328df 100644
--- a/src/main/java/org/apache/freemarker/core/Template.java
+++ b/src/main/java/org/apache/freemarker/core/Template.java
@@ -56,7 +56,7 @@ import org.apache.freemarker.core.util._NullArgumentException;
  * threads.
  * 
  * <p>
- * Typically, you will use {@link Configuration#getTemplate(String)} to 
create/get {@link Template} objects, so you
+ * Typically, you will use {@link Configuration#getTemplate(String)} to 
invoke/get {@link Template} objects, so you
  * don't construct them directly. But you can also construct a template from a 
{@link Reader} or a {@link String} that
  * contains the template source code. But then it's important to know that 
while the resulting {@link Template} is
  * efficient for later processing, creating a new {@link Template} itself is 
relatively expensive. So try to re-use

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/TokenMgrError.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/TokenMgrError.java 
b/src/main/java/org/apache/freemarker/core/TokenMgrError.java
index 81282a2..79d0f2a 100644
--- a/src/main/java/org/apache/freemarker/core/TokenMgrError.java
+++ b/src/main/java/org/apache/freemarker/core/TokenMgrError.java
@@ -40,7 +40,7 @@ public class TokenMgrError extends Error {
    static final int LEXICAL_ERROR = 0;
 
    /**
-    * An attempt was made to create a second instance of a static token 
manager.
+    * An attempt was made to invoke a second instance of a static token 
manager.
     */
    static final int STATIC_LEXER_ERROR = 1;
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java 
b/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
index e98c104..2769e49 100644
--- 
a/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
+++ 
b/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluator.java
@@ -59,7 +59,6 @@ import 
org.apache.freemarker.core.templateresolver.PathRegexMatcher;
 import org.apache.freemarker.core.util.BugException;
 import org.apache.freemarker.core.util.FTLUtil;
 import org.apache.freemarker.core.util.GenericParseException;
-import org.apache.freemarker.core.util.WriteProtectable;
 import org.apache.freemarker.core.util._ClassUtil;
 import org.apache.freemarker.core.util._StringUtil;
 
@@ -79,7 +78,8 @@ public class _ObjectBuilderSettingEvaluator {
 
     private static final String BUILD_METHOD_NAME = "build";
 
-    private static final String BUILDER_CLASS_POSTFIX = "Builder";
+    private static final String BUILDER_CLASS_POSTFIX_1 = "$Builder";
+    private static final String BUILDER_CLASS_POSTFIX_2 = "Builder";
 
     private static Map<String,String> SHORTHANDS;
     
@@ -860,30 +860,35 @@ public class _ObjectBuilderSettingEvaluator {
             
             boolean clIsBuilderClass;
             try {
-                cl = _ClassUtil.forName(className + BUILDER_CLASS_POSTFIX);
+                cl = _ClassUtil.forName(className + BUILDER_CLASS_POSTFIX_1);
                 clIsBuilderClass = true;
-            } catch (ClassNotFoundException e) {
-                clIsBuilderClass = false;
+            } catch (ClassNotFoundException eIgnored) {
                 try {
-                    cl = _ClassUtil.forName(className);
-                } catch (Exception e2) {
-                    boolean failedToGetAsStaticField;
-                    if (canBeStaticField) {
-                        // Try to interpret className as static filed: 
-                        try {
-                            return getStaticFieldValue(className);
-                        } catch (_ObjectBuilderSettingEvaluationException e3) {
-                            // Suppress it
-                            failedToGetAsStaticField = true;
+                    cl = _ClassUtil.forName(className + 
BUILDER_CLASS_POSTFIX_2);
+                    clIsBuilderClass = true;
+                } catch (ClassNotFoundException e) {
+                    clIsBuilderClass = false;
+                    try {
+                        cl = _ClassUtil.forName(className);
+                    } catch (Exception e2) {
+                        boolean failedToGetAsStaticField;
+                        if (canBeStaticField) {
+                            // Try to interpret className as static filed:
+                            try {
+                                return getStaticFieldValue(className);
+                            } catch (_ObjectBuilderSettingEvaluationException 
e3) {
+                                // Suppress it
+                                failedToGetAsStaticField = true;
+                            }
+                        } else {
+                            failedToGetAsStaticField = false;
                         }
-                    } else {
-                        failedToGetAsStaticField = false;
+                        throw new _ObjectBuilderSettingEvaluationException(
+                                "Failed to get class " + 
_StringUtil.jQuote(className)
+                                        + (failedToGetAsStaticField ? " (also 
failed to resolve name as static field)" : "")
+                                        + ".",
+                                e2);
                     }
-                    throw new _ObjectBuilderSettingEvaluationException(
-                            "Failed to get class " + 
_StringUtil.jQuote(className)
-                            + (failedToGetAsStaticField ? " (also failed to 
resolve name as static field)" : "")
-                            + ".",
-                            e2);
                 }
             }
             
@@ -909,17 +914,7 @@ public class _ObjectBuilderSettingEvaluator {
             // Named parameters will set JavaBeans properties:
             setJavaBeanProperties(constructorResult, namedParamNames, 
namedParamValues);
 
-            final Object result;
-            if (clIsBuilderClass) {
-                result = callBuild(constructorResult);
-            } else {
-                if (constructorResult instanceof WriteProtectable) {
-                    ((WriteProtectable) constructorResult).writeProtect();
-                }
-                result = constructorResult;
-            }
-            
-            return result;
+            return clIsBuilderClass ? callBuild(constructorResult) : 
constructorResult;
         }
         
         private Object getStaticFieldValue(String dottedName) throws 
_ObjectBuilderSettingEvaluationException {
@@ -972,7 +967,7 @@ public class _ObjectBuilderSettingEvaluator {
         private Object callConstructor(Class cl)
                 throws _ObjectBuilderSettingEvaluationException {
             if (hasNoParameters()) {
-                // No need to create ObjectWrapper
+                // No need to invoke ObjectWrapper
                 try {
                     return cl.newInstance();
                 } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java 
b/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java
index aaca144..9501185 100644
--- 
a/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java
+++ 
b/src/main/java/org/apache/freemarker/core/_SettingEvaluationEnvironment.java
@@ -53,7 +53,7 @@ public class _SettingEvaluationEnvironment {
 
     public DefaultObjectWrapper getObjectWrapper() {
         if (objectWrapper == null) {
-            objectWrapper = new 
DefaultObjectWrapper(Configuration.VERSION_3_0_0);
+            objectWrapper = new 
DefaultObjectWrapper.Builder(Configuration.VERSION_3_0_0).build();
         }
         return objectWrapper;
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
 
b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
index fbee3b1..8f83eca 100644
--- 
a/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
+++ 
b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java
@@ -41,7 +41,6 @@ import org.apache.freemarker.core.model.TemplateHashModelEx;
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.TemplateModelException;
 import org.apache.freemarker.core.model.impl.DefaultObjectWrapper;
-import org.apache.freemarker.core.model.impl.DefaultObjectWrapperBuilder;
 import org.apache.freemarker.core.model.impl.SimpleCollection;
 import org.apache.freemarker.core.model.impl.SimpleScalar;
 import org.apache.freemarker.core.util.UndeclaredThrowableException;
@@ -57,7 +56,7 @@ class RmiDebuggedEnvironmentImpl extends RmiDebugModelImpl 
implements DebuggedEn
     private static long nextId = 1;
     private static Set remotes = new HashSet();
 
-    private static final DefaultObjectWrapper OBJECT_WRAPPER = new 
DefaultObjectWrapperBuilder(Configuration
+    private static final DefaultObjectWrapper OBJECT_WRAPPER = new 
DefaultObjectWrapper.Builder(Configuration
             .VERSION_3_0_0)
             .build();
     

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerListenerImpl.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerListenerImpl.java 
b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerListenerImpl.java
index 7e263d8..28985ec 100644
--- 
a/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerListenerImpl.java
+++ 
b/src/main/java/org/apache/freemarker/core/debug/RmiDebuggerListenerImpl.java
@@ -31,7 +31,7 @@ import 
org.apache.freemarker.core.debug.EnvironmentSuspendedEvent;
 import org.slf4j.Logger;
 
 /**
- * Used by the {@link DebuggerClient} to create local 
+ * Used by the {@link DebuggerClient} to invoke local
  */
 class RmiDebuggerListenerImpl
 extends

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java 
b/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java
index d1d3cc0..2517d22 100644
--- a/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java
+++ b/src/main/java/org/apache/freemarker/core/model/TemplateMethodModelEx.java
@@ -26,7 +26,7 @@ import org.apache.freemarker.core.util.DeepUnwrap;
 
 /**
  * "extended method" template language data type: Objects that act like 
functions. Their main application is calling
- * Java methods via {@link 
org.apache.freemarker.core.model.impl.DefaultObjectWrapper}, but you can 
implement this interface to create
+ * Java methods via {@link 
org.apache.freemarker.core.model.impl.DefaultObjectWrapper}, but you can 
implement this interface to invoke
  * top-level functions too. They are "extended" compared to the deprecated 
{@link TemplateMethodModel}, which could only
  * accept string parameters.
  * 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java 
b/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
index 556660b..d630752 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java
@@ -114,7 +114,7 @@ public class BeanModel
      * (Side-note: this also implies that any class whose method has been 
called
      * will be strongly referred to by the framework and will not become
      * unloadable until this class has been unloaded first. Normally this is 
not
-     * an issue, but can be in a rare scenario where you create many classes 
on-
+     * an issue, but can be in a rare scenario where you invoke many classes 
on-
      * the-fly. Also, as the cache grows with new classes and methods 
introduced
      * to the framework, it may appear as if it were leaking memory. The
      * framework does, however detect class reloads (if you happen to be in an
@@ -137,29 +137,11 @@ public class BeanModel
         TemplateModel retval = null;
         
         try {
-            if (wrapper.isMethodsShadowItems()) {
-                Object fd = classInfo.get(key);
-                if (fd != null) {
-                    retval = invokeThroughDescriptor(fd, classInfo);
-                } else {
-                    retval = invokeGenericGet(classInfo, clazz, key);
-                }
+            Object fd = classInfo.get(key);
+            if (fd != null) {
+                retval = invokeThroughDescriptor(fd, classInfo);
             } else {
-                TemplateModel model = invokeGenericGet(classInfo, clazz, key);
-                final TemplateModel nullModel = wrapper.wrap(null);
-                if (model != nullModel && model != UNKNOWN) {
-                    return model;
-                }
-                Object fd = classInfo.get(key);
-                if (fd != null) {
-                    retval = invokeThroughDescriptor(fd, classInfo);
-                    if (retval == UNKNOWN && model == nullModel) {
-                        // This is the (somewhat subtle) case where the 
generic get() returns null
-                        // and we have no bean info, so we respect the fact 
that
-                        // the generic get() returns null and return null. (JR)
-                        retval = nullModel;
-                    }
-                }
+                retval = invokeGenericGet(classInfo, clazz, key);
             }
             if (retval == UNKNOWN) {
                 if (wrapper.isStrict()) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java 
b/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java
index c19de75..303672a 100644
--- a/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java
+++ b/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java
@@ -47,8 +47,10 @@ import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
+import org.apache.freemarker.core.Version;
 import org.apache.freemarker.core._CoreLogs;
 import org.apache.freemarker.core.util.BugException;
+import org.apache.freemarker.core.util.BuilderBase;
 import org.apache.freemarker.core.util._JavaVersions;
 import org.apache.freemarker.core.util._NullArgumentException;
 import org.slf4j.Logger;
@@ -161,17 +163,17 @@ class ClassIntrospector {
      * @param pa
      *            Stores what the values of the JavaBean properties of the 
returned instance will be. Not {@code null}.
      */
-    ClassIntrospector(ClassIntrospectorBuilder pa, Object sharedLock) {
+    ClassIntrospector(Builder pa, Object sharedLock) {
         this(pa, sharedLock, false, false);
     }
 
     /**
      * @param hasSharedInstanceRestrictons
-     *            {@code true} exactly if we are creating a new instance with 
{@link ClassIntrospectorBuilder}. Then
+     *            {@code true} exactly if we are creating a new instance with 
{@link Builder}. Then
      *            it's {@code true} even if it won't put the instance into the 
cache.
      */
-    ClassIntrospector(ClassIntrospectorBuilder builder, Object sharedLock,
-            boolean hasSharedInstanceRestrictons, boolean shared) {
+    ClassIntrospector(Builder builder, Object sharedLock,
+                      boolean hasSharedInstanceRestrictons, boolean shared) {
         _NullArgumentException.check("sharedLock", sharedLock);
 
         exposureLevel = builder.getExposureLevel();
@@ -190,11 +192,11 @@ class ClassIntrospector {
     }
 
     /**
-     * Returns a {@link ClassIntrospectorBuilder}-s that could be used to 
create an identical {@link #ClassIntrospector}
-     * . The returned {@link ClassIntrospectorBuilder} can be modified without 
interfering with anything.
+     * Returns a {@link Builder}-s that could be used to invoke an identical 
{@link #ClassIntrospector}
+     * . The returned {@link Builder} can be modified without interfering with 
anything.
      */
-    ClassIntrospectorBuilder createBuilder() {
-        return new ClassIntrospectorBuilder(this);
+    Builder createBuilder() {
+        return new Builder(this);
     }
 
     // 
------------------------------------------------------------------------------------------------------------------
@@ -1037,7 +1039,7 @@ class ClassIntrospector {
     }
 
     /**
-     * Returns {@code true} if this instance was created with {@link 
ClassIntrospectorBuilder}, even if it wasn't
+     * Returns {@code true} if this instance was created with {@link Builder}, 
even if it wasn't
      * actually put into the cache (as we reserve the right to do so in later 
versions).
      */
     boolean getHasSharedInstanceRestrictons() {
@@ -1071,4 +1073,164 @@ class ClassIntrospector {
         }
     }
 
+    static final class Builder extends BuilderBase<ClassIntrospector, Builder> 
implements Cloneable {
+
+        private static final Map/*<PropertyAssignments, 
Reference<ClassIntrospector>>*/ INSTANCE_CACHE = new HashMap();
+        private static final ReferenceQueue INSTANCE_CACHE_REF_QUEUE = new 
ReferenceQueue();
+
+        // Properties and their *defaults*:
+        private int exposureLevel = DefaultObjectWrapper.EXPOSE_SAFE;
+        private boolean exposeFields;
+        private MethodAppearanceFineTuner methodAppearanceFineTuner;
+        private MethodSorter methodSorter;
+        // Attention:
+        // - This is also used as a cache key, so non-normalized field values 
should be avoided.
+        // - If some field has a default value, it must be set until the end 
of the constructor. No field that has a
+        //   default can be left unset (like null).
+        // - If you add a new field, review all methods in this class, also 
the ClassIntrospector constructor
+
+        Builder(ClassIntrospector ci) {
+            exposureLevel = ci.exposureLevel;
+            exposeFields = ci.exposeFields;
+            methodAppearanceFineTuner = ci.methodAppearanceFineTuner;
+            methodSorter = ci.methodSorter;
+        }
+
+        Builder(Version incompatibleImprovements) {
+            // Warning: incompatibleImprovements must not affect this object 
at versions increments where there's no
+            // change in the 
DefaultObjectWrapper.normalizeIncompatibleImprovements results. That is, this 
class may don't react
+            // to some version changes that affects DefaultObjectWrapper, but 
not the other way around.
+            _NullArgumentException.check(incompatibleImprovements);
+            // Currently nothing depends on incompatibleImprovements
+        }
+
+        @Override
+        protected Object clone() {
+            try {
+                return super.clone();
+            } catch (CloneNotSupportedException e) {
+                throw new RuntimeException("Failed to deepClone Builder", e);
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + (exposeFields ? 1231 : 1237);
+            result = prime * result + exposureLevel;
+            result = prime * result + 
System.identityHashCode(methodAppearanceFineTuner);
+            result = prime * result + System.identityHashCode(methodSorter);
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (obj == null) return false;
+            if (getClass() != obj.getClass()) return false;
+            Builder other = (Builder) obj;
+
+            if (exposeFields != other.exposeFields) return false;
+            if (exposureLevel != other.exposureLevel) return false;
+            if (methodAppearanceFineTuner != other.methodAppearanceFineTuner) 
return false;
+            return methodSorter == other.methodSorter;
+        }
+
+        public int getExposureLevel() {
+            return exposureLevel;
+        }
+
+        /** See {@link DefaultObjectWrapper#setExposureLevel(int)}. */
+        public void setExposureLevel(int exposureLevel) {
+            if (exposureLevel < DefaultObjectWrapper.EXPOSE_ALL || 
exposureLevel > DefaultObjectWrapper.EXPOSE_NOTHING) {
+                throw new IllegalArgumentException("Illegal exposure level: " 
+ exposureLevel);
+            }
+
+            this.exposureLevel = exposureLevel;
+        }
+
+        public boolean getExposeFields() {
+            return exposeFields;
+        }
+
+        /** See {@link DefaultObjectWrapper#setExposeFields(boolean)}. */
+        public void setExposeFields(boolean exposeFields) {
+            this.exposeFields = exposeFields;
+        }
+
+        public MethodAppearanceFineTuner getMethodAppearanceFineTuner() {
+            return methodAppearanceFineTuner;
+        }
+
+        public void setMethodAppearanceFineTuner(MethodAppearanceFineTuner 
methodAppearanceFineTuner) {
+            this.methodAppearanceFineTuner = methodAppearanceFineTuner;
+        }
+
+        public MethodSorter getMethodSorter() {
+            return methodSorter;
+        }
+
+        public void setMethodSorter(MethodSorter methodSorter) {
+            this.methodSorter = methodSorter;
+        }
+
+        private static void removeClearedReferencesFromInstanceCache() {
+            Reference clearedRef;
+            while ((clearedRef = INSTANCE_CACHE_REF_QUEUE.poll()) != null) {
+                synchronized (INSTANCE_CACHE) {
+                    findClearedRef: for (Iterator it = 
INSTANCE_CACHE.values().iterator(); it.hasNext(); ) {
+                        if (it.next() == clearedRef) {
+                            it.remove();
+                            break findClearedRef;
+                        }
+                    }
+                }
+            }
+        }
+
+        /** For unit testing only */
+        static void clearInstanceCache() {
+            synchronized (INSTANCE_CACHE) {
+                INSTANCE_CACHE.clear();
+            }
+        }
+
+        /** For unit testing only */
+        static Map getInstanceCache() {
+            return INSTANCE_CACHE;
+        }
+
+        /**
+         * Returns an instance that is possibly shared (singleton). Note that 
this comes with its own "shared lock",
+         * since everyone who uses this object will have to lock with that 
common object.
+         */
+        @Override
+        public ClassIntrospector build() {
+            if ((methodAppearanceFineTuner == null || 
methodAppearanceFineTuner instanceof SingletonCustomizer)
+                    && (methodSorter == null || methodSorter instanceof 
SingletonCustomizer)) {
+                // Instance can be cached.
+                ClassIntrospector instance;
+                synchronized (INSTANCE_CACHE) {
+                    Reference instanceRef = (Reference) 
INSTANCE_CACHE.get(this);
+                    instance = instanceRef != null ? (ClassIntrospector) 
instanceRef.get() : null;
+                    if (instance == null) {
+                        Builder thisClone = (Builder) clone();  // prevent any 
aliasing issues
+                        instance = new ClassIntrospector(thisClone, new 
Object(), true, true);
+                        INSTANCE_CACHE.put(thisClone, new 
WeakReference(instance, INSTANCE_CACHE_REF_QUEUE));
+                    }
+                }
+
+                removeClearedReferencesFromInstanceCache();
+
+                return instance;
+            } else {
+                // If methodAppearanceFineTuner or methodSorter is specified 
and isn't marked as a singleton, the
+                // ClassIntrospector can't be shared/cached as those objects 
could contain a back-reference to the
+                // DefaultObjectWrapper.
+                return new ClassIntrospector(this, new Object(), true, false);
+            }
+        }
+
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/dceec32e/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospectorBuilder.java
----------------------------------------------------------------------
diff --git 
a/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospectorBuilder.java
 
b/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospectorBuilder.java
deleted file mode 100644
index 4a26a9c..0000000
--- 
a/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospectorBuilder.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * 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.freemarker.core.model.impl;
-
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-import org.apache.freemarker.core.Version;
-import org.apache.freemarker.core.util._NullArgumentException;
-
-final class ClassIntrospectorBuilder implements Cloneable {
-
-    private static final Map/*<PropertyAssignments, 
Reference<ClassIntrospector>>*/ INSTANCE_CACHE = new HashMap();
-    private static final ReferenceQueue INSTANCE_CACHE_REF_QUEUE = new 
ReferenceQueue(); 
-    
-    // Properties and their *defaults*:
-    private int exposureLevel = DefaultObjectWrapper.EXPOSE_SAFE;
-    private boolean exposeFields;
-    private MethodAppearanceFineTuner methodAppearanceFineTuner;
-    private MethodSorter methodSorter;
-    // Attention:
-    // - This is also used as a cache key, so non-normalized field values 
should be avoided.
-    // - If some field has a default value, it must be set until the end of 
the constructor. No field that has a
-    //   default can be left unset (like null).
-    // - If you add a new field, review all methods in this class, also the 
ClassIntrospector constructor
-    
-    ClassIntrospectorBuilder(ClassIntrospector ci) {
-        exposureLevel = ci.exposureLevel;
-        exposeFields = ci.exposeFields;
-        methodAppearanceFineTuner = ci.methodAppearanceFineTuner;
-        methodSorter = ci.methodSorter; 
-    }
-    
-    ClassIntrospectorBuilder(Version incompatibleImprovements) {
-        // Warning: incompatibleImprovements must not affect this object at 
versions increments where there's no
-        // change in the 
DefaultObjectWrapper.normalizeIncompatibleImprovements results. That is, this 
class may don't react
-        // to some version changes that affects DefaultObjectWrapper, but not 
the other way around.
-        _NullArgumentException.check(incompatibleImprovements);
-        // Currently nothing depends on incompatibleImprovements
-    }
-    
-    @Override
-    protected Object clone() {
-        try {
-            return super.clone();
-        } catch (CloneNotSupportedException e) {
-            throw new RuntimeException("Failed to clone 
ClassIntrospectorBuilder", e);
-        }
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + (exposeFields ? 1231 : 1237);
-        result = prime * result + exposureLevel;
-        result = prime * result + 
System.identityHashCode(methodAppearanceFineTuner);
-        result = prime * result + System.identityHashCode(methodSorter);
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        ClassIntrospectorBuilder other = (ClassIntrospectorBuilder) obj;
-        
-        if (exposeFields != other.exposeFields) return false;
-        if (exposureLevel != other.exposureLevel) return false;
-        if (methodAppearanceFineTuner != other.methodAppearanceFineTuner) 
return false;
-        return methodSorter == other.methodSorter;
-    }
-    
-    public int getExposureLevel() {
-        return exposureLevel;
-    }
-
-    /** See {@link DefaultObjectWrapper#setExposureLevel(int)}. */
-    public void setExposureLevel(int exposureLevel) {
-        if (exposureLevel < DefaultObjectWrapper.EXPOSE_ALL || exposureLevel > 
DefaultObjectWrapper.EXPOSE_NOTHING) {
-            throw new IllegalArgumentException("Illegal exposure level: " + 
exposureLevel);
-        }
-        
-        this.exposureLevel = exposureLevel;
-    }
-
-    public boolean getExposeFields() {
-        return exposeFields;
-    }
-
-    /** See {@link DefaultObjectWrapper#setExposeFields(boolean)}. */
-    public void setExposeFields(boolean exposeFields) {
-        this.exposeFields = exposeFields;
-    }
-
-    public MethodAppearanceFineTuner getMethodAppearanceFineTuner() {
-        return methodAppearanceFineTuner;
-    }
-
-    public void setMethodAppearanceFineTuner(MethodAppearanceFineTuner 
methodAppearanceFineTuner) {
-        this.methodAppearanceFineTuner = methodAppearanceFineTuner;
-    }
-
-    public MethodSorter getMethodSorter() {
-        return methodSorter;
-    }
-
-    public void setMethodSorter(MethodSorter methodSorter) {
-        this.methodSorter = methodSorter;
-    }
-
-    private static void removeClearedReferencesFromInstanceCache() {
-        Reference clearedRef;
-        while ((clearedRef = INSTANCE_CACHE_REF_QUEUE.poll()) != null) {
-            synchronized (INSTANCE_CACHE) {
-                findClearedRef: for (Iterator it = 
INSTANCE_CACHE.values().iterator(); it.hasNext(); ) {
-                    if (it.next() == clearedRef) {
-                        it.remove();
-                        break findClearedRef;
-                    }
-                }
-            }
-        }
-    }
-
-    /** For unit testing only */
-    static void clearInstanceCache() {
-        synchronized (INSTANCE_CACHE) {
-            INSTANCE_CACHE.clear();
-        }
-    }
-    
-    /** For unit testing only */
-    static Map getInstanceCache() {
-        return INSTANCE_CACHE;
-    }
-
-    /**
-     * Returns an instance that is possibly shared (singleton). Note that this 
comes with its own "shared lock",
-     * since everyone who uses this object will have to lock with that common 
object.
-     */
-    ClassIntrospector build() {
-        if ((methodAppearanceFineTuner == null || methodAppearanceFineTuner 
instanceof SingletonCustomizer)
-                && (methodSorter == null || methodSorter instanceof 
SingletonCustomizer)) {
-            // Instance can be cached.
-            ClassIntrospector instance;
-            synchronized (INSTANCE_CACHE) {
-                Reference instanceRef = (Reference) INSTANCE_CACHE.get(this);
-                instance = instanceRef != null ? (ClassIntrospector) 
instanceRef.get() : null;
-                if (instance == null) {
-                    ClassIntrospectorBuilder thisClone = 
(ClassIntrospectorBuilder) clone();  // prevent any aliasing issues
-                    instance = new ClassIntrospector(thisClone, new Object(), 
true, true);
-                    INSTANCE_CACHE.put(thisClone, new WeakReference(instance, 
INSTANCE_CACHE_REF_QUEUE));
-                }
-            }
-            
-            removeClearedReferencesFromInstanceCache();
-            
-            return instance;
-        } else {
-            // If methodAppearanceFineTuner or methodSorter is specified and 
isn't marked as a singleton, the
-            // ClassIntrospector can't be shared/cached as those objects could 
contain a back-reference to the
-            // DefaultObjectWrapper.
-            return new ClassIntrospector(this, new Object(), true, false);
-        }
-    }
-    
-}
\ No newline at end of file

Reply via email to