Index: build.xml =================================================================== --- build.xml (revision 908867) +++ build.xml (working copy) @@ -31,6 +31,7 @@ + @@ -53,6 +54,7 @@ + Index: lib/cglib-nodep-2.2.jar =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Property changes on: lib\cglib-nodep-2.2.jar ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Index: src/java/org/apache/lucene/util/ProxyAttributeSource.java =================================================================== --- src/java/org/apache/lucene/util/ProxyAttributeSource.java (revision 0) +++ src/java/org/apache/lucene/util/ProxyAttributeSource.java (revision 0) @@ -0,0 +1,172 @@ +package org.apache.lucene.util; + +/** + * 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. + */ + +import net.sf.cglib.proxy.Enhancer; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; +import net.sf.cglib.proxy.Factory; +import net.sf.cglib.core.DefaultNamingPolicy; +import net.sf.cglib.core.Predicate; + +import java.lang.reflect.Method; +import java.util.IdentityHashMap; +import java.util.Iterator; + +/** + * TODO. + */ +public class ProxyAttributeSource extends AttributeSource { + + static abstract class ProxyAttributeImpl extends AttributeImpl implements MethodInterceptor { + AttributeImpl delegate = null; + Class implementedAtt = null; + + // final that it will not be intercepted (else recursion and stack overflow) + public final Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { + // use the performant non-reflective pass-through + return proxy.invoke(delegate, args); + } + + // final that it will not be intercepted + @Override + public final Object clone() { + final ProxyAttributeImpl clone = (ProxyAttributeImpl) super.clone(); + clone.delegate = (AttributeImpl) this.delegate.clone(); + return clone; + } + + // final that it will not be intercepted + @Override + public final int hashCode() { + return delegate.hashCode(); + } + + // final that it will not be intercepted + @Override + public final boolean equals(Object o) { + if (o == this) return true; + if (o.getClass() == this.getClass()) { + return delegate.equals(((ProxyAttributeImpl) o).delegate); + } else { + return false; + } + } + + // final that it will not be intercepted + @Override + public final void clear() { + delegate.clear(); + } + + // final that it will not be intercepted + @Override + public final String toString() { + return delegate.toString(); + } + + // final that it will not be intercepted + @Override + public final void copyTo(AttributeImpl target) { + this.delegate.copyTo((target instanceof ProxyAttributeImpl) ? + ((ProxyAttributeImpl) target).delegate : target + ); + } + } + + static final class ProxyAttributeFactory extends AttributeFactory { + private static final IdentityHashMap, Class> attClassImplMap = + new IdentityHashMap, Class>(); + + ProxyAttributeSource parent = null; + + @Override + public AttributeImpl createAttributeInstance(Class attClass) { + if (parent == null) + throw new NullPointerException("Should never happen: Enclosing AttributeSource for the ProxyAttributeFactory not yet set."); + // add this attribute to all childs + for (AttributeSource child : parent.childs) { + child.addAttribute(attClass); + } + // create proxy + try { + final ProxyAttributeImpl impl = getClassForInterface(attClass).newInstance(); + ((Factory) impl).setCallback(0, impl); + impl.implementedAtt = attClass; + impl.delegate = (AttributeImpl) parent.childs[parent.current].getAttribute(attClass); + return impl; + } catch (InstantiationException e) { + throw new RuntimeException("Creation of ProxyAttributeImpl for " + attClass.getName() + " failed.", e); + } catch (IllegalAccessException e) { + throw new RuntimeException("Creation of ProxyAttributeImpl for " + attClass.getName() + " failed.", e); + } + } + + @SuppressWarnings("unchecked") + private static Class getClassForInterface(final Class attClass) { + synchronized(attClassImplMap) { + Class clazz = attClassImplMap.get(attClass); + if (clazz == null) { + final Enhancer enh = new Enhancer(); + enh.setNamingPolicy(new DefaultNamingPolicy() { + /** create a nice name for the class - based on DefaultNamingPolicy but with additional Attribute base name */ + public String getClassName(String prefix, String source, Object key, Predicate names) { + return super.getClassName(prefix + "$$" + attClass.getSimpleName(), source, key, names); + } + }); + enh.setSuperclass(ProxyAttributeImpl.class); + enh.setUseFactory(true); + enh.setInterfaces(new Class[]{ attClass }); + enh.setCallbackType(MethodInterceptor.class); + attClassImplMap.put(attClass, clazz = enh.createClass()); + System.out.println("DEBUG: Created class " + clazz.getName() + " for attribute " + attClass.getName()); + } + return clazz; + } + } + } + + final AttributeSource[] childs; + int current = 0; + + public ProxyAttributeSource(AttributeSource... childs) { + super(new ProxyAttributeFactory()); + // bad hack as we cannot pass this in ctor of factory + ((ProxyAttributeFactory) getAttributeFactory()).parent = this; + this.childs = childs; + } + + public final void setCurrentSource(final int index) { + current = index; + for (Iterator it = getAttributeImplsIterator(); it.hasNext();) { + final ProxyAttributeImpl proxy = (ProxyAttributeImpl) it.next(); + proxy.delegate = (AttributeImpl) childs[current].getAttribute(proxy.implementedAtt); + } + } + + public final int getCurrentSource() { + return current; + } + + @Override + public final void addAttributeImpl(final AttributeImpl att) { + if (!(att instanceof ProxyAttributeImpl)) + throw new IllegalArgumentException("ProxyAttributeSource does not support adding custom AttributeImpls"); + super.addAttributeImpl(att); + } +} Property changes on: src\java\org\apache\lucene\util\ProxyAttributeSource.java ___________________________________________________________________ Added: svn:keywords + Date Author Id Revision HeadURL Added: svn:eol-style + native Index: src/test/org/apache/lucene/util/TestProxyAttributeSource.java =================================================================== --- src/test/org/apache/lucene/util/TestProxyAttributeSource.java (revision 0) +++ src/test/org/apache/lucene/util/TestProxyAttributeSource.java (revision 0) @@ -0,0 +1,125 @@ +package org.apache.lucene.util; + +/** + * 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. + */ + +import org.apache.lucene.analysis.Token; +import org.apache.lucene.analysis.tokenattributes.*; + +import java.util.Random; + +public class TestProxyAttributeSource extends LuceneTestCase { + + public void testProxies() { + AttributeSource src0 = new AttributeSource(); + TermAttribute termAtt0 = src0.addAttribute(TermAttribute.class); + TypeAttribute typeAtt0 = src0.addAttribute(TypeAttribute.class); + termAtt0.setTermBuffer("TestTerm1"); + typeAtt0.setType("TestType"); + + AttributeSource src1 = new AttributeSource(); + TermAttribute termAtt1 = src1.addAttribute(TermAttribute.class); + FlagsAttribute flagsAtt1 = src1.addAttribute(FlagsAttribute.class); + termAtt1.setTermBuffer("TestTerm2"); + flagsAtt1.setFlags(123); + + ProxyAttributeSource proxy = new ProxyAttributeSource(src0, src1); + TermAttribute proxyTermAtt = proxy.addAttribute(TermAttribute.class); + TypeAttribute proxyTypeAtt = proxy.addAttribute(TypeAttribute.class); + FlagsAttribute proxyFlagsAtt = proxy.addAttribute(FlagsAttribute.class); + + // now both delegate att sources must also have the missing attribute + assertTrue(src0.hasAttribute(FlagsAttribute.class)); + assertTrue(src1.hasAttribute(TypeAttribute.class)); + + //default: proxy.setCurrentSource(0); + assertEquals("TestTerm1", proxyTermAtt.term()); + assertEquals("TestType", proxyTypeAtt.type()); + assertEquals(0, proxyFlagsAtt.getFlags()); + proxyFlagsAtt.setFlags(4711); + assertEquals(4711, proxyFlagsAtt.getFlags()); + assertEquals(4711, src0.getAttribute(FlagsAttribute.class).getFlags()); + + proxy.setCurrentSource(1); + assertEquals("TestTerm2", proxyTermAtt.term()); + assertEquals(TypeAttributeImpl.DEFAULT_TYPE, proxyTypeAtt.type()); + assertEquals(123, proxyFlagsAtt.getFlags()); + termAtt1.setTermBuffer("TestTerm3"); + assertEquals("TestTerm3", proxyTermAtt.term()); + + AttributeSource.State state = proxy.captureState(); + + proxy.setCurrentSource(0); + assertEquals("TestTerm1", proxyTermAtt.term()); + assertEquals("TestType", proxyTypeAtt.type()); + assertEquals(4711, proxyFlagsAtt.getFlags()); + + assertEquals(termAtt0.toString(), proxyTermAtt.toString()); + assertEquals(termAtt0.hashCode(), proxyTermAtt.hashCode()); + + proxy.restoreState(state); + assertEquals("TestTerm3", proxyTermAtt.term()); + assertEquals("TestTerm3", termAtt0.term()); + assertEquals(TypeAttributeImpl.DEFAULT_TYPE, proxyTypeAtt.type()); + assertEquals(TypeAttributeImpl.DEFAULT_TYPE, typeAtt0.type()); + assertEquals(123, proxyFlagsAtt.getFlags()); + assertEquals(123, src0.getAttribute(FlagsAttribute.class).getFlags()); + + proxy.clearAttributes(); + assertEquals("", proxyTermAtt.term()); + assertEquals("", termAtt0.term()); + assertEquals(TypeAttributeImpl.DEFAULT_TYPE, proxyTypeAtt.type()); + assertEquals(TypeAttributeImpl.DEFAULT_TYPE, typeAtt0.type()); + } + + static final String[] buffers = new String[] { + "The","quick","brown","fox","jumps","over","the","lazy","dog" + }; + + private void doRandom(Random rnd, TermAttribute termAtt) { + long start = System.nanoTime(); + + final int count = 10000000; + for (int i=0; i