2021SC@SDUSC
FreeMarker代码分析第十三篇
beans包
SetAdapter.java
代码分析
/*
* 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 java.util.Set;
import freemarker.template.TemplateCollectionModel;
/**
*/
class SetAdapter extends CollectionAdapter implements Set {
SetAdapter(TemplateCollectionModel model, BeansWrapper wrapper) {
super(model, wrapper);
}
}
SimpleMapModel.java
代码分析
/*
* 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 java.util.List;
import java.util.Map;
import freemarker.core.CollectionAndSequence;
import freemarker.ext.util.ModelFactory;
import freemarker.ext.util.WrapperTemplateModel;
import freemarker.template.AdapterTemplateModel;
import freemarker.template.MapKeyValuePairIterator;
import freemarker.template.ObjectWrapper;
import freemarker.template.SimpleSequence;
import freemarker.template.TemplateCollectionModel;
import freemarker.template.TemplateHashModelEx2;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateModelWithAPISupport;
import freemarker.template.WrappingTemplateModel;
import freemarker.template.utility.RichObjectWrapper;
/**
* Model used by {@link BeansWrapper} when <tt>simpleMapWrapper</tt>
* mode is enabled. Provides a simple hash model interface to the
* underlying map (does not copy like {@link freemarker.template.SimpleHash}),
* and a method interface to non-string keys.
*/
public class SimpleMapModel extends WrappingTemplateModel
implements TemplateHashModelEx2, TemplateMethodModelEx, AdapterTemplateModel,
WrapperTemplateModel, TemplateModelWithAPISupport {
static final ModelFactory FACTORY =
new ModelFactory()
{
@Override
public TemplateModel create(Object object, ObjectWrapper wrapper) {
return new SimpleMapModel((Map) object, (BeansWrapper) wrapper);
}
};
private final Map map;
public SimpleMapModel(Map map, BeansWrapper wrapper) {
super(wrapper);
this.map = map;
}
@Override
public TemplateModel get(String key) throws TemplateModelException {
Object val = map.get(key);
if (val == null) {
if (key.length() == 1) {
// just check for Character key if this is a single-character string
Character charKey = Character.valueOf(key.charAt(0));
val = map.get(charKey);
if (val == null && !(map.containsKey(key) || map.containsKey(charKey))) {
return null;
}
} else if (!map.containsKey(key)) {
return null;
}
}
return wrap(val);
}
@Override
public Object exec(List args) throws TemplateModelException {
Object key = ((BeansWrapper) getObjectWrapper()).unwrap((TemplateModel) args.get(0));
Object value = map.get(key);
if (value == null && !map.containsKey(key)) {
return null;
}
return wrap(value);
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public int size() {
return map.size();
}
@Override
public TemplateCollectionModel keys() {
return new CollectionAndSequence(new SimpleSequence(map.keySet(), getObjectWrapper()));
}
@Override
public TemplateCollectionModel values() {
return new CollectionAndSequence(new SimpleSequence(map.values(), getObjectWrapper()));
}
@Override
public KeyValuePairIterator keyValuePairIterator() {
return new MapKeyValuePairIterator(map, getObjectWrapper());
}
@Override
public Object getAdaptedObject(Class hint) {
return map;
}
@Override
public Object getWrappedObject() {
return map;
}
@Override
public TemplateModel getAPI() throws TemplateModelException {
return ((RichObjectWrapper) getObjectWrapper()).wrapAsAPI(map);
}
}
SimpleMethod.java
代码分析
/*
* 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 java.lang.reflect.Array;
import java.lang.reflect.Member;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import freemarker.core.TemplateMarkupOutputModel;
import freemarker.core._DelayedFTLTypeDescription;
import freemarker.core._DelayedOrdinal;
import freemarker.core._ErrorDescriptionBuilder;
import freemarker.core._TemplateModelException;
import freemarker.template.ObjectWrapperAndUnwrapper;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.utility.ClassUtil;
/**
* This class is used for as a base for non-overloaded method models and for constructors.
* (For overloaded methods and constructors see {@link OverloadedMethods}.)
*/
class SimpleMethod {
static final String MARKUP_OUTPUT_TO_STRING_TIP
= "A markup output value can be converted to markup string like value?markup_string. "
+ "But consider if the Java method whose argument it will be can handle markup strings properly.";
private final Member member;
private final Class[] argTypes;
protected SimpleMethod(Member member, Class[] argTypes) {
this.member = member;
this.argTypes = argTypes;
}
Object[] unwrapArguments(List arguments, BeansWrapper wrapper) throws TemplateModelException {
if (arguments == null) {
arguments = Collections.EMPTY_LIST;
}
boolean isVarArg = _MethodUtil.isVarargs(member);
int typesLen = argTypes.length;
if (isVarArg) {
if (typesLen - 1 > arguments.size()) {
throw new _TemplateModelException(
_MethodUtil.invocationErrorMessageStart(member),
" takes at least ", Integer.valueOf(typesLen - 1),
typesLen - 1 == 1 ? " argument" : " arguments", ", but ",
Integer.valueOf(arguments.size()), " was given.");
}
} else if (typesLen != arguments.size()) {
throw new _TemplateModelException(
_MethodUtil.invocationErrorMessageStart(member),
" takes ", Integer.valueOf(typesLen), typesLen == 1 ? " argument" : " arguments", ", but ",
Integer.valueOf(arguments.size()), " was given.");
}
Object[] args = unwrapArguments(arguments, argTypes, isVarArg, wrapper);
return args;
}
private Object[] unwrapArguments(List args, Class[] argTypes, boolean isVarargs,
BeansWrapper w)
throws TemplateModelException {
if (args == null) return null;
int typesLen = argTypes.length;
int argsLen = args.size();
Object[] unwrappedArgs = new Object[typesLen];
// Unwrap arguments:
Iterator it = args.iterator();
int normalArgCnt = isVarargs ? typesLen - 1 : typesLen;
int argIdx = 0;
while (argIdx < normalArgCnt) {
Class argType = argTypes[argIdx];
TemplateModel argVal = (TemplateModel) it.next();
Object unwrappedArgVal = w.tryUnwrapTo(argVal, argType);
if (unwrappedArgVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
throw createArgumentTypeMismarchException(argIdx, argVal, argType);
}
if (unwrappedArgVal == null && argType.isPrimitive()) {
throw createNullToPrimitiveArgumentException(argIdx, argType);
}
unwrappedArgs[argIdx++] = unwrappedArgVal;
}
if (isVarargs) {
// The last argType, which is the vararg type, wasn't processed yet.
Class varargType = argTypes[typesLen - 1];
Class varargItemType = varargType.getComponentType();
if (!it.hasNext()) {
unwrappedArgs[argIdx++] = Array.newInstance(varargItemType, 0);
} else {
TemplateModel argVal = (TemplateModel) it.next();
Object unwrappedArgVal;
// We first try to treat the last argument as a vararg *array*.
// This is consistent to what OverloadedVarArgMethod does.
if (argsLen - argIdx == 1
&& (unwrappedArgVal = w.tryUnwrapTo(argVal, varargType))
!= ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
// It was a vararg array.
unwrappedArgs[argIdx++] = unwrappedArgVal;
} else {
// It wasn't a vararg array, so we assume it's a vararg
// array *item*, possibly followed by further ones.
int varargArrayLen = argsLen - argIdx;
Object varargArray = Array.newInstance(varargItemType, varargArrayLen);
for (int varargIdx = 0; varargIdx < varargArrayLen; varargIdx++) {
TemplateModel varargVal = (TemplateModel) (varargIdx == 0 ? argVal : it.next());
Object unwrappedVarargVal = w.tryUnwrapTo(varargVal, varargItemType);
if (unwrappedVarargVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
throw createArgumentTypeMismarchException(
argIdx + varargIdx, varargVal, varargItemType);
}
if (unwrappedVarargVal == null && varargItemType.isPrimitive()) {
throw createNullToPrimitiveArgumentException(argIdx + varargIdx, varargItemType);
}
Array.set(varargArray, varargIdx, unwrappedVarargVal);
}
unwrappedArgs[argIdx++] = varargArray;
}
}
}
return unwrappedArgs;
}
private TemplateModelException createArgumentTypeMismarchException(
int argIdx, TemplateModel argVal, Class targetType) {
_ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
_MethodUtil.invocationErrorMessageStart(member), " couldn't be called: Can't convert the ",
new _DelayedOrdinal(Integer.valueOf(argIdx + 1)),
" argument's value to the target Java type, ", ClassUtil.getShortClassName(targetType),
". The type of the actual value was: ", new _DelayedFTLTypeDescription(argVal));
if (argVal instanceof TemplateMarkupOutputModel && (targetType.isAssignableFrom(String.class))) {
desc.tip(MARKUP_OUTPUT_TO_STRING_TIP);
}
return new _TemplateModelException(desc);
}
private TemplateModelException createNullToPrimitiveArgumentException(int argIdx, Class targetType) {
return new _TemplateModelException(
_MethodUtil.invocationErrorMessageStart(member), " couldn't be called: The value of the ",
new _DelayedOrdinal(Integer.valueOf(argIdx + 1)),
" argument was null, but the target Java parameter type (", ClassUtil.getShortClassName(targetType),
") is primitive and so can't store null.");
}
protected Member getMember() {
return member;
}
}