17674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa/* 27674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * Copyright (C) 2009 The Android Open Source Project 37674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * 47674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License"); 57674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * you may not use this file except in compliance with the License. 67674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * You may obtain a copy of the License at 77674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * 87674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * http://www.apache.org/licenses/LICENSE-2.0 97674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * 107674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software 117674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS, 127674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * See the License for the specific language governing permissions and 147674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * limitations under the License. 157674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa */ 161a44d5dcabc18cd5ef111f732ccff91683a1a093Neal Nguyenpackage android.pim.vcard; 177674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 187674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawaimport android.content.ContentValues; 195a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawaimport android.pim.vcard.VCardEntry; 205a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawaimport android.util.Log; 217674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 227674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawaimport java.util.ArrayList; 237674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawaimport java.util.Arrays; 247674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawaimport java.util.HashSet; 257674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawaimport java.util.List; 267674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawaimport java.util.Set; 277674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 287674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa/** 295c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa * Previously used in main vCard handling code but now exists only for testing. 305c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa * 315c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa * Especially useful for testing parser code (VCardParser), since all properties can be 325a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa * checked via this class unlike {@link VCardEntry}, which only emits the result of 335c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa * interpretation of the content of each vCard. We cannot know whether vCard parser or 345c3e687965a49e4e54196a049337544a6eed61d9Daisuke Miyakawa * ContactStruct is wrong withouth this class. 357674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa */ 367674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawapublic class PropertyNode { 377674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa public String propName; 387674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa public String propValue; 397674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa public List<String> propValue_vector; 407674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 417674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa /** Store value as byte[],after decode. 427674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * Used when propValue is encoded by something like BASE64, QUOTED-PRINTABLE, etc. 437674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa */ 447674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa public byte[] propValue_bytes; 457674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 467674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa /** param store: key=paramType, value=paramValue 477674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * Note that currently PropertyNode class does not support multiple param-values 487674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * defined in vCard 3.0 (See also RFC 2426). multiple-values are stored as 497674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * one String value like "A,B", not ["A", "B"]... 507674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa * TODO: fix this. 517674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa */ 527674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa public ContentValues paramMap; 537674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 547674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa /** Only for TYPE=??? param store. */ 557674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa public Set<String> paramMap_TYPE; 567674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 577674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa /** Store group values. Used only in VCard. */ 587674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa public Set<String> propGroupSet; 597674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 607674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa public PropertyNode() { 617674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa propName = ""; 627674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa propValue = ""; 637674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa propValue_vector = new ArrayList<String>(); 647674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa paramMap = new ContentValues(); 657674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa paramMap_TYPE = new HashSet<String>(); 667674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa propGroupSet = new HashSet<String>(); 677674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 687674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 697674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa public PropertyNode( 707674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa String propName, String propValue, List<String> propValue_vector, 717674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa byte[] propValue_bytes, ContentValues paramMap, Set<String> paramMap_TYPE, 727674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa Set<String> propGroupSet) { 737674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (propName != null) { 747674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.propName = propName; 757674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } else { 767674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.propName = ""; 777674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 787674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (propValue != null) { 797674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.propValue = propValue; 807674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } else { 817674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.propValue = ""; 827674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 837674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (propValue_vector != null) { 847674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.propValue_vector = propValue_vector; 857674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } else { 867674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.propValue_vector = new ArrayList<String>(); 877674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 887674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.propValue_bytes = propValue_bytes; 897674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (paramMap != null) { 907674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.paramMap = paramMap; 917674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } else { 927674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.paramMap = new ContentValues(); 937674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 947674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (paramMap_TYPE != null) { 957674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.paramMap_TYPE = paramMap_TYPE; 967674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } else { 977674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.paramMap_TYPE = new HashSet<String>(); 987674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 997674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (propGroupSet != null) { 1007674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.propGroupSet = propGroupSet; 1017674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } else { 1027674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa this.propGroupSet = new HashSet<String>(); 1037674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 1047674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 1057674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 1067674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa @Override 107ba2b21bee817c58e9b22c57e02ef6201e1cda1bdDaisuke Miyakawa public int hashCode() { 108ba2b21bee817c58e9b22c57e02ef6201e1cda1bdDaisuke Miyakawa // vCard may contain more than one same line in one entry, while HashSet or any other 109ba2b21bee817c58e9b22c57e02ef6201e1cda1bdDaisuke Miyakawa // library which utilize hashCode() does not honor that, so intentionally throw an 110ba2b21bee817c58e9b22c57e02ef6201e1cda1bdDaisuke Miyakawa // Exception. 111ba2b21bee817c58e9b22c57e02ef6201e1cda1bdDaisuke Miyakawa throw new UnsupportedOperationException( 112ba2b21bee817c58e9b22c57e02ef6201e1cda1bdDaisuke Miyakawa "PropertyNode does not provide hashCode() implementation intentionally."); 113ba2b21bee817c58e9b22c57e02ef6201e1cda1bdDaisuke Miyakawa } 114ba2b21bee817c58e9b22c57e02ef6201e1cda1bdDaisuke Miyakawa 115ba2b21bee817c58e9b22c57e02ef6201e1cda1bdDaisuke Miyakawa @Override 1167674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa public boolean equals(Object obj) { 1177674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (!(obj instanceof PropertyNode)) { 1187674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa return false; 1197674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 1207674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 1217674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa PropertyNode node = (PropertyNode)obj; 1227674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 1237674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (propName == null || !propName.equals(node.propName)) { 1247674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa return false; 125839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) { 1267674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa return false; 127839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa } else if (!paramMap_TYPE.equals(node.paramMap_TYPE)) { 1287674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa return false; 1297674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } else if (!propGroupSet.equals(node.propGroupSet)) { 1307674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa return false; 1317674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 132839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa 1337674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (propValue_bytes != null && Arrays.equals(propValue_bytes, node.propValue_bytes)) { 1347674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa return true; 1357674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } else { 1367674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (!propValue.equals(node.propValue)) { 1377674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa return false; 1387674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 1397674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 1407674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa // The value in propValue_vector is not decoded even if it should be 1417674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa // decoded by BASE64 or QUOTED-PRINTABLE. When the size of propValue_vector 1427674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa // is 1, the encoded value is stored in propValue, so we do not have to 1437674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa // check it. 1447674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa return (propValue_vector.equals(node.propValue_vector) || 1457674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa propValue_vector.size() == 1 || 1467674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa node.propValue_vector.size() == 1); 1477674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 1487674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 1497674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa 1507674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa @Override 1517674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa public String toString() { 1527674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa StringBuilder builder = new StringBuilder(); 1537674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa builder.append("propName: "); 1547674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa builder.append(propName); 1557674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa builder.append(", paramMap: "); 1567674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa builder.append(paramMap.toString()); 1575a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append(", paramMap_TYPE: ["); 1585a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa boolean first = true; 1595a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa for (String elem : paramMap_TYPE) { 1605a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa if (first) { 1615a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa first = false; 1625a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa } else { 1635a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append(", "); 1645a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa } 1655a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append('"'); 1665a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append(elem); 1675a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append('"'); 1685a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa } 1695a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append("]"); 1705a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa if (!propGroupSet.isEmpty()) { 1715a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append(", propGroupSet: ["); 172839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa first = true; 1735a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa for (String elem : propGroupSet) { 1745a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa if (first) { 1755a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa first = false; 1765a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa } else { 1775a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append(", "); 1785a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa } 1795a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append('"'); 1805a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append(elem); 1815a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append('"'); 1825a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa } 1835a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa builder.append("]"); 1845a1f2d2de026b582fbe8b1a46a83ad33760a2c48Daisuke Miyakawa } 1857674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (propValue_vector != null && propValue_vector.size() > 1) { 1867674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa builder.append(", propValue_vector size: "); 1877674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa builder.append(propValue_vector.size()); 1887674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 1897674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa if (propValue_bytes != null) { 1907674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa builder.append(", propValue_bytes size: "); 1917674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa builder.append(propValue_bytes.length); 1927674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 193839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa builder.append(", propValue: \""); 1947674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa builder.append(propValue); 195839c036444c8a5335cc557e174acc7ee28baafc4Daisuke Miyakawa builder.append("\""); 1967674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa return builder.toString(); 1977674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa } 1987674b81a05a8aa0b7a1be1bb04e041f6d2106fe2Daisuke Miyakawa} 199