/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * Licensed 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 woolpack;

import java.beans.PropertyDescriptor;
import java.io.Reader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Pattern;

import junit.framework.TestCase;

import org.w3c.dom.Node;

import woolpack.acquirable.Acquirable;
import woolpack.acquirable.AcquirableChain;
import woolpack.acquirable.AcquireFn;
import woolpack.acquirable.DoLock;
import woolpack.acquirable.DoSemaphore;
import woolpack.acquirable.DoSemaphoreFactory;
import woolpack.acquirable.TryLock;
import woolpack.acquirable.TrySemaphore;
import woolpack.acquirable.TrySemaphoreFactory;
import woolpack.action.ActionDef;
import woolpack.action.ActionInvoker;
import woolpack.action.ActionResult;
import woolpack.action.ForwardDef;
import woolpack.adapter.ConvertableTypeConverter;
import woolpack.adapter.JXE;
import woolpack.adapter.JXP;
import woolpack.adapter.OGE;
import woolpack.bool.And;
import woolpack.bool.BoolSeq;
import woolpack.bool.BoolUtils;
import woolpack.bool.CompareFn;
import woolpack.bool.ContainsAllChecker;
import woolpack.bool.ContainsChecker;
import woolpack.bool.EqualsChecker;
import woolpack.bool.MaxChecker;
import woolpack.bool.MaxLengthChecker;
import woolpack.bool.MinChecker;
import woolpack.bool.MinLengthChecker;
import woolpack.bool.NotDelegator;
import woolpack.bool.ObjectMatcher;
import woolpack.bool.RegExpChecker;
import woolpack.config.ConfigContext;
import woolpack.config.ConfigSetter;
import woolpack.config.PutResourceBundle;
import woolpack.config.ResourceBundleFactory;
import woolpack.config.ToLinkedHashMap;
import woolpack.container.ContainerContext;
import woolpack.convert.CloneFormatFactory;
import woolpack.convert.FormatConverter;
import woolpack.convert.NetMaskConverter;
import woolpack.convert.ParseConverter;
import woolpack.convert.RegExpConverter;
import woolpack.convert.RetainKeys;
import woolpack.convert.TrysFn;
import woolpack.ee.ActionBuilder;
import woolpack.ee.ValidatorBuilder;
import woolpack.el.ArrayPathEL;
import woolpack.el.CachePropertyELFactory;
import woolpack.el.ELUtils;
import woolpack.el.FixEL;
import woolpack.el.GettingEL;
import woolpack.el.MapEL;
import woolpack.el.MapPropertyELFactory;
import woolpack.el.PathEL;
import woolpack.el.PropertyEL;
import woolpack.el.ReflectionPropertyELFactory;
import woolpack.factory.ConcurrentMapCache;
import woolpack.factory.GettingELFn;
import woolpack.factory.InputStreamReaderFactory;
import woolpack.factory.MapCache;
import woolpack.factory.NewInstanceFactory;
import woolpack.factory.SideEffectConcurrentMapCache;
import woolpack.factory.SideEffectMapCache;
import woolpack.factory.StringInputStreamFactory;
import woolpack.factory.StringReaderFactory;
import woolpack.fn.CastFn;
import woolpack.fn.Delegator;
import woolpack.fn.ExecFn;
import woolpack.fn.FixFn;
import woolpack.fn.Fn;
import woolpack.fn.IfFn;
import woolpack.fn.JoinFn;
import woolpack.fn.NullFn;
import woolpack.fn.RecodeFn;
import woolpack.fn.SeqFn;
import woolpack.fn.SwitchFn;
import woolpack.fn.ThrowFn;
import woolpack.fn.TryFn;
import woolpack.html.AutoUpdater;
import woolpack.html.BranchPropertyCounter;
import woolpack.html.HiddenAppender;
import woolpack.html.MergeCell;
import woolpack.html.RadioRemaker;
import woolpack.html.RowAttrConverter;
import woolpack.html.RowIndexInserter;
import woolpack.html.SelectRemaker;
import woolpack.html.SelectedValueUpdater;
import woolpack.html.ValueUpdater;
import woolpack.id.IdContext;
import woolpack.id.IdConverter;
import woolpack.id.LocalId;
import woolpack.misc.FnRunnable;
import woolpack.misc.LapTimeFn;
import woolpack.misc.LoadBalancer;
import woolpack.misc.RunnableFn;
import woolpack.misc.SleepFn;
import woolpack.misc.SwitchNearLocale;
import woolpack.misc.ThreadLocalGetter;
import woolpack.misc.TryLocales;
import woolpack.typeconvert.CollectionConverter;
import woolpack.typeconvert.ConvertContext;
import woolpack.typeconvert.Converter;
import woolpack.typeconvert.DelegationIfNecessityConverter;
import woolpack.typeconvert.ToTypeConverter;
import woolpack.utils.AppendableWriter;
import woolpack.utils.BeanUtils;
import woolpack.utils.DelegationCollection;
import woolpack.utils.DelegationMap;
import woolpack.utils.MapIterableMap;
import woolpack.utils.Utils;
import woolpack.validator.AddressedMessage;
import woolpack.validator.ConvertValidator;
import woolpack.validator.LocalIndexValidator;
import woolpack.validator.LocalKeyValidator;
import woolpack.validator.MessageValidator;
import woolpack.validator.NameBranch;
import woolpack.validator.NameBranchIfExists;
import woolpack.validator.SimpleMessageCollector;
import woolpack.validator.ValidatorContext;
import woolpack.validator.ValueLoopValidator;
import woolpack.visitor.Visitor;
import woolpack.web.ContainerContextSetter;
import woolpack.web.WebContext;
import woolpack.xml.AttrRemover;
import woolpack.xml.AttrValueBranch;
import woolpack.xml.AttrValueGetter;
import woolpack.xml.AttrValueUpdater;
import woolpack.xml.ChildElementInserter;
import woolpack.xml.ChildTextReplacer;
import woolpack.xml.NodeContext;
import woolpack.xml.NodeFactory;
import woolpack.xml.NodeFindableChildNodesImpl;
import woolpack.xml.NodeFindableFactory;
import woolpack.xml.NodeFinder;
import woolpack.xml.NodeSeeker;
import woolpack.xml.NodeSetter;
import woolpack.xml.ParentElementInserter;
import woolpack.xml.TemplateCopier;
import woolpack.xml.TextReplacer;
import woolpack.xml.XmlUtils;

