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}