/* ==================================================================== 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.poi.hssf.usermodel; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; /** * Utility class to help test code verify that generated files do not differ from proof copies in * any significant detail. Normally this task would be simple except for the presence of artifacts * in the file that change every time it is generated. Usually these volatile artifacts are * time-stamps, user names, or other machine dependent parameters. * * @author Josh Micich */ public final class StreamUtility { /** * Compares two streams with expected differences in specified regions. The streams are * expected to be of equal length and comparison is always byte for byte. That is - * differences can only involve exchanging each individual byte for another single byte.<br> * Both input streams are closed. * * @param allowableDifferenceRegions array of integer pairs: (offset, length). * Any differences encountered in these regions of the streams will be ignored * @return <code>null</code> if streams are identical, else the * byte indexes of differing data. If streams were different lengths, * the returned indexes will be -1 and the length of the shorter stream */ public static int[] diffStreams(InputStream isA, InputStream isB, int[] allowableDifferenceRegions) { if((allowableDifferenceRegions.length % 2) != 0) { throw new RuntimeException("allowableDifferenceRegions length is odd"); } boolean success = false; int[] result; try { result = diffInternal(isA, isB, allowableDifferenceRegions); success = true; } catch (IOException e) { throw new RuntimeException(e); } finally { close(isA, success); close(isB, success); } return result; } /** * @param success <code>false</code> if the outer method is throwing an exception. */ private static void close(InputStream is, boolean success) { try { is.close(); } catch (IOException e) { if(success) { // this is a new error. ok to throw throw new RuntimeException(e); } // else don't subvert original exception. just print stack trace for this one e.printStackTrace(); } } private static int[] diffInternal(InputStream isA, InputStream isB, int[] allowableDifferenceRegions) throws IOException { int offset = 0; List<Integer> temp = new ArrayList<Integer>(); while (true) { int b = isA.read(); int b2 = isB.read(); if (b == -1) { // EOF if (b2 == -1) { return toPrimitiveIntArray(temp); } return new int[] { -1, offset, }; } if (b2 == -1) { return new int[] { -1, offset, }; } if (b != b2 && !isIgnoredRegion(allowableDifferenceRegions, offset)) { temp.add(Integer.valueOf(offset)); } offset++; } } private static boolean isIgnoredRegion(int[] allowableDifferenceRegions, int offset) { for (int i = 0; i < allowableDifferenceRegions.length; i+=2) { int start = allowableDifferenceRegions[i]; int end = start + allowableDifferenceRegions[i+1]; if(start <= offset && offset < end) { return true; } } return false; } private static int[] toPrimitiveIntArray(List<Integer> temp) { int nItems = temp.size(); if(nItems < 1) { return null; } Integer[] boxInts = new Integer[nItems]; temp.toArray(boxInts); int[] result = new int[nItems]; for (int i = 0; i < result.length; i++) { result[i] = boxInts[i].intValue(); } return result; } }