/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.tools.ant.types.resources; import java.io.File; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.types.AbstractFileSet; import org.apache.tools.ant.types.Reference; import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.types.ResourceCollection; /** * Union of file/dirsets that share the same patterns and selectors * but have different roots. * @since Ant 1.9.4 */ public class MultiRootFileSet extends AbstractFileSet implements ResourceCollection { private SetType type = SetType.file; private boolean cache = true; private List<File> baseDirs = new ArrayList<File>(); private Union union; @Override public void setDir(final File dir) { throw new BuildException(getDataTypeName() + " doesn't support the dir attribute"); } /** * Determines the types of resources to return. * @param type the types of resources to return */ public void setType(final SetType type) { if (isReference()) { throw tooManyAttributes(); } this.type = type; } /** * Set whether to cache collections. * @param b boolean cache flag. */ public synchronized void setCache(final boolean b) { if (isReference()) { throw tooManyAttributes(); } cache = b; } /** * Adds basedirs as a comma separated list. * @param dirs directories as CSV */ public void setBaseDirs(final String dirs) { if (isReference()) { throw tooManyAttributes(); } if (dirs != null && dirs.length() > 0) { final String[] ds = dirs.split(","); for (final String d : ds) { baseDirs.add(getProject().resolveFile(d)); } } } /** * Adds a basedir as nested element. * @param r basedir */ public void addConfiguredBaseDir(final FileResource r) { if (isReference()) { throw noChildrenAllowed(); } baseDirs.add(r.getFile()); } @Override public void setRefid(final Reference r) { if (!baseDirs.isEmpty()) { throw tooManyAttributes(); } super.setRefid(r); } /** * Return a MultiRootFileSet that has the same basedirs and same patternsets * as this one. * @return the cloned MultiRootFileSet. */ @Override public MultiRootFileSet clone() { if (isReference()) { return ((MultiRootFileSet) getRef(getProject())).clone(); } final MultiRootFileSet fs = (MultiRootFileSet) super.clone(); fs.baseDirs = new ArrayList<>(baseDirs); fs.union = null; return fs; } /** * Fulfill the ResourceCollection contract. * @return an Iterator of Resources. */ @Override public Iterator<Resource> iterator() { if (isReference()) { return ((MultiRootFileSet) getRef(getProject())).iterator(); } return merge().iterator(); } /** * Fulfill the ResourceCollection contract. * @return number of elements as int. */ @Override public int size() { if (isReference()) { return ((MultiRootFileSet) getRef(getProject())).size(); } return merge().size(); } /** * Always returns true. * @return true indicating that all elements will be FileResources. */ @Override public boolean isFilesystemOnly() { return true; } /** * Returns included directories as a list of semicolon-separated paths. * * @return a <code>String</code> of included directories. */ @Override public String toString() { if (isReference()) { return ((MultiRootFileSet) getRef(getProject())).toString(); } return merge().toString(); } private synchronized Union merge() { if (cache && union != null) { return union; } final Union u = new Union(); setup(u); if (cache) { union = u; } return u; } private void setup(final Union u) { for (final File d : baseDirs) { u.add(new Worker(this, type, d)); } } /** * What to return from the set: files, directories or both. */ public static enum SetType { file, dir, both } private static class Worker extends AbstractFileSet implements ResourceCollection { private final SetType type; private Worker(final MultiRootFileSet fs, final SetType type, final File dir) { super(fs); this.type = type; setDir(dir); } @Override public boolean isFilesystemOnly() { return true; } @Override public Iterator<Resource> iterator() { final DirectoryScanner ds = getDirectoryScanner(getProject()); String[] names = type == SetType.file ? ds.getIncludedFiles() : ds.getIncludedDirectories(); if (type == SetType.both) { final String[] files = ds.getIncludedFiles(); final String[] merged = new String[names.length + files.length]; System.arraycopy(names, 0, merged, 0, names.length); System.arraycopy(files, 0, merged, names.length, files.length); names = merged; } return new FileResourceIterator(getProject(), getDir(getProject()), names); } @Override public int size() { final DirectoryScanner ds = getDirectoryScanner(getProject()); int count = type == SetType.file ? ds.getIncludedFilesCount() : ds.getIncludedDirsCount(); if (type == SetType.both) { count += ds.getIncludedFilesCount(); } return count; } } }