1561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes/*
2561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *  Licensed to the Apache Software Foundation (ASF) under one or more
3561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *  contributor license agreements.  See the NOTICE file distributed with
4561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *  this work for additional information regarding copyright ownership.
5561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *  The ASF licenses this file to You under the Apache License, Version 2.0
6561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *  (the "License"); you may not use this file except in compliance with
7561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *  the License.  You may obtain a copy of the License at
8561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *
9561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *     http://www.apache.org/licenses/LICENSE-2.0
10561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *
11561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *  Unless required by applicable law or agreed to in writing, software
12561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *  distributed under the License is distributed on an "AS IS" BASIS,
13561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *  See the License for the specific language governing permissions and
15561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes *  limitations under the License.
16561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes */
17561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
18561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes/**
198d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath * @author Boris V. Kuznetsov
208d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath */
21561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
22561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughespackage java.security;
23561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
24561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughesimport java.io.FileInputStream;
25561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughesimport java.io.FileNotFoundException;
26561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughesimport java.io.IOException;
27561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughesimport java.util.HashMap;
28561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughesimport java.util.Iterator;
29561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughesimport java.util.Set;
30561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
31561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughesimport junit.framework.TestCase;
32561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
33561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughesimport org.apache.harmony.security.tests.support.SpiEngUtils;
34561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughesimport org.apache.harmony.security.tests.support.TestUtils;
35561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
36561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughesimport tests.support.resource.Support_Resources;
37561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
38561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
39561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes/**
40561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes * Tests for <code>Provider</code> constructor and methods
41561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes */
42561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughespublic class ProviderTest extends TestCase {
43561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
44561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    Provider p;
458d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath
46561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    /*
478d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath    * @see TestCase#setUp()
488d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath    */
49561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    protected void setUp() throws Exception {
50561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        super.setUp();
51561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p = new MyProvider();
52561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    }
538d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath
54561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    /*
558d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath    * Class under test for void load(InputStream)
568d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath    */
57561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    public final void testLoadInputStream() throws IOException {
58561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
59561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p.load(Support_Resources
608d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath                .getResourceStream("java/security/Provider.prop.dat"));
61561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
62561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        if (!"value 1".equals(p.getProperty("Property 1").trim()) ||
638d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath                !"className".equals(p.getProperty("serviceName.algName").trim()) ||
64561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                !"attrValue".equals(p.getProperty("serviceName.algName attrName").trim()) ||
65561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                !"standardName".equals(p.getProperty("Alg.Alias.engineClassName.aliasName").trim()) ||
66561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                !String.valueOf(p.getName()).equals(p.getProperty("Provider.id name").trim()) ||
67561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                !String.valueOf(p.getVersion()).equals(p.getProperty("Provider.id version").trim()) ||
68561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                !String.valueOf(p.getInfo()).equals(p.getProperty("Provider.id info").trim()) ||
69561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                !p.getClass().getName().equals(p.getProperty("Provider.id className").trim()) ||
708d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath                !"SomeClassName".equals(p.getProperty("MessageDigest.SHA-1").trim())) {
71561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("Incorrect property value");
72561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
73561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    }
74561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
75561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    public final void testGetService() {
768d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath        try {
77561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            p.getService(null, "algorithm");
78561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("No expected NullPointerException");
79561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        } catch (NullPointerException e) {
80561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
818d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath        try {
82561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            p.getService("type", null);
83561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("No expected NullPointerException");
84561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        } catch (NullPointerException e) {
85561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
868d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath
87561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        Provider.Service s = new Provider.Service(p, "Type", "Algorithm",
88561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                "className", null, null);
89561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p.putService(s);
908d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath
91561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        if (p.getService("Type", "AlgoRithM") != s) {
92561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("Case 1. getService() failed");
93561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
948d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath
95561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        Provider.Service s1 = p.getService("MessageDigest", "AbC");
96561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        if (s1 == null) {
978d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath            fail("Case 2. getService() failed");
98561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
998d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath
100561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        s = new Provider.Service(p, "MessageDigest", "SHA-1",
101561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                "className", null, null);
102561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p.putService(s);
103561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        if (s1 == p.getService("MessageDigest", "SHA-1")) {
104561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("Case 3. getService() failed");
105561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
1068d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath
107561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        if (p.getService("MessageDigest", "SHA1") == null) {
108561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("Case 4. getService() failed");
109561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
110561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    }
111561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
112561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    public final void testGetServices() {
113561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        Provider.Service s = new Provider.Service(p, "Type", "Algorithm",
114561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                "className", null, null);
115561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
116561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        // incomplete services should be removed
117561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p.put("serv.alg", "aaaaaaaaaaaaa");
118561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p.put("serv.alg KeySize", "11111");
119561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p.put("serv1.alg1 KeySize", "222222");
120561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p.remove("serv.alg");
1218d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath
122561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p.putService(s);
123561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        Set services = p.getServices();
124561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        if (services.size() != 3) {
125561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("incorrect size");
126561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
1278d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath        for (Iterator it = services.iterator(); it.hasNext(); ) {
1288d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath            s = (Provider.Service) it.next();
129561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            if ("Type".equals(s.getType()) &&
130561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                    "Algorithm".equals(s.getAlgorithm()) &&
131561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                    "className".equals(s.getClassName())) {
132561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                continue;
133561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            }
134561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            if ("MessageDigest".equals(s.getType()) &&
135561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                    "SHA-1".equals(s.getAlgorithm()) &&
136561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                    "SomeClassName".equals(s.getClassName())) {
137561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                continue;
138561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            }
139561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            if ("MessageDigest".equals(s.getType()) &&
140561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                    "abc".equals(s.getAlgorithm()) &&
141561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                    "SomeClassName".equals(s.getClassName())) {
142561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                continue;
1438d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath            }
144561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("Incorrect service");
145561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
146561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    }
147561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
148561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    public final void testPutService() {
149561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        HashMap hm = new HashMap();
150561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        hm.put("KeySize", "1024");
151561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        hm.put("AAA", "BBB");
152561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        Provider.Service s = new Provider.Service(p, "Type", "Algorithm",
153561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                "className", null, hm);
154561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p.putService(s);
1558d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath        if (s != p.getService("Type", "Algorithm")) {
156561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("putService failed");
157561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
158561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        if (!"className".equals(p.getProperty("Type.Algorithm"))) {
159561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("incorrect className");
160561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
161561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        if (!"1024".equals(p.getProperty("Type.Algorithm KeySize"))) {
162561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("incorrect attribute");
1638d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath        }
164561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    }
165561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
166561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    public final void testRemoveService() {
167561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        Provider.Service s = new Provider.Service(p, "Type", "Algorithm",
168561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                "className", null, null);
169561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p.putService(s);
170561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        p.removeService(s);
171561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        Set services = p.getServices();
172561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        if (services.size() != 2) {
173561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("incorrect size");
174561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
1758d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath
1768d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath        for (Iterator it = services.iterator(); it.hasNext(); ) {
1778d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath            s = (Provider.Service) it.next();
178561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            if ("MessageDigest".equals(s.getType()) &&
179561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                    "SHA-1".equals(s.getAlgorithm()) &&
180561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                    "SomeClassName".equals(s.getClassName())) {
181561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                continue;
182561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            }
183561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            if ("MessageDigest".equals(s.getType()) &&
184561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                    "abc".equals(s.getAlgorithm()) &&
185561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                    "SomeClassName".equals(s.getClassName())) {
186561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes                continue;
187561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            }
188561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("Incorrect service");
189561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
1908d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath
191561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        if (p.getProperty("Type.Algorithm") != null) {
192561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            fail("incorrect property");
1938d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath        }
194561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    }
195561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes
196561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    class MyProvider extends Provider {
197561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        MyProvider() {
198561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            super("MyProvider", 1.0, "Provider for testing");
199561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            put("MessageDigest.SHA-1", "SomeClassName");
200561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            put("MessageDigest.abc", "SomeClassName");
201561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            put("Alg.Alias.MessageDigest.SHA1", "SHA-1");
202561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
2038d8858e39800de641b50f6e8e864af9cf68bedeaNarayan Kamath
204561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        MyProvider(String name, double version, String info) {
205561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes            super(name, version, info);
206561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes        }
207561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes    }
208561ee011997c6c2f1befbfaa9d5f0a99771c1d63Elliott Hughes}
209