1/*
2 *******************************************************************************
3 * Copyright (C) 1996-2015, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
6 *
7 */
8
9package com.ibm.icu.dev.test.serializable;
10
11import java.io.ByteArrayInputStream;
12import java.io.ByteArrayOutputStream;
13import java.io.File;
14import java.io.FileInputStream;
15import java.io.FileNotFoundException;
16import java.io.IOException;
17import java.io.InputStream;
18import java.io.ObjectInputStream;
19import java.net.JarURLConnection;
20import java.net.URL;
21import java.util.Enumeration;
22import java.util.MissingResourceException;
23import java.util.jar.JarEntry;
24import java.util.jar.JarFile;
25
26import com.ibm.icu.dev.test.TestFmwk;
27
28/**
29 * @author emader
30 */
31public class CompatibilityTest extends TestFmwk
32{
33    public class FolderTarget extends Target
34    {
35        private Target head = new Target(null);
36        private Target tail = head;
37
38        public FolderTarget(String name)
39        {
40            super(name);
41        }
42
43        public void add(String className, InputStream is)
44        {
45            HandlerTarget newTarget = new HandlerTarget(className, is);
46
47            tail.setNext(newTarget);
48            tail = newTarget;
49        }
50
51        protected boolean validate()
52        {
53            return true;
54        }
55
56        protected void execute() throws Exception
57        {
58            params.indentLevel += 1;
59
60            for (Target target = head.getNext(); target != null; target = target.getNext())
61            {
62                target.run();
63            }
64
65            params.indentLevel -= 1;
66        }
67    }
68
69    public class HandlerTarget extends Target
70    {
71        protected SerializableTest.Handler handler = null;
72        protected InputStream inputStream = null;
73
74        public HandlerTarget(String name, InputStream is)
75        {
76            super(name);
77            inputStream = is;
78        }
79
80        protected boolean validate()
81        {
82            handler = SerializableTest.getHandler(name);
83
84            return handler != null;
85        }
86
87        protected void execute() throws Exception
88        {
89            try {
90                if (params.inDocMode()) {
91                    // nothing to execute
92                } else if (!params.stack.included) {
93                    ++params.invalidCount;
94                } else {
95                    params.testCount += 1;
96
97                    try {
98                        ObjectInputStream in = new ObjectInputStream(inputStream);
99                        Object inputObjects[] = (Object[]) in.readObject();
100                        Object testObjects[] = handler.getTestObjects();
101
102                        in.close();
103                        inputStream.close();
104
105                        // TODO: add equality test...
106                        // The commented out code below does that,
107                        // but some test objects don't define an equals() method,
108                        // and the default method is the same as the "==" operator...
109                        for (int i = 0; i < testObjects.length; i += 1) {
110                            // if (! inputObjects[i].equals(testObjects[i])) {
111                            // errln("Input object " + i + " failed equality test.");
112                            // }
113
114                            if (!handler.hasSameBehavior(inputObjects[i], testObjects[i])) {
115                                warnln("Input object " + i + " failed behavior test.");
116                            }
117                        }
118                    } catch (MissingResourceException e) {
119                        warnln("Could not load the data. " + e.getMessage());
120                    } catch (Exception e) {
121                        e.printStackTrace();
122                        errln("Exception: " + e.toString());
123                    }
124                }
125            } finally {
126                inputStream.close();
127                inputStream = null;
128            }
129        }
130    }
131
132    private static final String[][] SKIP_CASES = {
133        // ICU 52+ PluralRules/PluralFormat/CurrencyPluralInfo are not
134        // serialization-compatible with previous versions.
135        {"ICU_50.1", "com.ibm.icu.text.CurrencyPluralInfo.dat"},
136        {"ICU_51.1", "com.ibm.icu.text.CurrencyPluralInfo.dat"},
137
138        {"ICU_50.1", "com.ibm.icu.text.PluralFormat.dat"},
139        {"ICU_51.1", "com.ibm.icu.text.PluralFormat.dat"},
140
141        {"ICU_50.1", "com.ibm.icu.text.PluralRules.dat"},
142        {"ICU_51.1", "com.ibm.icu.text.PluralRules.dat"},
143
144        // GeneralMeasureFormat was in technical preview, but is going away after ICU 52.1.
145        {"ICU_52.1", "com.ibm.icu.text.GeneralMeasureFormat.dat"},
146
147        // RuleBasedNumberFormat
148        {"ICU_3.6",     "com.ibm.icu.text.RuleBasedNumberFormat.dat"},
149
150        // ICU 4.8+ MessageFormat is not serialization-compatible with previous versions.
151        {"ICU_3.6",     "com.ibm.icu.text.MessageFormat.dat"},
152    };
153
154    private Target getFileTargets(URL fileURL)
155    {
156        File topDir = new File(fileURL.getPath());
157        File dataDirs[] = topDir.listFiles();
158        FolderTarget target = null;
159
160        for (int d = 0; d < dataDirs.length; d += 1) {
161            File dataDir = dataDirs[d];
162
163            if (dataDir.isDirectory()) {
164                FolderTarget newTarget = new FolderTarget(dataDir.getName());
165                File files[] = dataDir.listFiles();
166
167                newTarget.setNext(target);
168                target = newTarget;
169
170                String dataDirName = dataDir.getName();
171
172                element_loop:
173                for (int i = 0; i < files.length; i += 1) {
174                    File file = files[i];
175                    String filename = file.getName();
176                    int ix = filename.indexOf(".dat");
177
178                    if (ix > 0) {
179                        String className = filename.substring(0, ix);
180
181                        // Skip some cases which do not work well
182                        for (int j = 0; j < SKIP_CASES.length; j++) {
183                            if (dataDirName.equals(SKIP_CASES[j][0]) && filename.equals(SKIP_CASES[j][1])) {
184                                logln("Skipping test case - " + dataDirName + "/" + className);
185                                continue element_loop;
186                            }
187                        }
188
189                        try {
190                            @SuppressWarnings("resource")  // Closed by HandlerTarget.execute().
191                            InputStream is = new FileInputStream(file);
192                            target.add(className, is);
193                        } catch (FileNotFoundException e) {
194                            errln("Exception: " + e.toString());
195                        }
196
197                    }
198                }
199            }
200        }
201
202        return target;
203    }
204
205    private static InputStream copyInputStream(InputStream in) throws IOException {
206        try {
207            ByteArrayOutputStream out = new ByteArrayOutputStream();
208            byte[] buf = new byte[1024];
209            while (true) {
210                int r = in.read(buf);
211                if (r == -1) {
212                    break;
213                }
214                out.write(buf, 0, r);
215            }
216            return new ByteArrayInputStream(out.toByteArray());
217        } finally {
218            in.close();
219        }
220    }
221
222    private Target getJarTargets(URL jarURL)
223    {
224        String prefix = jarURL.getPath();
225        String currentDir = null;
226        int ix = prefix.indexOf("!/");
227        FolderTarget target = null;
228
229        if (ix >= 0) {
230            prefix = prefix.substring(ix + 2);
231        }
232
233        try {
234            JarURLConnection conn = (JarURLConnection) jarURL.openConnection();
235            JarFile jarFile = conn.getJarFile();
236            try {
237                Enumeration entries = jarFile.entries();
238element_loop:
239                while (entries.hasMoreElements()) {
240                    JarEntry entry = (JarEntry) entries.nextElement();
241                    String name = entry.getName();
242
243                    if (name.startsWith(prefix)) {
244                        name = name.substring(prefix.length());
245
246                        if (!entry.isDirectory()) {
247                            int dx = name.lastIndexOf("/");
248                            String dirName = name.substring(1, dx);
249                            String filename = name.substring(dx + 1);
250
251                            if (!dirName.equals(currentDir)) {
252                                currentDir = dirName;
253
254                                FolderTarget newTarget = new FolderTarget(currentDir);
255
256                                newTarget.setNext(target);
257                                target = newTarget;
258                            }
259
260                            int xx = filename.indexOf(".dat");
261
262                            if (xx > 0) {
263                                String className = filename.substring(0, xx);
264
265                                // Skip some cases which do not work well
266                                for (int i = 0; i < SKIP_CASES.length; i++) {
267                                    if (dirName.equals(SKIP_CASES[i][0]) && filename.equals(SKIP_CASES[i][1])) {
268                                        logln("Skipping test case - " + dirName + "/" + className);
269                                        continue element_loop;
270                                    }
271                                }
272
273                                // The InputStream object returned by JarFile.getInputStream() will
274                                // no longer be useable after JarFile.close() has been called. It's
275                                // therefore necessary to make a copy of it here.
276                                target.add(className, copyInputStream(jarFile.getInputStream(entry)));
277                            }
278                        }
279                    }
280                }
281            } finally {
282                jarFile.close();
283            }
284        } catch (Exception e) {
285            errln("jar error: " + e.getMessage());
286        }
287
288        return target;
289    }
290
291    protected Target getTargets(String targetName)
292    {
293        URL dataURL = getClass().getResource("data");
294        String protocol = dataURL.getProtocol();
295
296        if (protocol.equals("jar")) {
297            return getJarTargets(dataURL);
298        } else if (protocol.equals("file")) {
299            return getFileTargets(dataURL);
300        } else {
301            errln("Don't know how to test " + dataURL);
302            return null;
303        }
304    }
305
306    public static void main(String[] args)
307    {
308        CompatibilityTest test = new CompatibilityTest();
309
310        test.run(args);
311    }
312}
313