Repository: commons-compress Updated Branches: refs/heads/compress-2.0 2b31d222b -> 9caa606a8
add compressors class for discovering compression formats Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/9caa606a Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/9caa606a Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/9caa606a Branch: refs/heads/compress-2.0 Commit: 9caa606a8c7726b868d8c1bed7da420389191745 Parents: 2b31d22 Author: Stefan Bodewig <bode...@apache.org> Authored: Sat Apr 29 22:41:16 2017 +0200 Committer: Stefan Bodewig <bode...@apache.org> Committed: Sat Apr 29 22:41:16 2017 +0200 ---------------------------------------------------------------------- .../commons/compress2/FormatDiscoverer.java | 117 +++++++++++++++++++ .../commons/compress2/archivers/Archivers.java | 88 ++------------ .../compress2/compressors/Compressors.java | 58 +++++++++ ...mons.compress2.compressors.CompressionFormat | 1 + .../compress2/archivers/ArchiversTest.java | 13 ++- .../compress2/compressors/CompressorsTest.java | 64 ++++++++++ 6 files changed, 257 insertions(+), 84 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/commons-compress/blob/9caa606a/src/main/java/org/apache/commons/compress2/FormatDiscoverer.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress2/FormatDiscoverer.java b/src/main/java/org/apache/commons/compress2/FormatDiscoverer.java new file mode 100644 index 0000000..a838012 --- /dev/null +++ b/src/main/java/org/apache/commons/compress2/FormatDiscoverer.java @@ -0,0 +1,117 @@ +/* + * 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.commons.compress2; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.TreeSet; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import org.apache.commons.compress2.Format; + +/** + * Loads formats defined as "services" and provides access to them. + * + * <p>Uses {@link java.util.ServiceLoader} under the covers but iterates over all formats found eagerly inside the + * constructor so errors are reported early.</p> + */ +public abstract class FormatDiscoverer<F extends Format> implements Iterable<F> { + private final ServiceLoader<F> formatLoader; + private Map<String, F> formats; + + /** + * Loads services using the given class loader. + * @throws ServiceConfigurationError if an error occurs reading a service file or instantiating a format + */ + protected FormatDiscoverer(ClassLoader cl, Class<F> clazz) throws ServiceConfigurationError { + this(ServiceLoader.load(clazz, cl)); + } + + private FormatDiscoverer(ServiceLoader<F> loader) { + formatLoader = loader; + fillMap(); + } + + /** + * Clears the cached formats and rebuilds it. + * + * @see ServiceLoader#reload + */ + public void reload() { + formatLoader.reload(); + fillMap(); + } + + /** + * Iterator over all known formats. + */ + public Iterator<F> iterator() { + return formats.values().iterator(); + } + + /** + * Iterates over all known formats that support writing. + */ + public Iterable<F> getFormatsWithWriteSupport() { + return filter(Format::supportsWriting); + } + + /** + * Gets a format by its name. + * @param name the {@link Format#getName name} of the format. + * @return the Format instance if one is known by that name + */ + public Optional<F> getFormatByName(String name) { + return Optional.ofNullable(formats.get(name)); + } + + private void fillMap() throws ServiceConfigurationError { + Set<F> ts = new TreeSet<F>(Format.AUTO_DETECTION_ORDER); + ts.addAll(asList(formatLoader)); + formats = Collections.unmodifiableMap(ts.stream() + .collect(Collectors.toMap(Format::getName, Function.identity()))); + } + + protected Iterable<F> filter(final Predicate<F> p) { + return () -> StreamSupport.stream(Spliterators.spliterator(formats.values(), + Spliterator.NONNULL), + false) + .filter(p) + .iterator(); + } + + private static <T> List<T> asList(Iterable<T> i) { + List<T> l = new ArrayList<T>(); + for (T t : i) { + l.add(t); + } + return l; + } +} http://git-wip-us.apache.org/repos/asf/commons-compress/blob/9caa606a/src/main/java/org/apache/commons/compress2/archivers/Archivers.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress2/archivers/Archivers.java b/src/main/java/org/apache/commons/compress2/archivers/Archivers.java index 26f527c..014c2de 100644 --- a/src/main/java/org/apache/commons/compress2/archivers/Archivers.java +++ b/src/main/java/org/apache/commons/compress2/archivers/Archivers.java @@ -18,22 +18,9 @@ */ package org.apache.commons.compress2.archivers; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.List; -import java.util.Map; +import java.util.Optional; import java.util.ServiceConfigurationError; -import java.util.ServiceLoader; -import java.util.Set; -import java.util.TreeSet; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; -import org.apache.commons.compress2.Format; +import org.apache.commons.compress2.FormatDiscoverer; /** * Loads ArchiveFormats defined as "services" from {@code @@ -42,9 +29,7 @@ import org.apache.commons.compress2.Format; * <p>Uses {@link java.util.ServiceLoader} under the covers but iterates over all formats found eagerly inside the * constructor so errors are reported early.</p> */ -public class Archivers implements Iterable<ArchiveFormat<? extends ArchiveEntry>> { - private final ServiceLoader<ArchiveFormat> formatLoader; - private Map<String, ArchiveFormat<? extends ArchiveEntry>> archivers; +public class Archivers extends FormatDiscoverer<ArchiveFormat> { /** * Loads services using the current thread's context class loader. @@ -59,90 +44,37 @@ public class Archivers implements Iterable<ArchiveFormat<? extends ArchiveEntry> * @throws ServiceConfigurationError if an error occurs reading a service file or instantiating a format */ public Archivers(ClassLoader cl) throws ServiceConfigurationError { - this(ServiceLoader.load(ArchiveFormat.class, cl)); - } - - private Archivers(ServiceLoader<ArchiveFormat> loader) { - formatLoader = loader; - fillMap(); - } - - /** - * Clears the cached formats and rebuilds it. - * - * @see ServiceLoader#reload - */ - public void reload() { - formatLoader.reload(); - fillMap(); - } - - /** - * Iterator over all known formats. - */ - public Iterator<ArchiveFormat<? extends ArchiveEntry>> iterator() { - return archivers.values().iterator(); - } - - /** - * Iterates over all known formats that can write archives. - */ - public Iterable<ArchiveFormat<? extends ArchiveEntry>> getFormatsWithWriteSupport() { - return filter(ArchiveFormat::supportsWriting); + super(cl, ArchiveFormat.class); } /** * Iterates over all known formats that can write archives to channels. */ - public Iterable<ArchiveFormat<? extends ArchiveEntry>> getFormatsWithWriteSupportForNonSeekableChannels() { + public Iterable<ArchiveFormat> getFormatsWithWriteSupportForNonSeekableChannels() { return filter(ArchiveFormat::supportsWritingToNonSeekableChannels); } /** * Iterates over all known formats that can read archives from channels. */ - public Iterable<ArchiveFormat<? extends ArchiveEntry>> getFormatsWithReadSupportForNonSeekableChannels() { + public Iterable<ArchiveFormat> getFormatsWithReadSupportForNonSeekableChannels() { return filter(ArchiveFormat::supportsReadingFromNonSeekableChannels); } /** * Iterates over all known formats that provide random access input. */ - public Iterable<ArchiveFormat<? extends ArchiveEntry>> getFormatsWithRandomAccessInput() { + public Iterable<ArchiveFormat> getFormatsWithRandomAccessInput() { return filter(ArchiveFormat::supportsRandomAccessInput); } /** * Gets a format by its name. * @param name the {@link ArchiveFormat#getName name} of the format. - * @return the ArchiveFormat instance or null if not format is known by that name + * @return the Format instance if one is known by that name */ - public ArchiveFormat getArchiveFormatByName(String name) { - return archivers.get(name); + public Optional<ArchiveFormat> getArchiveFormatByName(String name) { + return getFormatByName(name); } - private void fillMap() throws ServiceConfigurationError { - Set<ArchiveFormat<? extends ArchiveEntry>> ts = - new TreeSet<ArchiveFormat<? extends ArchiveEntry>>(Format.AUTO_DETECTION_ORDER); - ts.addAll(asList(formatLoader)); - archivers = Collections.unmodifiableMap(ts.stream() - .collect(Collectors.toMap(ArchiveFormat::getName, Function.identity()))); - } - - private Iterable<ArchiveFormat<? extends ArchiveEntry>> - filter(final Predicate<ArchiveFormat<? extends ArchiveEntry>> p) { - return () -> StreamSupport.stream(Spliterators.spliterator(archivers.values(), - Spliterator.NONNULL), - false) - .filter(p) - .iterator(); - } - - private static <T> List<T> asList(Iterable<T> i) { - List<T> l = new ArrayList<T>(); - for (T t : i) { - l.add(t); - } - return l; - } } http://git-wip-us.apache.org/repos/asf/commons-compress/blob/9caa606a/src/main/java/org/apache/commons/compress2/compressors/Compressors.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/commons/compress2/compressors/Compressors.java b/src/main/java/org/apache/commons/compress2/compressors/Compressors.java new file mode 100644 index 0000000..b3a0672 --- /dev/null +++ b/src/main/java/org/apache/commons/compress2/compressors/Compressors.java @@ -0,0 +1,58 @@ +/* + * 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.commons.compress2.compressors; + +import java.util.Optional; +import java.util.ServiceConfigurationError; +import org.apache.commons.compress2.FormatDiscoverer; + +/** + * Loads CompressionFormats defined as "services" from {@code + * META-INF/services/org.apache.commons.compress2.compressors.CompressionFormat} and provides access to them. + * + * <p>Uses {@link java.util.ServiceLoader} under the covers but iterates over all formats found eagerly inside the + * constructor so errors are reported early.</p> + */ +public class Compressors extends FormatDiscoverer<CompressionFormat> { + + /** + * Loads services using the current thread's context class loader. + * @throws ServiceConfigurationError if an error occurs reading a service file or instantiating a format + */ + public Compressors() throws ServiceConfigurationError { + this(Thread.currentThread().getContextClassLoader()); + } + + /** + * Loads services using the given class loader. + * @throws ServiceConfigurationError if an error occurs reading a service file or instantiating a format + */ + public Compressors(ClassLoader cl) throws ServiceConfigurationError { + super(cl, CompressionFormat.class); + } + + /** + * Gets a format by its name. + * @param name the {@link CompressionFormat#getName name} of the format. + * @return the Format instance if one is known by that name + */ + public Optional<CompressionFormat> getCompressionFormatByName(String name) { + return getFormatByName(name); + } +} http://git-wip-us.apache.org/repos/asf/commons-compress/blob/9caa606a/src/main/resources/META-INF/services/org.apache.commons.compress2.compressors.CompressionFormat ---------------------------------------------------------------------- diff --git a/src/main/resources/META-INF/services/org.apache.commons.compress2.compressors.CompressionFormat b/src/main/resources/META-INF/services/org.apache.commons.compress2.compressors.CompressionFormat new file mode 100644 index 0000000..421c8fe --- /dev/null +++ b/src/main/resources/META-INF/services/org.apache.commons.compress2.compressors.CompressionFormat @@ -0,0 +1 @@ +org.apache.commons.compress2.formats.deflate.DeflateCompressionFormat http://git-wip-us.apache.org/repos/asf/commons-compress/blob/9caa606a/src/test/java/org/apache/commons/compress2/archivers/ArchiversTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/compress2/archivers/ArchiversTest.java b/src/test/java/org/apache/commons/compress2/archivers/ArchiversTest.java index 0846244..397c10e 100644 --- a/src/test/java/org/apache/commons/compress2/archivers/ArchiversTest.java +++ b/src/test/java/org/apache/commons/compress2/archivers/ArchiversTest.java @@ -28,8 +28,9 @@ public class ArchiversTest { @Test public void shouldFindArArchiveFormatByName() { - ArchiveFormat<? extends ArchiveEntry> arFormat = - new Archivers().getArchiveFormatByName(ArArchiveFormat.AR_FORMAT_NAME); + ArchiveFormat arFormat = + new Archivers().getArchiveFormatByName(ArArchiveFormat.AR_FORMAT_NAME) + .orElse(null); Assert.assertNotNull(arFormat); Assert.assertEquals(ArArchiveFormat.class, arFormat.getClass()); } @@ -59,8 +60,8 @@ public class ArchiversTest { shouldNotFind(ArArchiveFormat.class, new Archivers().getFormatsWithRandomAccessInput()); } - private void shouldFind(Class<?> archiveFormat, Iterable<ArchiveFormat<? extends ArchiveEntry>> i) { - for (ArchiveFormat<? extends ArchiveEntry> a : i) { + private void shouldFind(Class<?> archiveFormat, Iterable<ArchiveFormat> i) { + for (ArchiveFormat a : i) { if (archiveFormat.equals(a.getClass())) { return; } @@ -68,8 +69,8 @@ public class ArchiversTest { Assert.fail("Expected to find " + archiveFormat); } - private void shouldNotFind(Class<?> archiveFormat, Iterable<ArchiveFormat<? extends ArchiveEntry>> i) { - for (ArchiveFormat<? extends ArchiveEntry> a : i) { + private void shouldNotFind(Class<?> archiveFormat, Iterable<ArchiveFormat> i) { + for (ArchiveFormat a : i) { if (archiveFormat.equals(a.getClass())) { Assert.fail("Didn't expect to find " + archiveFormat); } http://git-wip-us.apache.org/repos/asf/commons-compress/blob/9caa606a/src/test/java/org/apache/commons/compress2/compressors/CompressorsTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/org/apache/commons/compress2/compressors/CompressorsTest.java b/src/test/java/org/apache/commons/compress2/compressors/CompressorsTest.java new file mode 100644 index 0000000..723abb4 --- /dev/null +++ b/src/test/java/org/apache/commons/compress2/compressors/CompressorsTest.java @@ -0,0 +1,64 @@ +/* + * 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.commons.compress2.compressors; + + +import org.apache.commons.compress2.formats.deflate.DeflateCompressionFormat; + +import org.junit.Assert; +import org.junit.Test; + +public class CompressorsTest { + + @Test + public void shouldFindDeflateCompressionFormatByName() { + CompressionFormat deflateFormat = + new Compressors().getCompressionFormatByName(DeflateCompressionFormat.DEFLATE_FORMAT_NAME) + .orElse(null); + Assert.assertNotNull(deflateFormat); + Assert.assertEquals(DeflateCompressionFormat.class, deflateFormat.getClass()); + } + + @Test + public void shouldFindDeflateCompressionFormatWhenIterating() { + shouldFind(DeflateCompressionFormat.class, new Compressors()); + } + + @Test + public void shouldFindDeflateCompressionFormatAsWritableFormat() { + shouldFind(DeflateCompressionFormat.class, new Compressors().getFormatsWithWriteSupport()); + } + + private void shouldFind(Class<?> compressionFormat, Iterable<CompressionFormat> i) { + for (CompressionFormat a : i) { + if (compressionFormat.equals(a.getClass())) { + return; + } + } + Assert.fail("Expected to find " + compressionFormat); + } + + private void shouldNotFind(Class<?> compressionFormat, Iterable<CompressionFormat> i) { + for (CompressionFormat a : i) { + if (compressionFormat.equals(a.getClass())) { + Assert.fail("Didn't expect to find " + compressionFormat); + } + } + } +}