001/* 002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016package org.javamoney.moneta.spi; 017 018import javax.money.*; 019 020import org.javamoney.moneta.spi.base.BaseMonetaryAmountsSingletonQuerySpi; 021 022import javax.money.spi.Bootstrap; 023import javax.money.spi.MonetaryAmountFactoryProviderSpi; 024import javax.money.spi.MonetaryAmountFactoryProviderSpi.QueryInclusionPolicy; 025 026import java.util.ArrayList; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.Comparator; 030import java.util.List; 031import java.util.Objects; 032 033/** 034 * Default implementation ot {@link javax.money.spi.MonetaryAmountsSingletonSpi} loading the SPIs on startup 035 * initially once, using the 036 * JSR's {@link javax.money.spi.Bootstrap} mechanism. 037 */ 038public class DefaultMonetaryAmountsSingletonQuerySpi extends BaseMonetaryAmountsSingletonQuerySpi{ 039 040 private static final Comparator<MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount>> CONTEXT_COMPARATOR = 041 new Comparator<MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount>>() { 042 @Override 043 public int compare(MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount> f1, 044 MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount> f2) { 045 int compare = 0; 046 MonetaryContext c1 = f1.getMaximalMonetaryContext(); 047 MonetaryContext c2 = f2.getMaximalMonetaryContext(); 048 if(c1.getPrecision() == 0 && c2.getPrecision() != 0){ 049 compare = -1; 050 } 051 if(compare == 0 && c2.getPrecision() == 0 && c1.getPrecision() != 0){ 052 compare = 1; 053 } 054 if(compare == 0 && c1.getPrecision() != 0 && c2.getPrecision() > c1.getPrecision()){ 055 compare = 1; 056 } 057 if(compare == 0 && c2.getPrecision() != 0 && c2.getPrecision() < c1.getPrecision()){ 058 compare = -1; 059 } 060 if(compare == 0 && (c1.getMaxScale() > c2.getMaxScale())){ 061 compare = -1; 062 } 063 if(compare == 0 && (c1.getMaxScale() < c2.getMaxScale())){ 064 compare = 1; 065 } 066 return compare; 067 } 068 }; 069 070 071 /** 072 * (non-Javadoc) 073 * 074 * @see javax.money.spi.MonetaryAmountsSingletonQuerySpi#getAmountFactories(javax.money.MonetaryAmountFactoryQuery) 075 */ 076 @SuppressWarnings("unchecked") 077 @Override 078 public Collection<MonetaryAmountFactory<?>> getAmountFactories(MonetaryAmountFactoryQuery factoryQuery){ 079 Objects.requireNonNull(factoryQuery); 080 List<MonetaryAmountFactory<?>> factories = new ArrayList<>(); 081 // first check for explicit type 082 for(@SuppressWarnings("unchecked") MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount> prov : Bootstrap 083 .getServices(MonetaryAmountFactoryProviderSpi.class)){ 084 if(prov.getQueryInclusionPolicy() == QueryInclusionPolicy.NEVER){ 085 continue; 086 } 087 if(factoryQuery.getTargetType() == prov.getAmountType()){ 088 if(isPrecisionOK(factoryQuery, prov.getMaximalMonetaryContext())){ 089 factories.add(createFactory(prov, factoryQuery)); 090 }else{ 091 throw new MonetaryException("Incompatible context required=" + factoryQuery + ", maximal=" + 092 prov.getMaximalMonetaryContext()); 093 } 094 } 095 } 096 List<MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount>> selection = new ArrayList<>(); 097 for(@SuppressWarnings("unchecked") MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount> f : Bootstrap 098 .getServices(MonetaryAmountFactoryProviderSpi.class)){ 099 if(f.getQueryInclusionPolicy() == QueryInclusionPolicy.DIRECT_REFERENCE_ONLY || 100 f.getQueryInclusionPolicy() == QueryInclusionPolicy.NEVER){ 101 continue; 102 } 103 if(isPrecisionOK(factoryQuery, f.getMaximalMonetaryContext())){ 104 selection.add(f); 105 } 106 } 107 if(selection.isEmpty()){ 108 // fall back, add all selections, ignore flavor 109 for(@SuppressWarnings("unchecked") MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount> f : Bootstrap 110 .getServices(MonetaryAmountFactoryProviderSpi.class)){ 111 if(f.getQueryInclusionPolicy() == QueryInclusionPolicy.DIRECT_REFERENCE_ONLY || 112 f.getQueryInclusionPolicy() == QueryInclusionPolicy.NEVER){ 113 continue; 114 } 115 if(isPrecisionOK(factoryQuery, f.getMaximalMonetaryContext())){ 116 selection.add(f); 117 } 118 } 119 } 120 if(selection.size() == 1){ 121 factories.add(createFactory(selection.get(0), factoryQuery)); 122 } 123 Collections.sort(selection, CONTEXT_COMPARATOR); 124 factories.add(createFactory(selection.get(0), factoryQuery)); 125 return factories; 126 } 127 128 /** 129 * Creates a {@link MonetaryAmountFactory} using the given provider and configures the 130 * {@link MonetaryContext} based on the given {@link MonetaryAmountFactoryQuery}. 131 * 132 * This code should actually be done in "createMonetaryAmountFactory", see issue #65. 133 * @param prov the factory provider, not null 134 * @param factoryQuery the original query 135 * @return the configured amount factory, never null. 136 */ 137 private MonetaryAmountFactory<?> createFactory(MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount> prov, MonetaryAmountFactoryQuery factoryQuery) { 138 MonetaryAmountFactory<?> factory = prov.createMonetaryAmountFactory(); 139 if(factoryQuery!=null) { 140 MonetaryContextBuilder cb = factory.getDefaultMonetaryContext().toBuilder(); 141 for (String key : factoryQuery.getKeys(Object.class)) { 142 cb.set(key, factoryQuery.get(key, Object.class)); 143 } 144 factory.setContext(cb.build()); 145 } 146 return factory; 147 } 148 149 private boolean isPrecisionOK(MonetaryAmountFactoryQuery requiredContext, MonetaryContext maximalMonetaryContext){ 150 if(maximalMonetaryContext.getPrecision() == 0){ 151 return true; 152 } 153 if(requiredContext.getPrecision() != null){ 154 if(requiredContext.getPrecision() == 0){ 155 return false; 156 } 157 if(requiredContext.getPrecision() > maximalMonetaryContext.getPrecision()){ 158 return false; 159 } 160 } 161 return null == requiredContext.getMaxScale() || 162 requiredContext.getMaxScale() <= maximalMonetaryContext.getMaxScale(); 163 } 164 165}