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