1/*
2 * Copyright (C) 2009 The Android Open Source Project
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 */
16package com.android.vcard.tests.testutils;
17
18import android.content.ContentValues;
19
20import com.android.vcard.VCardEntry;
21
22import java.util.ArrayList;
23import java.util.Arrays;
24import java.util.HashSet;
25import java.util.List;
26import java.util.Set;
27
28/**
29 * <p>
30 * The class representing one property (e.g. "N;ENCODING=UTF-8:family:given:middle:prefix:suffix").
31 * </p>
32 * <p>
33 * Previously used in main vCard handling code but now exists only for testing.
34 * </p>
35 * <p>
36 * Especially useful for testing parser code (VCardParser), since all properties can be
37 * checked via this class unlike {@link VCardEntry}, which only emits the result of
38 * interpretation of the content of each vCard. We cannot know whether vCard parser or
39 * {@link VCardEntry} is wrong without this class.
40 * </p>
41 */
42public class PropertyNode {
43    public String propName;
44    public String propValue;
45    public List<String> propValue_vector;
46
47    /** Store value as byte[],after decode.
48     * Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc.
49     */
50    public byte[] propValue_bytes;
51
52    /**
53     * param store: key=paramType, value=paramValue
54     * Note that currently PropertyNode class does not support multiple param-values
55     * defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as
56     * one String value like "A,B", not ["A", "B"]...
57     * TODO: fix this.
58     */
59    public ContentValues paramMap;
60
61    /** Only for TYPE=??? param store. */
62    public Set<String> paramMap_TYPE;
63
64    /** Store group values. Used only in VCard. */
65    public Set<String> propGroupSet;
66
67    public PropertyNode() {
68        propName = "";
69        propValue = "";
70        propValue_vector = new ArrayList<String>();
71        paramMap = new ContentValues();
72        paramMap_TYPE = new HashSet<String>();
73        propGroupSet = new HashSet<String>();
74    }
75
76    public PropertyNode(
77            String propName, String propValue, List<String> propValue_vector,
78            byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE,
79            Set<String> propGroupSet) {
80        if (propName != null) {
81            this.propName = propName;
82        } else {
83            this.propName = "";
84        }
85        if (propValue != null) {
86            this.propValue = propValue;
87        } else {
88            this.propValue = "";
89        }
90        if (propValue_vector != null) {
91            this.propValue_vector = propValue_vector;
92        } else {
93            this.propValue_vector = new ArrayList<String>();
94        }
95        this.propValue_bytes = propValue_bytes;
96        if (paramMap != null) {
97            this.paramMap = paramMap;
98        } else {
99            this.paramMap = new ContentValues();
100        }
101        if (paramMap_TYPE != null) {
102            this.paramMap_TYPE = paramMap_TYPE;
103        } else {
104            this.paramMap_TYPE = new HashSet<String>();
105        }
106        if (propGroupSet != null) {
107            this.propGroupSet = propGroupSet;
108        } else {
109            this.propGroupSet = new HashSet<String>();
110        }
111    }
112
113    @Override
114    public int hashCode() {
115        // vCard may contain more than one same line in one entry, while HashSet or any other
116        // library which utilize hashCode() does not honor that, so intentionally throw an
117        // Exception.
118        throw new UnsupportedOperationException(
119                "PropertyNode does not provide hashCode() implementation intentionally.");
120    }
121
122    @Override
123    public boolean equals(Object obj) {
124        if (!(obj instanceof PropertyNode)) {
125            return false;
126        }
127
128        PropertyNode node = (PropertyNode)obj;
129
130        if (propName == null || !propName.equals(node.propName)) {
131            return false;
132        } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) {
133            return false;
134        } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) {
135            return false;
136        } else if (!propGroupSet.equals(node.propGroupSet)) {
137            return false;
138        }
139
140        if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) {
141            return true;
142        } else {
143            if (!propValue.equals(node.propValue)) {
144                return false;
145            }
146
147            // The value in propValue_vector is not decoded even if it should be
148            // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector
149            // is 1, the encoded value is stored in propValue, so we do not have to
150            // check it.
151            return (propValue_vector.equals(node.propValue_vector) ||
152                    propValue_vector.size() == 1 ||
153                    node.propValue_vector.size() == 1);
154        }
155    }
156
157    @Override
158    public String toString() {
159        StringBuilder builder = new StringBuilder();
160        builder.append("propName: ");
161        builder.append(propName);
162        builder.append(", paramMap: ");
163        builder.append(paramMap.toString());
164        builder.append(", paramMap_TYPE: [");
165        boolean first = true;
166        for (String elem : paramMap_TYPE) {
167            if (first) {
168                first = false;
169            } else {
170                builder.append(", ");
171            }
172            builder.append('"');
173            builder.append(elem);
174            builder.append('"');
175        }
176        builder.append("]");
177        if (!propGroupSet.isEmpty()) {
178            builder.append(", propGroupSet: [");
179            first = true;
180            for (String elem : propGroupSet) {
181                if (first) {
182                    first = false;
183                } else {
184                    builder.append(", ");
185                }
186                builder.append('"');
187                builder.append(elem);
188                builder.append('"');
189            }
190            builder.append("]");
191        }
192        if (propValue_vector != null && propValue_vector.size() > 1) {
193            builder.append(", propValue_vector size: ");
194            builder.append(propValue_vector.size());
195        }
196        if (propValue_bytes != null) {
197            builder.append(", propValue_bytes size: ");
198            builder.append(propValue_bytes.length);
199        }
200        builder.append(", propValue: \"");
201        builder.append(propValue);
202        builder.append("\"");
203        return builder.toString();
204    }
205}
206