/**
 * コンストラクタとアクセサをテストする。
 * @author nakamura
 *
 */
public class AccessorTest extends TestCase {
	
	/**
	 * 以下の観点をテストする。
	 * コンストラクタすべて null でもよい。
	 * @param o
	 * @throws IllegalArgumentException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	private void checkNullConsructors(final Object o) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
		final Class<?> clazz = o.getClass();
		final Constructor[] array = clazz.getConstructors();
		for(final Constructor c : array) {
			final Class[] paramTypes = c.getParameterTypes();
			final Object[] params = new Object[paramTypes.length];
			for(int i = 0; i < paramTypes.length; i++) {
				final Class paramType = paramTypes[i];
				if (paramType.isPrimitive()) {
					Object param = null;
					if (boolean.class.equals(paramType)) {
						param = false;
					} else if (char.class.equals(paramType)) {
						param = 'c';
					} else if (byte.class.equals(paramType)) {
						param = (byte)0;
					} else if (short.class.equals(paramType)) {
						param = (short)0;
					} else if (int.class.equals(paramType)) {
						param = (int)0;
					} else if (long.class.equals(paramType)) {
						param = (long)0;
					} else if (float.class.equals(paramType)) {
						param = (float)0;
					} else if (double.class.equals(paramType)) {
						param = (double)0;
					}
					params[i] = param;
				}
			}
			c.newInstance(params);
		}
	}
	
	/**
	 * 以下の観点をテストする。
	 * コンストラクタとゲッターメソッドの対応({@link BeanUtils#getConstructorGetterList}で試験される)。
	 * @param o
	 */
	public void checkGC(final Object o) {
		BeanUtils.getConstructorGetterList(o);
	}
	
