| /* |
| * 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; |
| import freemarker.template._VersionInts; |
| |
| /** |
| * 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 |
| |
| /** |
| * @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 return 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); |
| |
| // We can't do this in the BeansWrapper constructor, as by that time the version is normalized. |
| if (!isIncompImprsAlreadyNormalized) { |
| _TemplateAPI.checkCurrentVersionNotRecycled( |
| incompatibleImprovements, |
| "freemarker.beans", "BeansWrapper"); |
| } |
| |
| incompatibleImprovements = isIncompImprsAlreadyNormalized |
| ? incompatibleImprovements |
| : BeansWrapper.normalizeIncompatibleImprovementsVersion(incompatibleImprovements); |
| this.incompatibleImprovements = incompatibleImprovements; |
| |
| preferIndexedReadMethod = incompatibleImprovements.intValue() < _VersionInts.V_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 {@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 */ |
| 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 {@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 {@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 MemberAccessPolicy getMemberAccessPolicy() { |
| return classIntrospectorBuilder.getMemberAccessPolicy(); |
| } |
| |
| /** See {@link BeansWrapper#setMemberAccessPolicy(MemberAccessPolicy)}. */ |
| public void setMemberAccessPolicy(MemberAccessPolicy memberAccessPolicy) { |
| classIntrospectorBuilder.setMemberAccessPolicy(memberAccessPolicy); |
| } |
| |
| 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 {@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; |
| } |
| |
| } |