001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.configuration2.io; 018 019import java.io.IOException; 020import java.net.URL; 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.Collections; 024import java.util.List; 025import java.util.Set; 026import java.util.regex.Pattern; 027 028/** 029 * A specialized implementation of a {@code FileLocationStrategy} which encapsulates an arbitrary number of 030 * {@code FileLocationStrategy} objects. 031 * <p> 032 * A collection with the wrapped {@code FileLocationStrategy} objects is passed at construction time. During a 033 * [{@code locate()} operation the wrapped strategies are called one after the other until one returns a non <strong>null</strong> 034 * URL. This URL is returned. If none of the wrapped strategies is able to resolve the passed in {@link FileLocator}, 035 * result is <strong>null</strong>. This is similar to the <em>chain of responsibility</em> design pattern. 036 * </p> 037 * <p> 038 * This class, together with the provided concrete {@code FileLocationStrategy} implementations, offers a convenient way 039 * to customize the lookup for configuration files: Just add the desired concrete strategies to a 040 * {@code CombinedLocationStrategy} object. If necessary, custom strategies can be implemented if there are specific 041 * requirements. Note that the order in which strategies are added to a {@code CombinedLocationStrategy} matters: sub 042 * strategies are queried in the same order as they appear in the collection passed to the constructor. 043 * </p> 044 * <p> 045 * See {@link AbstractFileLocationStrategy} learn how to grant an deny URL schemes and hosts. 046 * </p> 047 * 048 * @see AbstractFileLocationStrategy 049 * @since 2.0 050 */ 051public class CombinedLocationStrategy extends AbstractFileLocationStrategy { 052 053 /** 054 * Builds new instances of {@link CombinedLocationStrategy}. 055 * 056 * @since 2.15.0 057 */ 058 public static class Builder extends AbstractBuilder<CombinedLocationStrategy, Builder> { 059 060 /** A collection with all sub strategies managed by this object. */ 061 private Collection<? extends FileLocationStrategy> subStrategies; 062 063 /** 064 * Constructs a new instance. 065 */ 066 public Builder() { 067 // empty 068 } 069 070 @Override 071 public CombinedLocationStrategy get() throws IOException { 072 return new CombinedLocationStrategy(this); 073 } 074 075 /** 076 * Propagates properties of the parent builder scheme and host to subStrategies. 077 * 078 * @return {@code this} instance. 079 */ 080 public Builder propagate() { 081 if (subStrategies != null) { 082 subStrategies.forEach(e -> { 083 if (e instanceof AbstractFileLocationStrategy) { 084 final AbstractFileLocationStrategy afls = (AbstractFileLocationStrategy) e; 085 final Set<String> schemes = afls.getSchemes(); 086 schemes.clear(); 087 schemes.addAll(getSchemes()); 088 final Set<Pattern> hosts = afls.getHosts(); 089 hosts.clear(); 090 hosts.addAll(getHosts()); 091 } 092 }); 093 } 094 return asThis(); 095 } 096 097 /** 098 * Sets the collection with sub strategies. 099 * 100 * @param subStrategies the collection with sub strategies. 101 * @return {@code this} instance. 102 */ 103 public Builder setSubStrategies(final Collection<FileLocationStrategy> subStrategies) { 104 this.subStrategies = subStrategies; 105 return asThis(); 106 } 107 108 } 109 110 /** A collection with all sub strategies managed by this object. */ 111 private final Collection<FileLocationStrategy> subStrategies; 112 113 /** 114 * Constructs a new instance. 115 * 116 * @param builder How to build the instance. 117 */ 118 private CombinedLocationStrategy(final Builder builder) { 119 super(builder); 120 if (builder.subStrategies == null) { 121 throw new IllegalArgumentException("Collection with sub strategies must not be null."); 122 } 123 List<FileLocationStrategy> subStrategiesCopy = new ArrayList<>(builder.subStrategies); 124 if (subStrategiesCopy.contains(null)) { 125 throw new IllegalArgumentException("Collection with sub strategies contains null entry."); 126 } 127 subStrategies = Collections.unmodifiableCollection(subStrategiesCopy); 128 } 129 130 /** 131 * Creates a new instance of {@code CombinedLocationStrategy} and initializes it with the provided sub strategies. The 132 * passed in collection must not be <strong>null</strong> or contain <strong>null</strong> elements. 133 * 134 * @param subs the collection with sub strategies. 135 * @throws IllegalArgumentException if the collection is <strong>null</strong> or has <strong>null</strong> elements. 136 */ 137 public CombinedLocationStrategy(final Collection<FileLocationStrategy> subs) { 138 this(new Builder().setSubStrategies(subs)); 139 } 140 141 /** 142 * Gets a (unmodifiable) collection with the sub strategies managed by this object. 143 * 144 * @return the sub {@code FileLocationStrategy} objects 145 */ 146 public Collection<FileLocationStrategy> getSubStrategies() { 147 return subStrategies; 148 } 149 150 /** 151 * {@inheritDoc} This implementation tries to locate the file by delegating to the managed sub strategies. 152 */ 153 @Override 154 public URL locate(final FileSystem fileSystem, final FileLocator locator) { 155 for (final FileLocationStrategy sub : getSubStrategies()) { 156 final URL url = sub.locate(fileSystem, locator); 157 if (url != null) { 158 return check(url); 159 } 160 } 161 return null; 162 } 163}