	/**
	 * 以下の観点をテストする。
	 * コンストラクタとゲッターメソッドの対応({@link BeanUtils#getConstructorGetterList}で試験される)。
	 * ゲッターメソッドに対するセッターメソッドが存在する。
	 * @param o
	 * @throws IllegalArgumentException
	 * @throws SecurityException
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws NoSuchMethodException
	 */
	private void checkSC(final Object o) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		final List<PropertyDescriptor> list = BeanUtils.getConstructorGetterList(o);
		final Object[] params = new Object[list.size()];
		final Class[] paramTypes = new Class[params.length];
		for (int i = 0; i < params.length; i++) {
			final PropertyDescriptor d = list.get(i);
			params[i] = d.getReadMethod().invoke(o, new Object[0]);
			paramTypes[i] = d.getPropertyType();
		}
		final Object o2 = o.getClass().getConstructor(paramTypes).newInstance(params);
		for (int i = 0; i < params.length; i++) {
			final PropertyDescriptor d = list.get(i);
			assertEquals(params[i], d.getReadMethod().invoke(o2, new Object[0]));
			d.getWriteMethod().invoke(o2, new Object[]{params[i]});
			assertEquals(params[i], d.getReadMethod().invoke(o2, new Object[0]));
		}
	}
	
	/**
	 * 以下の観点をテストする。
	 * ゲッターメソッドに対するセッターメソッドが存在する。
	 * 
	 * @param o
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	private void checkS(final Object o) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		final List<PropertyDescriptor> list = BeanUtils.getGetterList(o.getClass());
		for (final PropertyDescriptor d : list) {
			final Object v = d.getReadMethod().invoke(o, new Object[0]);
			d.getWriteMethod().invoke(o, new Object[]{v});
			assertEquals(v, d.getReadMethod().invoke(o, new Object[0]));
		}
	}
	
//	private void checkGCN(final Object o) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
//		checkNullConsructors(o);
//		checkGC(o);
//	}
	
	private void checkSCN(final Object o) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException {
		checkNullConsructors(o);
		checkSC(o);
	}
	
	public void testAccessor() throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		
		// base
		checkSCN(new CastFn<Integer>(Integer.class));
		checkSCN(new Delegator<Integer, String>(new NullFn<Integer, String>()));
		checkSCN(new ExecFn<Integer, String>(
				new NullFn<Integer, Fn<? super Integer, ? extends String>>()));
		checkSCN(new FixFn<Integer, String>("a"));
		checkSCN(new IfFn<Integer, String>(
				new NullFn<Integer, Boolean>(),
				new NullFn<Integer, String>(),
				new NullFn<Integer, String>()
		));
		checkSCN(new JoinFn<Integer, String, Boolean>(
				new NullFn<Integer, String>(),
				new NullFn<String, Boolean>()
		));
		checkSCN(new RecodeFn<Integer, String>(
				new NullFn<Integer, String>(),
				"a",
				new ArrayList<String>(),
				new ArrayList<Integer>(),
				new ArrayList<String>()
		));
		checkSCN(new SeqFn<Integer, String>(Utils
				.list(new NullFn<Integer, String>())
				.list(new NullFn<Integer, String>())
		));
		checkSCN(new SwitchFn<Integer, String>(Utils
				.map(3, "a")
				.map(5, "a"),
				"a"
		));
		checkSCN(new ThrowFn<Integer, String>(new RuntimeException()));
		checkSCN(new TryFn<Integer, String>(
				new NullFn<Integer, String>(),
				new NullFn<Throwable, String>(),
				new NullFn<Integer, String>()
		));
		
		// bool
		checkSCN(new BoolSeq<Integer>(
				new And<Integer>(),
				Utils
				.list(new NullFn<Integer, Boolean>())
				.list(new NullFn<Integer, Boolean>())
		));
		checkSCN(new CompareFn<Integer>(3));
		checkSCN(new ContainsAllChecker(new ArrayList<Integer>()));
		checkSCN(new ContainsChecker(new ArrayList<Integer>()));
		checkSCN(new EqualsChecker(new Object()));
		checkSCN(new MaxChecker<Integer>(3));
		checkSCN(new MaxLengthChecker(3));
		checkSCN(new MinChecker<Integer>(3));
		checkSCN(new MinLengthChecker(3));
		checkSCN(new NotDelegator<Integer>(new NullFn<Integer, Boolean>()));
		checkSCN(new ObjectMatcher(new Object()));
		checkSCN(new RegExpChecker(Pattern.compile(".*")));
		
		// convert
		checkSCN(new CloneFormatFactory(new DecimalFormat()));
		checkSCN(new FormatConverter(new CloneFormatFactory(new DecimalFormat())));
		checkSCN(new NetMaskConverter("255.255.255.0"));
		checkSCN(new ParseConverter(new CloneFormatFactory(new DecimalFormat())));
		checkSCN(new RegExpConverter(Pattern.compile("(.*)"), "$1"));
		checkSCN(new TrysFn<Integer, Boolean>(Utils
				.list(new NullFn<Integer, Boolean>())
				.list(new NullFn<Integer, Boolean>())));
		checkSCN(new RetainKeys(new ArrayList<String>()));
		
		// id
		checkSCN(new IdConverter<Boolean>(new NullFn<String, String>()));
		checkSCN(new LocalId<IdContext, Boolean>(new NullFn<IdContext, Boolean>()));
		checkSCN(new GettingELFn(new PathEL("a")));
		checkSCN(new InputStreamReaderFactory<Integer>(
				new StringInputStreamFactory("Shift_JIS", "a"),
				"Shift_JIS"
		));
		checkSCN(new LapTimeFn<Integer, String>(
				new NullFn<Integer, String>(),
				new NullFn<Long, String>()
		));
		checkSCN(new FnRunnable<Integer>(new NullFn<Integer, String>(), 1));
		checkSCN(new RunnableFn<String>(
				new FnRunnable<Integer>(new NullFn<Integer, String>(), 1)
		));
		checkSCN(new SleepFn<Integer, String>(3L));
		checkSCN(new StringInputStreamFactory("Shift_JIS", "a"));
		checkSCN(new StringReaderFactory("a"));
		checkSCN(new SwitchNearLocale<String>(new SwitchFn<Locale, String>(Utils.map(Locale.ENGLISH, "a"))));
		checkSCN(new ThreadLocalGetter<String>(new ThreadLocal<String>()));
		
		// xml
		checkSCN(new AttrValueBranch<NodeContext>(
				Arrays.asList("a"),
				new NullFn<String, Fn<NodeContext, Void>>()
		));
		checkSCN(new NodeFinder<NodeContext>(
				new NodeFindableFactory().exec("a"),
				new NullFn<NodeContext, Void>(),
				new NullFn<NodeContext, Void>()
		));
		checkSCN(new AttrValueGetter("a"));
		checkSCN(new ChildElementInserter<NodeContext>("a", new NullFn<NodeContext, Void>()));
		checkSCN(new ParentElementInserter<NodeContext>("a", new NullFn<NodeContext, Void>()));
		checkSCN(new TemplateCopier<NodeContext>(ELUtils.NULL, new PathEL("a"), new NullFn<NodeContext, Void>()));
		checkSCN(new NodeFactory(new NullFn<String, Reader>(), XmlUtils.TRANSFORMER));
		checkSCN(new NodeFindableChildNodesImpl(new NullFn<Node, Boolean>(), false));
		checkSCN(new NodeFindableChildNodesImpl(new NullFn<Node, Boolean>(), false));
		checkSCN(new AttrRemover("a"));
		checkSCN(new TextReplacer<NodeContext>(new NullFn<NodeContext, String>()));
		checkSCN(new ChildTextReplacer<NodeContext>(new NullFn<NodeContext, String>()));
		checkSCN(new NodeSeeker<NodeContext>(new NullFn<NodeContext, Void>()));
		checkSCN(new NodeSetter<NodeContext>(new NullFn<NodeContext, Node>()));
		checkSCN(new AttrValueUpdater<NodeContext>("a", new NullFn<NodeContext, String>()));

		// validator
		checkSCN(new AddressedMessage("a", 0, "b", "c"));
		checkSCN(new NameBranch(
				new And<ValidatorContext>(),
				new HashMap<String, Fn<ValidatorContext, Boolean>>()));
		checkSCN(new NameBranchIfExists(
				new HashMap<String, Fn<ValidatorContext, Boolean>>()));
		checkSCN(new ConvertValidator(new NullFn<Object, Object>()));
		checkSCN(new MessageValidator("a"));
		checkSCN(new LocalIndexValidator(0, new NullFn<ValidatorContext, Boolean>()));
		checkSCN(new LocalKeyValidator("a", new NullFn<ValidatorContext, Boolean>()));
		checkSCN(new ValueLoopValidator(
				new And<ValidatorContext>(), new NullFn<ValidatorContext, Boolean>()));
		{
			// getValueがあるので個別対応
			final ValidatorContext o = new ValidatorContext();
			final SimpleMessageCollector c = new SimpleMessageCollector();
			o.setCollectable(c);
			assertEquals(c, o.getCollectable());
		}
		
		// html
		checkSCN(new AutoUpdater(
				Arrays.asList("a"),
				new PathEL("a"),
				new PathEL("a"),
				new HashSet<Class>(),
				new PathEL("a")));
		checkSCN(new BranchPropertyCounter<NodeContext>(
				new PathEL("a"),
				Arrays.asList("a"),
				new NullFn<NodeContext, Void>(),
				new NullFn<NodeContext, Void>()));
		checkSCN(new RowAttrConverter("a", new String[]{"a"}));
		checkSCN(new HiddenAppender(new PathEL("a"), Arrays.asList("a")));
		checkSCN(new RowIndexInserter("a"));
		checkSCN(new RadioRemaker(new PathEL("a")));
		checkSCN(new SelectRemaker(new PathEL("a")));
		checkSCN(new MergeCell(0));
		checkSCN(new SelectedValueUpdater(new PathEL("a"), new PathEL("a")));
		checkSCN(new ValueUpdater(new PathEL("a"), new PathEL("a"), false));
		
		// typeconvert
		checkSCN(new CollectionConverter(new NullFn<ConvertContext, Void>()));
		checkSCN(new Converter(new NullFn<Object, String>()));
		checkSCN(new DelegationIfNecessityConverter(new NullFn<ConvertContext, Void>()));
		checkSCN(new ToTypeConverter(new NullFn<Class, Class>()));
		
		// el
		checkSC(new ArrayPathEL(new GettingEL[]{new PathEL("a")}, new PathEL("a")));
		checkSCN(new CachePropertyELFactory(
				new ReflectionPropertyELFactory(new NullFn<ConvertContext, Void>())));
		checkSCN(new FixEL(new Object()));
		checkSCN(new MapEL("a", new NullFn<ConvertContext, Void>()));
		checkSCN(new MapPropertyELFactory(
				new NullFn<ConvertContext, Void>(),
				new ReflectionPropertyELFactory(new NullFn<ConvertContext, Void>())));
		checkSC(new PathEL(
				"a",
				new ReflectionPropertyELFactory(new NullFn<ConvertContext, Void>())));
		checkSCN(new PropertyEL(
				"a",
				new ReflectionPropertyELFactory(new NullFn<ConvertContext, Void>())));
		checkSCN(new ReflectionPropertyELFactory(new NullFn<ConvertContext, Void>()));
		
		// acquirable
		checkSCN(new AcquirableChain(new Acquirable[0]));
		checkSCN(new AcquireFn<Integer, String>(
				new NullFn<Integer, Acquirable>(),
				new NullFn<Integer, String>(),
				new NullFn<Integer, String>()
		));
		checkSCN(new DoLock(new ReentrantLock()));
		checkSCN(new DoSemaphore(new Semaphore(1)));
		checkSCN(new DoSemaphoreFactory(1, false));
		checkSCN(new TryLock(new ReentrantLock()));
		checkSCN(new TrySemaphore(new Semaphore(1)));
		checkSCN(new TrySemaphoreFactory(1, false));
		
		// action
		checkSC(new ActionDef(new PathEL("a"), new PathEL("a"), new ArrayList<ForwardDef>()));
		checkSC(new ActionInvoker(new NullFn<String, ActionDef>(), new ForwardDef[]{}));
		checkSCN(new ActionResult(new ForwardDef("id0"), "a"));
		checkSCN(new ForwardDef("id0"));
		checkSCN(new ForwardDef("id0", new PathEL("a")));
		checkSCN(new ForwardDef("id0", BoolUtils.NOT_THROWABLE));
		
		// adapter
		checkSCN(new ConvertableTypeConverter(new NullFn<ConvertContext, Void>()));
		checkGC(new JXE("a"));
		checkGC(new JXP("a"));
		checkGC(new OGE("a"));
		
		// config
		checkSCN(new ConfigSetter<ConfigContext>(new NullFn<ConfigContext, Map<String, Object>>()));
		checkSCN(new PutResourceBundle<ConfigContext>(new NullFn<ConfigContext, ResourceBundle>()));
		checkSCN(new ResourceBundleFactory("a", new ThreadLocal<Locale>()));
		checkSCN(new ToLinkedHashMap("a", "b", "c", "d"));
		
		// container
		{
			final ContainerContext<Object> o = new ContainerContext<Object>();
			o.setKey(new Object());
			o.setSubContext(new Object());
			o.setFn(new NullFn<ContainerContext, Object>());
			checkS(o);
		}
		
		// ee
		{
			// Fnを返す関数がアクセサと誤解されるので個別対応
			final ActionInvoker invoker = new ActionInvoker(new NullFn<String, ActionDef>(), new ForwardDef[]{});
			final List<String> list = Arrays.asList("name", "id");
			final PathEL el0 = new PathEL("a");
			final PathEL el1 = new PathEL("b");
			final ActionBuilder builder = new ActionBuilder(invoker, list, el0, el1);
			assertEquals(invoker, builder.getActionDefs());
			assertEquals(list, builder.getAttrNames());
			assertEquals(el0, builder.getForwardComponentELEL());
			assertEquals(el1, builder.getReturnEL());
			builder.setActionDefs(invoker);
			builder.setAttrNames(list);
			builder.setForwardComponentELEL(el0);
			builder.setReturnEL(el1);
			assertEquals(invoker, builder.getActionDefs());
			assertEquals(list, builder.getAttrNames());
			assertEquals(el0, builder.getForwardComponentELEL());
			assertEquals(el1, builder.getReturnEL());
		}
		
		checkSCN(new ContainerContextSetter(new NullFn<ContainerContext<WebContext>, Object>()));
		{
			// Fnを返す関数がアクセサと誤解されるので個別対応
			final NullFn<ValidatorContext, Boolean> fn = new NullFn<ValidatorContext, Boolean>();
			final PathEL el = new PathEL("a");
			final ValidatorBuilder builder = new ValidatorBuilder(fn, el, true);
			assertEquals(fn, builder.getValidatorFn());
			assertEquals(el, builder.getMessagesEL());
			assertTrue(builder.isValueNotFoundIgnoreFlag());
			builder.setValidatorFn(fn);
			builder.setMessagesEL(el);
			builder.setValueNotFoundIgnoreFlag(true);
			assertEquals(fn, builder.getValidatorFn());
			assertEquals(el, builder.getMessagesEL());
			assertTrue(builder.isValueNotFoundIgnoreFlag());
		}
		
		// test
		
		// misc
		checkGC(new LoadBalancer<Integer, String>(new NullFn<Object, Fn<Integer, String>>(), 3));
		checkSCN(new TryLocales<Object>(new NullFn<String, Object>(), new ThreadLocal<Locale>()));
		
		// factory
		checkSCN(new ConcurrentMapCache<Object, String>(
				new NullFn<Object, ConcurrentMap<String, Object>>(),
				new NullFn<Object, String>(),
				new NullFn<Object, Object>()));
		checkSCN(new MapCache<Object, String>(
				new NullFn<Object, Map<String, Object>>(),
				new NullFn<Object, String>(),
				new NullFn<Object, Object>()));
		checkSCN(new NewInstanceFactory<Object>(Object.class));
		checkSCN(new SideEffectConcurrentMapCache<Object, String>(
				new NullFn<Object, ConcurrentMap<String, Object>>(),
				new NullFn<Object, String>(),
				new NullFn<Object, Object>(),
				new PathEL("a")));
		checkSCN(new SideEffectMapCache<Object, String>(
				new NullFn<Object, Map<String, Object>>(),
				new NullFn<Object, String>(),
				new NullFn<Object, Object>(),
				new PathEL("a")));
		
		// utils
		checkSCN(new AppendableWriter(new StringBuilder()));
		{
			// isEmptyがアクセサと誤解されるので個別対応
			final ArrayList<Object> list = new ArrayList<Object>();
			final DelegationCollection<Object> target = new DelegationCollection<Object>(list);
			assertEquals(list, target.getCollection());
			target.setCollection(list);
			assertEquals(list, target.getCollection());
		}
		{
			// isEmptyがアクセサと誤解されるので個別対応
			final HashMap<Object, Object> map = new HashMap<Object, Object>();
			final DelegationMap<Object, Object> target = new DelegationMap<Object, Object>(map);
			assertEquals(map, target.getMap());
			target.setMap(map);
			assertEquals(map, target.getMap());
		}
		{
			// isEmptyがアクセサと誤解されるので個別対応
			final ArrayList<Map<Object, Object>> list = new ArrayList<Map<Object, Object>>();
			final MapIterableMap<Object, Object> target = new MapIterableMap<Object, Object>(list);
			assertEquals(list, target.getIterable());
			target.setIterable(list);
			assertEquals(list, target.getIterable());
		}
		
		// visitor
		{
			final Visitor<Object> visitor = new Visitor<Object>();
			visitor.setSubContext(new Object());
			visitor.setElement(new Object());
			visitor.setMap(new HashMap<Object, Fn<Visitor<Object>, Void>>());
			checkS(visitor);
		}
	}
}
