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.internal; 017 018import javax.money.MonetaryAmount; 019import javax.money.MonetaryAmountFactory; 020import javax.money.MonetaryAmountFactoryQuery; 021import javax.money.MonetaryContext; 022import javax.money.MonetaryException; 023import org.javamoney.moneta.spi.base.BaseMonetaryAmountsSingletonQuerySpi; 024 025import javax.money.spi.Bootstrap; 026import javax.money.spi.MonetaryAmountFactoryProviderSpi; 027import javax.money.spi.MonetaryAmountFactoryProviderSpi.QueryInclusionPolicy; 028import java.util.*; 029 030/** 031 * Default implementation ot {@link javax.money.spi.MonetaryAmountsSingletonSpi} loading the SPIs on startup 032 * initially once, using the 033 * JSR's {@link javax.money.spi.Bootstrap} mechanism. 034 */ 035public class DefaultMonetaryAmountsSingletonQuerySpi extends BaseMonetaryAmountsSingletonQuerySpi{ 036 037 private static final Comparator<MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount>> CONTEXT_COMPARATOR = 038 new Comparator<MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount>>() { 039 @Override 040 public int compare(MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount> f1, 041 MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount> f2) { 042 int compare = 0; 043 MonetaryContext c1 = f1.getMaximalMonetaryContext(); 044 MonetaryContext c2 = f2.getMaximalMonetaryContext(); 045 if(c1.getPrecision() == 0 && c2.getPrecision() != 0){ 046 compare = -1; 047 } 048 if(compare == 0 && c2.getPrecision() == 0 && c1.getPrecision() != 0){ 049 compare = 1; 050 } 051 if(compare == 0 && c1.getPrecision() != 0 && c2.getPrecision() > c1.getPrecision()){ 052 compare = 1; 053 } 054 if(compare == 0 && c2.getPrecision() != 0 && c2.getPrecision() < c1.getPrecision()){ 055 compare = -1; 056 } 057 if(compare == 0 && (c1.getMaxScale() > c2.getMaxScale())){ 058 compare = -1; 059 } 060 if(compare == 0 && (c1.getMaxScale() < c2.getMaxScale())){ 061 compare = 1; 062 } 063 return compare; 064 } 065 }; 066 067 068 /** 069 * (non-Javadoc) 070 * 071 * @see javax.money.spi.MonetaryAmountsSingletonQuerySpi#getAmountFactories(javax.money.MonetaryAmountFactoryQuery) 072 */ 073 @SuppressWarnings("unchecked") 074 @Override 075 public Collection<MonetaryAmountFactory<?>> getAmountFactories(MonetaryAmountFactoryQuery factoryQuery){ 076 Objects.requireNonNull(factoryQuery); 077 List<MonetaryAmountFactory<?>> factories = new ArrayList<>(); 078 // first check for explicit type 079 for(@SuppressWarnings("unchecked") MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount> f : Bootstrap 080 .getServices(MonetaryAmountFactoryProviderSpi.class)){ 081 if(f.getQueryInclusionPolicy() == QueryInclusionPolicy.NEVER){ 082 continue; 083 } 084 if(factoryQuery.getTargetType() == f.getAmountType()){ 085 if(isPrecisionOK(factoryQuery, f.getMaximalMonetaryContext())){ 086 factories.add(f.createMonetaryAmountFactory()); 087 }else{ 088 throw new MonetaryException("Incompatible context required=" + factoryQuery + ", maximal=" + 089 f.getMaximalMonetaryContext()); 090 } 091 } 092 } 093 List<MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount>> selection = new ArrayList<>(); 094 for(@SuppressWarnings("unchecked") MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount> f : Bootstrap 095 .getServices(MonetaryAmountFactoryProviderSpi.class)){ 096 if(f.getQueryInclusionPolicy() == QueryInclusionPolicy.DIRECT_REFERENCE_ONLY || 097 f.getQueryInclusionPolicy() == QueryInclusionPolicy.NEVER){ 098 continue; 099 } 100 if(isPrecisionOK(factoryQuery, f.getMaximalMonetaryContext())){ 101 selection.add(f); 102 } 103 } 104 if(selection.isEmpty()){ 105 // fall back, add all selections, ignore flavor 106 for(@SuppressWarnings("unchecked") MonetaryAmountFactoryProviderSpi<? extends MonetaryAmount> f : Bootstrap 107 .getServices(MonetaryAmountFactoryProviderSpi.class)){ 108 if(f.getQueryInclusionPolicy() == QueryInclusionPolicy.DIRECT_REFERENCE_ONLY || 109 f.getQueryInclusionPolicy() == QueryInclusionPolicy.NEVER){ 110 continue; 111 } 112 if(isPrecisionOK(factoryQuery, f.getMaximalMonetaryContext())){ 113 selection.add(f); 114 } 115 } 116 } 117 if(selection.size() == 1){ 118 factories.add(selection.get(0).createMonetaryAmountFactory()); 119 } 120 Collections.sort(selection, CONTEXT_COMPARATOR); 121 factories.add(selection.get(0).createMonetaryAmountFactory()); 122 return factories; 123 } 124 125 private boolean isPrecisionOK(MonetaryAmountFactoryQuery requiredContext, MonetaryContext maximalMonetaryContext){ 126 if(maximalMonetaryContext.getPrecision() == 0){ 127 return true; 128 } 129 if(requiredContext.getPrecision() != null){ 130 if(requiredContext.getPrecision() == 0){ 131 return false; 132 } 133 if(requiredContext.getPrecision() > maximalMonetaryContext.getPrecision()){ 134 return false; 135 } 136 } 137 return null == requiredContext.getMaxScale() || 138 requiredContext.getMaxScale() <= maximalMonetaryContext.getMaxScale(); 139 } 140 141}