001package com.hammurapi.extract; 002 003import java.util.HashMap; 004import java.util.Map; 005import java.util.concurrent.TimeUnit; 006 007import com.hammurapi.extract.ComparisonResult.Type; 008 009 010/** 011 * Base class for extractors, implements value caching. 012 * @author Pavel Vlasov. 013 * 014 * @param <T> 015 * @param <V> 016 */ 017public abstract class ExtractorBase<T, V, C> implements Extractor<T, V, C> { 018 019 protected TimeUnit costUnit; 020 protected long invocations; 021 protected double totalCost; 022 protected boolean nanos; 023 protected double cost; 024 protected double initialCost; 025 026 /** 027 * @param initialCost Initial cost. If positive, this value is added to the calculated cost. If costUnit is null, 028 * then cost is always equal to the initial cost. If negative and time unit is not null, then absolute value is used as first measurement of cost. 029 * @param costUnit If this argument is not null, then extractor cost is 030 * calculated as average time to execute extractInternal() method in specified time unit. 031 * If time unit is nanoseconds or microseconds, then System.nanoTime() method is used for 032 * measurement, otherwise Systsm.currentTimeMillis() is used. 033 */ 034 protected ExtractorBase(double initialCost, TimeUnit costUnit) { 035 this.costUnit = costUnit; 036 this.initialCost = initialCost; 037 if (initialCost<0 && costUnit!=null) { 038 invocations = 1; 039 totalCost = initialCost; 040 } else { 041 this.cost = initialCost; 042 } 043 nanos = costUnit!=null && TimeUnit.MILLISECONDS.compareTo(costUnit)>0; 044 } 045 046 @SuppressWarnings("unchecked") 047 public V extract(C context, Map<C, Map<Extractor<T, ? super V, C>, ? super V>> cache, T... obj) { 048 long start = 0; 049 if (costUnit!=null) { 050 start = nanos ? System.nanoTime() : System.currentTimeMillis(); 051 } 052 try { 053 if (cache==null) { 054 return extractInternal(context, cache, obj); 055 } 056 057 synchronized (cache) { 058 Map<Extractor<T, ? super V, C>, ? super V> ce = cache.get(context); 059 if (ce==null) { 060 ce = createCacheEntry(); 061 cache.put(context, ce); 062 } 063 064 V ret = (V) ce.get(this); 065 if (ret==null) { 066 ret = extractInternal(context, cache, obj); 067 ce.put(this, ret); 068 } 069 return ret; 070 } 071 } finally { 072 if (costUnit!=null) { 073 long end = nanos ? System.nanoTime() : System.currentTimeMillis(); 074 totalCost += costUnit.convert(end-start, nanos ? TimeUnit.NANOSECONDS : TimeUnit.MICROSECONDS); 075 ++invocations; 076 } 077 } 078 } 079 080 @Override 081 public double getCost() { 082 083 if (invocations==0) { 084 return cost; 085 } 086 087 return cost + totalCost/invocations; 088 } 089 090 /** 091 * Basic comparisons with True and False. 092 * @param otherPredicate 093 * @return 094 */ 095 public ComparisonResult compareTo(Extractor<T, V, C> other) { 096 if (this==other) { 097 return ComparisonResult.EQUAL_NM; 098 } 099 100 // True is less restrictive than anything but self. 101 if (other instanceof True) { 102 return ComparisonResult.MORE_RESTRICTIVE_NM; 103 } 104 105 // False is more restrictive than anything but self. 106 if (other instanceof False) { 107 return ComparisonResult.LESS_RESTRICTIVE_NM; 108 } 109 110 if (other instanceof FacadeExtractor && !(this instanceof FacadeExtractor)) { 111 ComparisonResult cr = other.compareTo(this); 112 if (cr==null) { 113 return cr; 114 } 115 switch (cr.getType()) { 116 case EQUAL: 117 case NOT_EQUAL: 118 case OPPOSITE: 119 return cr; 120 case LESS_RESTRICTIVE: 121 return new ComparisonResult(Type.MORE_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices())); 122 case MORE_RESTRICTIVE: 123 return new ComparisonResult(Type.LESS_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices())); 124 case OPPOSITE_LESS_RESTRICTIVE: 125 return new ComparisonResult(Type.OPPOSITE_LESS_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices())); 126 case OPPOSITE_MORE_RESTRICTIVE: 127 return new ComparisonResult(Type.OPPOSITE_MORE_RESTRICTIVE, ComparisonResult.inverse(cr.getIndexMap(), parameterIndices())); 128 } 129 } 130 131 return ComparisonResult.NOT_EQUAL_NM; 132 } 133 134 135 protected abstract V extractInternal(C context, Map<C, Map<Extractor<T, ? super V, C>, ? super V>> cache, T... obj); 136 137 protected Map<Extractor<T, ? super V, C>, ? super V> createCacheEntry() { 138 return new HashMap<Extractor<T,? super V,C>, V>(); 139 } 140 141 @Override 142 public boolean equals(Object obj) { 143 if (this == obj) { 144 return true; 145 } 146 if (obj instanceof Extractor) { 147 ComparisonResult cr = compareTo((Extractor) obj); 148 if (ComparisonResult.Type.EQUAL.equals(cr.getType()) && cr.isOneToOneMapping()) { 149 return true; 150 } 151 } 152 return false; 153 } 154 155}