1 /* 2 * Copyright 2010 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.typography.font.sfntly.testutils; 18 19 import com.google.typography.font.sfntly.data.ReadableFontData; 20 21 import com.ibm.icu.charset.CharsetICU; 22 23 import java.io.File; 24 import java.io.FileInputStream; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.io.OutputStream; 28 import java.io.RandomAccessFile; 29 import java.io.UnsupportedEncodingException; 30 import java.nio.ByteBuffer; 31 import java.nio.CharBuffer; 32 import java.nio.channels.FileChannel; 33 import java.nio.charset.Charset; 34 import java.nio.charset.CharsetEncoder; 35 import java.nio.charset.CoderResult; 36 37 /** 38 * @author Stuart Gill 39 */ 40 public class TestUtils { TestUtils()41 private TestUtils() {} 42 43 /** 44 * Compare sections of two byte arrays for equality. 45 * @param b1 byte array 1 46 * @param offset1 offset for comparison in byte array 1 47 * @param b2 byte array 2 48 * @param offset2 offset for comparison in byte array 2 49 * @param length the length of the byte arrays to compare 50 * @return true if the array segments are equal; false otherwise 51 */ equals(byte[] b1, int offset1, byte[] b2, int offset2, int length)52 public static boolean equals(byte[] b1, int offset1, byte[] b2, int offset2, int length) { 53 for (int i = 0; i < length; i++) { 54 if (b1[i + offset1] != b2[i + offset2]) { 55 return false; 56 } 57 } 58 return true; 59 } 60 createFileChannelForWriting(File file)61 public static FileChannel createFileChannelForWriting(File file) throws IOException { 62 createNewFile(file); 63 64 RandomAccessFile raf = new RandomAccessFile(file, "rw"); 65 return raf.getChannel(); 66 } 67 68 /** 69 * Creates a new file including deleting an already existing file with the same path 70 * and name and creating any needed directories. 71 * 72 * @param file the file to create 73 * @throws IOException 74 */ createNewFile(File file)75 public static void createNewFile(File file) throws IOException { 76 if (file.exists()) { 77 file.delete(); 78 } 79 file.getParentFile().mkdirs(); 80 file.createNewFile(); 81 } 82 createOutputStream(File file)83 public static OutputStream createOutputStream(File file) throws IOException { 84 createNewFile(file); 85 return new FileOutputStream(file); 86 } 87 88 /** 89 * Converts an integer into a 4 character string using the ASCII encoding. 90 * @param i the value to convert 91 * @return the String based on the number 92 */ dumpLongAsString(int i)93 public static String dumpLongAsString(int i) { 94 byte[] b = new byte[] { 95 (byte) (i >> 24 & 0xff), 96 (byte) (i >> 16 & 0xff), 97 (byte) (i >> 8 & 0xff), 98 (byte) (i & 0xff)}; 99 100 String s; 101 try { 102 s = new String(b, "US-ASCII"); 103 } catch (UnsupportedEncodingException e) { 104 throw new RuntimeException("Guaranteed encoding US-ASCII missing."); 105 } 106 return s; 107 } 108 109 /** 110 * Calculate an OpenType checksum from the array. 111 * @param b the array to calculate checksum on 112 * @param offset the starting index in the array 113 * @param length the number of bytes to check; <b>must</b> be a multiple of 4 114 * @return checksum 115 */ checkSum(byte[] b, int offset, int length)116 public static long checkSum(byte[] b, int offset, int length) { 117 long checkSum = 0; 118 119 for (int i = offset; i < length; i+=4) { 120 for (int j = 0; j < 4; j++) { 121 if (j + i < length) { 122 checkSum += (b[j+i] & 0xff) << (24 - 8*j); 123 } 124 } 125 } 126 return checkSum & 0xffffffffL; 127 } 128 129 /** 130 * Encode a single character in UTF-16. 131 * @param encoder the encoder to use for the encoding 132 * @param uchar the Unicode character to encode 133 * @return the encoded character 134 */ encodeOneChar(CharsetEncoder encoder, int uchar)135 public static int encodeOneChar(CharsetEncoder encoder, int uchar) { 136 ByteBuffer bb = ByteBuffer.allocate(10); 137 CharBuffer cb = CharBuffer.wrap(new char[] {(char) uchar}); 138 CoderResult result = encoder.encode(cb, bb, true); 139 if (result.isError()) { 140 return 0; 141 } 142 if (bb.position() > 4) { 143 return 0; 144 } 145 int encChar = 0; 146 for (int position = 0; position < bb.position(); position++) { 147 encChar <<= 8; 148 encChar |= (bb.get(position) & 0xff); 149 } 150 return encChar; 151 } 152 153 /** 154 * Get an encoder for the charset name. 155 * If the name is null or the empty string then just return null. 156 * @param charsetName the charset to get an encoder for 157 * @return an encoder or null if no encoder available for charset name 158 */ getEncoder(String charsetName)159 public static CharsetEncoder getEncoder(String charsetName) { 160 if (charsetName == null || charsetName.equals("")) { 161 return null; 162 } 163 Charset cs = CharsetICU.forNameICU(charsetName); 164 return cs.newEncoder(); 165 } 166 167 private static final char EXTENSION_SEPARATOR = '.'; 168 extension(File file)169 public static String extension(File file) { 170 String ext = file.getName(); 171 int extPosition = ext.lastIndexOf(EXTENSION_SEPARATOR); 172 if (extPosition == -1) { 173 return ""; 174 } 175 return ext.substring(extPosition); 176 } 177 178 /** 179 * Read a file fully into a new byte array. 180 * @param file the file to read 181 * @return the byte array 182 * @throws IOException 183 */ readFile(File file)184 public static byte[] readFile(File file) throws IOException { 185 int length = (int) file.length(); 186 byte[] b = new byte[length]; 187 188 FileInputStream fis = null; 189 try { 190 fis = new FileInputStream(file); 191 while (length > 0) { 192 length -= fis.read(b, b.length - length, length); 193 } 194 return b; 195 } finally { 196 fis.close(); 197 } 198 } 199 200 /** 201 * @param offset1 offset to start comparing the first ReadableFontData from 202 * @param rfd1 the first ReadableFontData 203 * @param offset2 offset to start comparing the second ReadableFontData from 204 * @param rfd2 the second ReadableFontData 205 * @param length the number of bytes to compare 206 * @return true if all bytes in the ranges given are equal; false otherwise 207 */ equals( int offset1, ReadableFontData rfd1, int offset2, ReadableFontData rfd2, int length)208 public static boolean equals( 209 int offset1, ReadableFontData rfd1, int offset2, ReadableFontData rfd2, int length) { 210 for (int i = 0; i < length; i++) { 211 int b1 = rfd1.readByte(i + offset1); 212 int b2 = rfd2.readByte(i + offset2); 213 if (b1 != b2) { 214 return false; 215 } 216 } 217 return true; 218 } 219 220 /** 221 * Checks that both objects are equal as defined by the object itself. If one 222 * is null then they are not equal. If both are null they are considered 223 * equal. 224 * 225 * @param o1 first object 226 * @param o2 second object 227 * @return true if equal 228 */ equalsNullOk(Object o1, Object o2)229 public static boolean equalsNullOk(Object o1, Object o2) { 230 if (o1 == o2) { 231 return true; 232 } 233 if (o1 == null || o2 == null) { 234 // both can't be null - caught above 235 return false; 236 } 237 return o1.equals(o2); 238 } 239 } 240