/*
 * 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 freemarker.ext.beans;

import freemarker.template.DefaultObjectWrapperBuilder;
import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateDateModel;
import freemarker.template.Version;
import freemarker.template._TemplateAPI;

Holds BeansWrapper configuration settings and defines their defaults. You will not use this abstract class directly, but concrete subclasses like BeansWrapperBuilder and DefaultObjectWrapperBuilder. Unless, you are developing a builder for a custom BeansWrapper subclass.

This class is designed so that its instances can be used as lookup keys in a singleton cache. This is also why this class defines the configuration setting defaults for BeansWrapper, instead of leaving that to BeansWrapper itself. (Because, the default values influence the lookup key, and the singleton needs to be looked up without creating a BeansWrapper instance.) However, because instances are mutable, you should deep-clone it with clone(boolean) before using it as cache key.

Since:2.3.21
/** * Holds {@link BeansWrapper} configuration settings and defines their defaults. * You will not use this abstract class directly, but concrete subclasses like {@link BeansWrapperBuilder} and * {@link DefaultObjectWrapperBuilder}. Unless, you are developing a builder for a custom {@link BeansWrapper} subclass. * * <p>This class is designed so that its instances can be used as lookup keys in a singleton cache. This is also why * this class defines the configuration setting defaults for {@link BeansWrapper}, instead of leaving that to * {@link BeansWrapper} itself. (Because, the default values influence the lookup key, and the singleton needs to be * looked up without creating a {@link BeansWrapper} instance.) However, because instances are mutable, you should * deep-clone it with {@link #clone(boolean)} before using it as cache key. * * @since 2.3.21 */
public abstract class BeansWrapperConfiguration implements Cloneable { private final Version incompatibleImprovements; private ClassIntrospectorBuilder classIntrospectorBuilder; // Properties and their *defaults*: private boolean simpleMapWrapper = false; private boolean preferIndexedReadMethod; private int defaultDateType = TemplateDateModel.UNKNOWN; private ObjectWrapper outerIdentity = null; private boolean strict = false; private boolean useModelCache = false; // Attention! // - As this object is a cache key, non-normalized field values should be avoided. // - Fields with default values must be set until the end of the constructor to ensure that when the lookup happens, // there will be no unset fields. // - If you add a new field, review all methods in this class
Params:
  • incompatibleImprovements – See the corresponding parameter of BeansWrapper(Version). Not null. Note that the version will be normalized to the lowest version where the same incompatible BeansWrapper improvements were already present, so for the returned instance getIncompatibleImprovements() might returns a lower version than what you have specified here.
  • isIncompImprsAlreadyNormalized – Tells if the incompatibleImprovements parameter contains an already normalized value. This parameter meant to be true when the class that extends BeansWrapper needs to add additional breaking versions over those of BeansWrapper. Thus, if this parameter is true, the versions where BeansWrapper had breaking changes must be already factored into the incompatibleImprovements parameter value, as no more normalization will happen. (You can use BeansWrapper.normalizeIncompatibleImprovementsVersion(Version) to discover those.)
Since:2.3.22
/** * @param incompatibleImprovements * See the corresponding parameter of {@link BeansWrapper#BeansWrapper(Version)}. Not {@code null}. Note * that the version will be normalized to the lowest version where the same incompatible * {@link BeansWrapper} improvements were already present, so for the returned instance * {@link #getIncompatibleImprovements()} might returns a lower version than what you have specified * here. * @param isIncompImprsAlreadyNormalized * Tells if the {@code incompatibleImprovements} parameter contains an <em>already normalized</em> value. * This parameter meant to be {@code true} when the class that extends {@link BeansWrapper} needs to add * additional breaking versions over those of {@link BeansWrapper}. Thus, if this parameter is * {@code true}, the versions where {@link BeansWrapper} had breaking changes must be already factored * into the {@code incompatibleImprovements} parameter value, as no more normalization will happen. (You * can use {@link BeansWrapper#normalizeIncompatibleImprovementsVersion(Version)} to discover those.) * * @since 2.3.22 */
protected BeansWrapperConfiguration(Version incompatibleImprovements, boolean isIncompImprsAlreadyNormalized) { _TemplateAPI.checkVersionNotNullAndSupported(incompatibleImprovements); incompatibleImprovements = isIncompImprsAlreadyNormalized ? incompatibleImprovements : BeansWrapper.normalizeIncompatibleImprovementsVersion(incompatibleImprovements); this.incompatibleImprovements = incompatibleImprovements; preferIndexedReadMethod = incompatibleImprovements.intValue() < _TemplateAPI.VERSION_INT_2_3_27; classIntrospectorBuilder = new ClassIntrospectorBuilder(incompatibleImprovements); } /** * Same as {@link #BeansWrapperConfiguration(Version, boolean) BeansWrapperConfiguration(Version, false)}. */ protected BeansWrapperConfiguration(Version incompatibleImprovements) { this(incompatibleImprovements, false); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + incompatibleImprovements.hashCode(); result = prime * result + (simpleMapWrapper ? 1231 : 1237); result = prime * result + (preferIndexedReadMethod ? 1231 : 1237); result = prime * result + defaultDateType; result = prime * result + (outerIdentity != null ? outerIdentity.hashCode() : 0); result = prime * result + (strict ? 1231 : 1237); result = prime * result + (useModelCache ? 1231 : 1237); result = prime * result + classIntrospectorBuilder.hashCode(); return result; }
Two BeansWrapperConfiguration-s are equal exactly if their classes are identical (==), and their field values are equal.
/** * Two {@link BeansWrapperConfiguration}-s are equal exactly if their classes are identical ({@code ==}), and their * field values are equal. */
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; BeansWrapperConfiguration other = (BeansWrapperConfiguration) obj; if (!incompatibleImprovements.equals(other.incompatibleImprovements)) return false; if (simpleMapWrapper != other.simpleMapWrapper) return false; if (preferIndexedReadMethod != other.preferIndexedReadMethod) return false; if (defaultDateType != other.defaultDateType) return false; if (outerIdentity != other.outerIdentity) return false; if (strict != other.strict) return false; if (useModelCache != other.useModelCache) return false; if (!classIntrospectorBuilder.equals(other.classIntrospectorBuilder)) return false; return true; } protected Object clone(boolean deepCloneKey) { try { BeansWrapperConfiguration clone = (BeansWrapperConfiguration) super.clone(); if (deepCloneKey) { clone.classIntrospectorBuilder = (ClassIntrospectorBuilder) classIntrospectorBuilder.clone(); } return clone; } catch (CloneNotSupportedException e) { throw new RuntimeException("Failed to clone BeansWrapperConfiguration", e); } } public boolean isSimpleMapWrapper() { return simpleMapWrapper; } /** See {@link BeansWrapper#setSimpleMapWrapper(boolean)}. */ public void setSimpleMapWrapper(boolean simpleMapWrapper) { this.simpleMapWrapper = simpleMapWrapper; }
Since:2.3.27
/** @since 2.3.27 */
public boolean getPreferIndexedReadMethod() { return preferIndexedReadMethod; } /** See {@link BeansWrapper#setPreferIndexedReadMethod(boolean)}. @since 2.3.27 */ public void setPreferIndexedReadMethod(boolean preferIndexedReadMethod) { this.preferIndexedReadMethod = preferIndexedReadMethod; } public int getDefaultDateType() { return defaultDateType; } /** See {@link BeansWrapper#setDefaultDateType(int)}. */ public void setDefaultDateType(int defaultDateType) { this.defaultDateType = defaultDateType; } public ObjectWrapper getOuterIdentity() { return outerIdentity; }
See BeansWrapper.setOuterIdentity(ObjectWrapper), except here the default is null that means the ObjectWrapper that you will set up with this BeansWrapperBuilder object.
/** * See {@link BeansWrapper#setOuterIdentity(ObjectWrapper)}, except here the default is {@code null} that means * the {@link ObjectWrapper} that you will set up with this {@link BeansWrapperBuilder} object. */
public void setOuterIdentity(ObjectWrapper outerIdentity) { this.outerIdentity = outerIdentity; } public boolean isStrict() { return strict; } /** See {@link BeansWrapper#setStrict(boolean)}. */ public void setStrict(boolean strict) { this.strict = strict; } public boolean getUseModelCache() { return useModelCache; }
See BeansWrapper.setUseCache(boolean) (it means the same).
/** See {@link BeansWrapper#setUseCache(boolean)} (it means the same). */
public void setUseModelCache(boolean useModelCache) { this.useModelCache = useModelCache; } public Version getIncompatibleImprovements() { return incompatibleImprovements; } public int getExposureLevel() { return classIntrospectorBuilder.getExposureLevel(); } /** See {@link BeansWrapper#setExposureLevel(int)}. */ public void setExposureLevel(int exposureLevel) { classIntrospectorBuilder.setExposureLevel(exposureLevel); } public boolean getExposeFields() { return classIntrospectorBuilder.getExposeFields(); } /** See {@link BeansWrapper#setExposeFields(boolean)}. */ public void setExposeFields(boolean exposeFields) { classIntrospectorBuilder.setExposeFields(exposeFields); } public boolean getTreatDefaultMethodsAsBeanMembers() { return classIntrospectorBuilder.getTreatDefaultMethodsAsBeanMembers(); } /** See {@link BeansWrapper#setTreatDefaultMethodsAsBeanMembers(boolean)} */ public void setTreatDefaultMethodsAsBeanMembers(boolean treatDefaultMethodsAsBeanMembers) { classIntrospectorBuilder.setTreatDefaultMethodsAsBeanMembers(treatDefaultMethodsAsBeanMembers); } public MethodAppearanceFineTuner getMethodAppearanceFineTuner() { return classIntrospectorBuilder.getMethodAppearanceFineTuner(); }
See BeansWrapper.setMethodAppearanceFineTuner(MethodAppearanceFineTuner); additionally, note that currently setting this to non-null will disable class introspection cache sharing, unless the value implements SingletonCustomizer.
/** * See {@link BeansWrapper#setMethodAppearanceFineTuner(MethodAppearanceFineTuner)}; additionally, * note that currently setting this to non-{@code null} will disable class introspection cache sharing, unless * the value implements {@link SingletonCustomizer}. */
public void setMethodAppearanceFineTuner(MethodAppearanceFineTuner methodAppearanceFineTuner) { classIntrospectorBuilder.setMethodAppearanceFineTuner(methodAppearanceFineTuner); } MethodSorter getMethodSorter() { return classIntrospectorBuilder.getMethodSorter(); } void setMethodSorter(MethodSorter methodSorter) { classIntrospectorBuilder.setMethodSorter(methodSorter); } ClassIntrospectorBuilder getClassIntrospectorBuilder() { return classIntrospectorBuilder; } }