All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sfj.StringsCompare Maven / Gradle / Ivy

Go to download

This is a collection of disparate pieces of code, each file containing a single piece of functionality. The idea is software minimalism, you get 1000 lines of Java code, no dependencies. Collection of useful things, especially for prototyping/rapid development.

There is a newer version: 1.2.0
Show newest version
/*
 * Copyright 2020 C. Schanck
 *
 * Licensed 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.sfj;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * Compare two string sequences (files, resource streams, anything that
 * can be expressed as sequence of lines). Possibly ignore line endings,
 * possible ignore leading and/or trailing whitespace, possibly ignore case.
 * 

Useful for testing when you have an expected file as a resource, say, and * need to compare it to output of some execution. */ public class StringsCompare { /** * A source of lines. Essentially, an iterator with IOExceptions. Also * supports the use of predicates to skip lines testing true. */ interface LineSource extends Closeable { /** * Has a line? * @return ture if another line exists. * @throws IOException on error */ boolean hasLine() throws IOException; /** * Fetch next line. * @return next line * @throws IOException on error */ String nextLine() throws IOException; @Override default void close() throws IOException { } } /** * Predicate to skip no lines. */ public static Predicate SKIP_NONE = (s) -> false; /** * Predicate to skip whitespace only lines. */ public static Predicate SKIP_BLANK = (s) -> s.trim().length() == 0; /** * Predicate to skip empty lines. */ public static Predicate SKIP_EMPTY = (s) -> s.length() == 0; /** * Ident transform, use when you want no mapping done on strings. */ public static final Function IDENT = (s) -> s; /** * Mapping transform to remove leading whitespace. */ public static final Function TRIM_LEADING_WHITESPACE = (s) -> { int i = 0; for (; i < s.length(); i++) { if (!Character.isWhitespace(s.charAt(i))) { break; } } if (i > 0) { return s.substring(i); } return s; }; /** * Mapping transform to remove trailing whitespace. */ public static final Function TRIM_TRAILING_WHITESPACE = (s) -> { int i = s.length(); for (; i > 0; i--) { if (!Character.isWhitespace(s.charAt(i - 1))) { break; } } if (i < s.length()) { return s.substring(0, i); } return s; }; /** * Mapping transform to lowercase a string, allowing for case insensitive * compares. */ public static final Function LOWER_CASE = String::toLowerCase; /** * Mapping transform to trim leading and trailing whitespace. */ public static final Function TRIM_LEADING_AND_TRAILING_WHITESPACE = String::trim; /** * Default operation is to remove trailing whitespace before compare. */ public static final Function DEFAULT_OPERATION = TRIM_TRAILING_WHITESPACE; private StringsCompare() { } static class SkippingLineSource implements LineSource { private final Predicate predicate; private final LineSource delegate; private String next = null; public SkippingLineSource(Predicate predicate, LineSource delegate) throws IOException { this.predicate = predicate; this.delegate = delegate; while (delegate.hasLine()) { String p = delegate.nextLine(); if (!predicate.test(p)) { next = p; break; } } } @Override public boolean hasLine() { return next != null; } @Override public String nextLine() throws IOException { if (hasLine()) { String ret = next; next = null; while (delegate.hasLine()) { String p = delegate.nextLine(); if (!predicate.test(p)) { next = p; break; } } return ret; } throw new NoSuchElementException(); } } /** * Compare two {@link LineSource} objects using the default operation. * @param left one side. * @param right the other. * @return true if they match * @throws IOException on exception */ public static boolean stringsCompare(LineSource left, LineSource right) throws IOException { return stringsCompare(left, right, SKIP_NONE, DEFAULT_OPERATION); } /** * Compare with a specified skipping predicate and the default mapping. * @param left left source * @param right right source. * @param filter filter to use to skip lines * @return true if they match * @throws IOException on exception */ public static boolean stringsCompare(LineSource left, LineSource right, Predicate filter) throws IOException { return stringsCompare(left, right, filter, DEFAULT_OPERATION); } /** * Compare with no skipping filter but the specified mapper. * @param left left source * @param right right source * @param mapper mapper per line. * @return true if the sources match * @throws IOException on exception */ public static boolean stringsCompare(LineSource left, LineSource right, Function mapper) throws IOException { return stringsCompare(left, right, SKIP_NONE, mapper); } /** * Compare two {@link LineSource} objects using a specified operation. Note that any * {@link Function} operation can be changed to make compound operations. * @param left left source * @param right right source * @param filter predicate to designate lines to skip * @param mapper mapper per line. * @return true if they match * @throws IOException on exception */ public static boolean stringsCompare(LineSource left, LineSource right, Predicate filter, Function mapper) throws IOException { try { left = new SkippingLineSource(filter, left); right = new SkippingLineSource(filter, right); while (left.hasLine() && right.hasLine()) { String p1 = left.nextLine(); String p2 = right.nextLine(); p1 = mapper.apply(p1); p2 = mapper.apply(p2); if (!p1.equals(p2)) { return false; } } return !left.hasLine() && !right.hasLine(); } finally { left.close(); right.close(); } } /** * Source for a reader. * @param r reader to draw from * @return LineSource * @throws IOException on underlying exception */ public static LineSource source(Reader r) throws IOException { BufferedReader br = new BufferedReader(r); String first = br.readLine(); return new LineSource() { String next = first; @Override public boolean hasLine() { return next != null; } @Override public String nextLine() throws IOException { if (hasLine()) { String p = next; next = br.readLine(); return p; } throw new NoSuchElementException(); } @Override public void close() throws IOException { r.close(); } }; } /** * Source from a string. * @param s String to use * @return LineSource * @throws IOException on exception */ public static LineSource source(String s) throws IOException { return source(new StringReader(s)); } /** * Source from an input stream, using UTF-8 encoding. * @param is input stream * @return LineSource * @throws IOException on exception */ public static LineSource source(InputStream is) throws IOException { return source(is, StandardCharsets.UTF_8); } /** * Source from an input stream using a specific char set. * @param is input stream * @param cset charset encoding * @return LineSource * @throws IOException on exception */ public static LineSource source(InputStream is, Charset cset) throws IOException { InputStreamReader isr = new InputStreamReader(is, cset); return source(isr); } /** * Source from a resource stream, UTF-8 charset. * @param clz class to use for getResourceAsStream() * @param name name to open * @return LineSource * @throws IOException on exception */ public static LineSource source(Class clz, String name) throws IOException { return source(clz, name, StandardCharsets.UTF_8); } /** * Source from a resource stream, specified charset * @param clz class to use * @param name name to use for resource * @param cset charset encoding to use. * @return LineSource * @throws IOException on exception */ public static LineSource source(Class clz, String name, Charset cset) throws IOException { InputStream res = clz.getResourceAsStream(name); return source(res, cset); } /** * Source from an array of strings. * @param arr array of strings * @return LineSource */ public static LineSource source(String[] arr) { return source(arr, 0, arr.length); } /** * Source from an array of strings. * @param arr array of strings * @param pos position of first entry to use * @param len number of entries to use * @return LineSource */ public static LineSource source(String[] arr, int pos, int len) { return new LineSource() { int idx = pos; @Override public boolean hasLine() { return idx < pos + len; } @Override public String nextLine() { if (hasLine()) { return arr[idx++]; } throw new NoSuchElementException(); } }; } /** * Source from an arbitrary iterator. * @param iter base iterator * @return LineSource */ public static LineSource source(Iterator iter) { return new LineSource() { @Override public boolean hasLine() { return iter.hasNext(); } @Override public String nextLine() { if (hasLine()) { return iter.next(); } throw new NoSuchElementException(); } }; } /** * Source from an iterable. What did you expect, exactly? * @param iter iterable * @return LineSource */ public static LineSource source(Iterable iter) { return source(iter.iterator()); } /** * Source from a file using UTF8 charset encoding. * @param f file to open * @return LineSource * @throws IOException on exception */ public static LineSource source(File f) throws IOException { return source(f, StandardCharsets.UTF_8); } /** * Source from a file using specified encoding. * @param f file * @param cSet charset * @return LineSource * @throws IOException on exception */ public static LineSource source(File f, Charset cSet) throws IOException { FileInputStream fis = new FileInputStream(f); return source(fis, cSet); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy