| /* |
| * 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 |
| * |
| * https://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.imaging; |
| |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| /** |
| * Provides information about the compliance of a specified data source (byte array, file, etc.) to an image format. |
| */ |
| public class FormatCompliance { |
| |
| private static final Logger LOGGER = Logger.getLogger(FormatCompliance.class.getName()); |
| |
| /** |
| * Gets the default format compliance instance. |
| * |
| * @return a default FormatCompliance instance that ignores errors. |
| */ |
| public static FormatCompliance getDefault() { |
| return new FormatCompliance("ignore", false); |
| } |
| |
| private final boolean failOnError; |
| private final String description; |
| |
| private final List<String> comments = new ArrayList<>(); |
| |
| /** |
| * Constructs a new FormatCompliance with the specified description. |
| * |
| * @param description the description of the format compliance. |
| */ |
| public FormatCompliance(final String description) { |
| this.description = description; |
| this.failOnError = false; |
| } |
| |
| /** |
| * Constructs a new FormatCompliance with the specified description and fail-on-error flag. |
| * |
| * @param description the description of the format compliance. |
| * @param failOnError true to throw exceptions on errors, false to only collect comments. |
| */ |
| public FormatCompliance(final String description, final boolean failOnError) { |
| this.description = description; |
| this.failOnError = failOnError; |
| } |
| |
| /** |
| * Adds a comment about a compliance issue. |
| * |
| * @param comment the comment to add. |
| * @throws ImagingException if failOnError is true. |
| */ |
| public void addComment(final String comment) throws ImagingException { |
| comments.add(comment); |
| if (failOnError) { |
| throw new ImagingException(comment); |
| } |
| } |
| |
| /** |
| * Adds a comment about a compliance issue with a value. |
| * |
| * @param comment the comment to add. |
| * @param value the value associated with the comment. |
| * @throws ImagingException if failOnError is true. |
| */ |
| public void addComment(final String comment, final int value) throws ImagingException { |
| addComment(comment + ": " + getValueDescription(value)); |
| } |
| |
| /** |
| * Checks if a value is within specified bounds. |
| * |
| * @param name the name of the value being checked. |
| * @param min the minimum allowed value. |
| * @param max the maximum allowed value. |
| * @param actual the actual value to check. |
| * @return true if the value is within bounds, false otherwise. |
| * @throws ImagingException if failOnError is true and the check fails. |
| */ |
| public boolean checkBounds(final String name, final int min, final int max, final int actual) throws ImagingException { |
| if (actual < min || actual > max) { |
| addComment(name + ": bounds check: " + min + " <= " + actual + " <= " + max + ": false"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Compares an actual value against a valid value. |
| * |
| * @param name the name of the value being compared. |
| * @param valid the valid value. |
| * @param actual the actual value to compare. |
| * @return true if the actual value matches the valid value, false otherwise. |
| * @throws ImagingException if failOnError is true and the comparison fails. |
| */ |
| public boolean compare(final String name, final int valid, final int actual) throws ImagingException { |
| return compare(name, new int[] { valid, }, actual); |
| } |
| |
| /** |
| * Compares an actual value against an array of valid values. |
| * |
| * @param name the name of the value being compared. |
| * @param valid the array of valid values. |
| * @param actual the actual value to compare. |
| * @return true if the actual value matches any valid value, false otherwise. |
| * @throws ImagingException if failOnError is true and the comparison fails. |
| */ |
| public boolean compare(final String name, final int[] valid, final int actual) throws ImagingException { |
| for (final int element : valid) { |
| if (actual == element) { |
| return true; |
| } |
| } |
| |
| final StringBuilder result = new StringBuilder(43); |
| result.append(name); |
| result.append(": Unexpected value: (valid: "); |
| if (valid.length > 1) { |
| result.append('{'); |
| } |
| for (int i = 0; i < valid.length; i++) { |
| if (i > 0) { |
| result.append(", "); |
| } |
| result.append(getValueDescription(valid[i])); |
| } |
| if (valid.length > 1) { |
| result.append('}'); |
| } |
| result.append(", actual: ").append(getValueDescription(actual)).append(")"); |
| addComment(result.toString()); |
| return false; |
| } |
| |
| /** |
| * Compares two byte arrays for equality. |
| * |
| * @param name the name of the byte arrays being compared. |
| * @param expected the expected byte array. |
| * @param actual the actual byte array to compare. |
| * @return true if the arrays are equal, false otherwise. |
| * @throws ImagingException if failOnError is true and the comparison fails. |
| */ |
| public boolean compareBytes(final String name, final byte[] expected, final byte[] actual) throws ImagingException { |
| if (expected.length != actual.length) { |
| addComment(name + ": Unexpected length: (expected: " + expected.length + ", actual: " + actual.length + ")"); |
| return false; |
| } |
| for (int i = 0; i < expected.length; i++) { |
| // System.out.println("expected: " |
| // + getValueDescription(expected[i]) + ", actual: " |
| // + getValueDescription(actual[i]) + ")"); |
| if (expected[i] != actual[i]) { |
| addComment( |
| name + ": Unexpected value: (expected: " + getValueDescription(expected[i]) + ", actual: " + getValueDescription(actual[i]) + ")"); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Dumps the format compliance information to the logger. |
| */ |
| public void dump() { |
| try (StringWriter sw = new StringWriter(); |
| PrintWriter pw = new PrintWriter(sw)) { |
| dump(pw); |
| pw.flush(); |
| sw.flush(); |
| LOGGER.fine(sw.toString()); |
| } catch (final IOException e) { |
| LOGGER.log(Level.SEVERE, e.getMessage(), e); |
| } |
| } |
| |
| /** |
| * Dumps the format compliance information to the specified PrintWriter. |
| * |
| * @param pw the PrintWriter to write to. |
| */ |
| public void dump(final PrintWriter pw) { |
| pw.println("Format Compliance: " + description); |
| |
| if (comments.isEmpty()) { |
| pw.println("\tNo comments."); |
| } else { |
| for (int i = 0; i < comments.size(); i++) { |
| pw.println("\t" + (i + 1) + ": " + comments.get(i)); |
| } |
| } |
| pw.println(""); |
| pw.flush(); |
| } |
| |
| private String getValueDescription(final int value) { |
| return value + " (" + Integer.toHexString(value) + ")"; |
| } |
| |
| @Override |
| public String toString() { |
| final StringWriter sw = new StringWriter(); |
| final PrintWriter pw = new PrintWriter(sw); |
| |
| dump(pw); |
| |
| return sw.getBuffer().toString(); |
| } |
| } |