1c134b29628280a680fdf2696480166de56074b1eKevin Jin/*
2c134b29628280a680fdf2696480166de56074b1eKevin Jin * Copyright (C) 2013 DroidDriver committers
3c134b29628280a680fdf2696480166de56074b1eKevin Jin *
4c134b29628280a680fdf2696480166de56074b1eKevin Jin * Licensed under the Apache License, Version 2.0 (the "License");
5c134b29628280a680fdf2696480166de56074b1eKevin Jin * you may not use this file except in compliance with the License.
6c134b29628280a680fdf2696480166de56074b1eKevin Jin * You may obtain a copy of the License at
7c134b29628280a680fdf2696480166de56074b1eKevin Jin *
8c134b29628280a680fdf2696480166de56074b1eKevin Jin *      http://www.apache.org/licenses/LICENSE-2.0
9c134b29628280a680fdf2696480166de56074b1eKevin Jin *
10c134b29628280a680fdf2696480166de56074b1eKevin Jin * Unless required by applicable law or agreed to in writing, software
11c134b29628280a680fdf2696480166de56074b1eKevin Jin * distributed under the License is distributed on an "AS IS" BASIS,
12c134b29628280a680fdf2696480166de56074b1eKevin Jin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c134b29628280a680fdf2696480166de56074b1eKevin Jin * See the License for the specific language governing permissions and
14c134b29628280a680fdf2696480166de56074b1eKevin Jin * limitations under the License.
15c134b29628280a680fdf2696480166de56074b1eKevin Jin */
167576fbbba2bf515908b45293b7156b5bfe088938Kevin Jinpackage com.google.android.droiddriver.finders;
17c134b29628280a680fdf2696480166de56074b1eKevin Jin
1879884ac460f8d357c28091ec3f42fe369964b809Kevin Jinimport android.util.Log;
1979884ac460f8d357c28091ec3f42fe369964b809Kevin Jin
2079884ac460f8d357c28091ec3f42fe369964b809Kevin Jinimport com.google.android.droiddriver.UiElement;
21f50519233078e65a056cff49d7b4989d57c3e750Kevin Jinimport com.google.android.droiddriver.base.BaseUiElement;
22c134b29628280a680fdf2696480166de56074b1eKevin Jinimport com.google.android.droiddriver.exceptions.DroidDriverException;
2379884ac460f8d357c28091ec3f42fe369964b809Kevin Jinimport com.google.android.droiddriver.exceptions.ElementNotFoundException;
246316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jinimport com.google.android.droiddriver.util.FileUtils;
2579884ac460f8d357c28091ec3f42fe369964b809Kevin Jinimport com.google.android.droiddriver.util.Logs;
2617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport com.google.android.droiddriver.util.Preconditions;
2717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport com.google.android.droiddriver.util.Strings;
28c134b29628280a680fdf2696480166de56074b1eKevin Jin
2979884ac460f8d357c28091ec3f42fe369964b809Kevin Jinimport org.w3c.dom.DOMException;
3079884ac460f8d357c28091ec3f42fe369964b809Kevin Jinimport org.w3c.dom.Document;
3179884ac460f8d357c28091ec3f42fe369964b809Kevin Jinimport org.w3c.dom.Element;
3279884ac460f8d357c28091ec3f42fe369964b809Kevin Jin
336316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jinimport java.io.BufferedOutputStream;
3417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport java.util.HashMap;
3517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jinimport java.util.Map;
366316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin
3779884ac460f8d357c28091ec3f42fe369964b809Kevin Jinimport javax.xml.parsers.DocumentBuilderFactory;
3879884ac460f8d357c28091ec3f42fe369964b809Kevin Jinimport javax.xml.parsers.ParserConfigurationException;
396316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jinimport javax.xml.transform.OutputKeys;
406316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jinimport javax.xml.transform.Transformer;
416316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jinimport javax.xml.transform.TransformerFactory;
426316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jinimport javax.xml.transform.dom.DOMSource;
436316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jinimport javax.xml.transform.stream.StreamResult;
44fc21930b0a5cc598f59c0c893274e4ce33abd235Kevin Jinimport javax.xml.xpath.XPath;
4579884ac460f8d357c28091ec3f42fe369964b809Kevin Jinimport javax.xml.xpath.XPathConstants;
46c134b29628280a680fdf2696480166de56074b1eKevin Jinimport javax.xml.xpath.XPathExpression;
47c134b29628280a680fdf2696480166de56074b1eKevin Jinimport javax.xml.xpath.XPathExpressionException;
48c134b29628280a680fdf2696480166de56074b1eKevin Jinimport javax.xml.xpath.XPathFactory;
49c134b29628280a680fdf2696480166de56074b1eKevin Jin
50fc21930b0a5cc598f59c0c893274e4ce33abd235Kevin Jin/**
51fc21930b0a5cc598f59c0c893274e4ce33abd235Kevin Jin * Find matching UiElement by XPath.
52fc21930b0a5cc598f59c0c893274e4ce33abd235Kevin Jin */
537576fbbba2bf515908b45293b7156b5bfe088938Kevin Jinpublic class ByXPath implements Finder {
54fc21930b0a5cc598f59c0c893274e4ce33abd235Kevin Jin  private static final XPath XPATH_COMPILER = XPathFactory.newInstance().newXPath();
5579884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  // document needs to be static so that when buildDomNode is called recursively
5679884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  // on children they are in the same document to be appended.
5779884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  private static Document document;
5817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin  // The two maps should be kept in sync
5974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin  private static final Map<BaseUiElement<?, ?>, Element> TO_DOM_MAP =
6074676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin      new HashMap<BaseUiElement<?, ?>, Element>();
6174676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin  private static final Map<Element, BaseUiElement<?, ?>> FROM_DOM_MAP =
6274676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin      new HashMap<Element, BaseUiElement<?, ?>>();
63c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin
64c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin  public static void clearData() {
6517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    TO_DOM_MAP.clear();
6617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    FROM_DOM_MAP.clear();
67c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin    document = null;
68c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin  }
69c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin
708e4d4bb6a6c2b2e4a470c0804833de2a92f154d5Kevin Jin  private final String xPathString;
71c134b29628280a680fdf2696480166de56074b1eKevin Jin  private final XPathExpression xPathExpression;
72c134b29628280a680fdf2696480166de56074b1eKevin Jin
738e4d4bb6a6c2b2e4a470c0804833de2a92f154d5Kevin Jin  protected ByXPath(String xPathString) {
748e4d4bb6a6c2b2e4a470c0804833de2a92f154d5Kevin Jin    this.xPathString = Preconditions.checkNotNull(xPathString);
75c134b29628280a680fdf2696480166de56074b1eKevin Jin    try {
76fc21930b0a5cc598f59c0c893274e4ce33abd235Kevin Jin      xPathExpression = XPATH_COMPILER.compile(xPathString);
77c134b29628280a680fdf2696480166de56074b1eKevin Jin    } catch (XPathExpressionException e) {
786316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin      throw new DroidDriverException("xPathString=" + xPathString, e);
79c134b29628280a680fdf2696480166de56074b1eKevin Jin    }
80c134b29628280a680fdf2696480166de56074b1eKevin Jin  }
81c134b29628280a680fdf2696480166de56074b1eKevin Jin
82c134b29628280a680fdf2696480166de56074b1eKevin Jin  @Override
83c134b29628280a680fdf2696480166de56074b1eKevin Jin  public String toString() {
8417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    return Strings.toStringHelper(this).addValue(xPathString).toString();
85c134b29628280a680fdf2696480166de56074b1eKevin Jin  }
86c134b29628280a680fdf2696480166de56074b1eKevin Jin
8779884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  @Override
8879884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  public UiElement find(UiElement context) {
8974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin    Element domNode = getDomNode((BaseUiElement<?, ?>) context, UiElement.VISIBLE);
9079884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    try {
9179884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      getDocument().appendChild(domNode);
9279884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      Element foundNode = (Element) xPathExpression.evaluate(domNode, XPathConstants.NODE);
9379884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      if (foundNode == null) {
946316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin        Logs.log(Log.DEBUG, "XPath evaluation returns null for " + xPathString);
9579884ac460f8d357c28091ec3f42fe369964b809Kevin Jin        throw new ElementNotFoundException(this);
9679884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      }
97eb4e1921c193bb90eb0122ea7b0fd37cef60e8e1Kevin Jin
9817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      UiElement match = FROM_DOM_MAP.get(foundNode);
99eb4e1921c193bb90eb0122ea7b0fd37cef60e8e1Kevin Jin      Logs.log(Log.INFO, "Found match: " + match);
100eb4e1921c193bb90eb0122ea7b0fd37cef60e8e1Kevin Jin      return match;
10179884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    } catch (XPathExpressionException e) {
10279884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      throw new ElementNotFoundException(this, e);
10379884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    } finally {
10479884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      try {
10579884ac460f8d357c28091ec3f42fe369964b809Kevin Jin        getDocument().removeChild(domNode);
1060b9344441daed36d371df59ca4735d1e0e008189Kevin Jin      } catch (DOMException e) {
1070b9344441daed36d371df59ca4735d1e0e008189Kevin Jin        Logs.log(Log.ERROR, e, "Failed to clear document");
10879884ac460f8d357c28091ec3f42fe369964b809Kevin Jin        document = null; // getDocument will create new
10979884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      }
11079884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    }
11179884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  }
11279884ac460f8d357c28091ec3f42fe369964b809Kevin Jin
11379884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  private static Document getDocument() {
11479884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    if (document == null) {
11579884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      try {
11679884ac460f8d357c28091ec3f42fe369964b809Kevin Jin        document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
11779884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      } catch (ParserConfigurationException e) {
11879884ac460f8d357c28091ec3f42fe369964b809Kevin Jin        throw new DroidDriverException(e);
11979884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      }
12079884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    }
12179884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    return document;
12279884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  }
12379884ac460f8d357c28091ec3f42fe369964b809Kevin Jin
12479884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  /**
125c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin   * Returns the DOM node representing this UiElement.
12679884ac460f8d357c28091ec3f42fe369964b809Kevin Jin   */
12774676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin  private static Element getDomNode(BaseUiElement<?, ?> uiElement,
12874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin      Predicate<? super UiElement> predicate) {
12917342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    Element domNode = TO_DOM_MAP.get(uiElement);
130c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin    if (domNode == null) {
131c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin      domNode = buildDomNode(uiElement, predicate);
132c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin    }
133c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin    return domNode;
134c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin  }
135c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin
13674676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin  private static Element buildDomNode(BaseUiElement<?, ?> uiElement,
137c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin      Predicate<? super UiElement> predicate) {
13879884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    String className = uiElement.getClassName();
13979884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    if (className == null) {
14079884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      className = "UNKNOWN";
14179884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    }
14279884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    Element element = getDocument().createElement(XPaths.tag(className));
14317342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    TO_DOM_MAP.put(uiElement, element);
14417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin    FROM_DOM_MAP.put(element, uiElement);
14579884ac460f8d357c28091ec3f42fe369964b809Kevin Jin
14679884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.CLASS, className);
14779884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.RESOURCE_ID, uiElement.getResourceId());
14879884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.PACKAGE, uiElement.getPackageName());
14979884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.CONTENT_DESC, uiElement.getContentDescription());
15079884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.TEXT, uiElement.getText());
15179884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.CHECKABLE, uiElement.isCheckable());
15279884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.CHECKED, uiElement.isChecked());
15379884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.CLICKABLE, uiElement.isClickable());
15479884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.ENABLED, uiElement.isEnabled());
15579884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.FOCUSABLE, uiElement.isFocusable());
15679884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.FOCUSED, uiElement.isFocused());
15779884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.SCROLLABLE, uiElement.isScrollable());
15879884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.LONG_CLICKABLE, uiElement.isLongClickable());
15979884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.PASSWORD, uiElement.isPassword());
1605c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin    if (uiElement.hasSelection()) {
1615c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin      element.setAttribute(Attribute.SELECTION_START.getName(),
1625c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin          Integer.toString(uiElement.getSelectionStart()));
1635c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin      element.setAttribute(Attribute.SELECTION_END.getName(),
1645c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin          Integer.toString(uiElement.getSelectionEnd()));
1655c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin    }
16679884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    setAttribute(element, Attribute.SELECTED, uiElement.isSelected());
16779884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    element.setAttribute(Attribute.BOUNDS.getName(), uiElement.getBounds().toShortString());
16879884ac460f8d357c28091ec3f42fe369964b809Kevin Jin
169d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin    // If we're dumping for debugging, add extra information
170d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin    if (!UiElement.VISIBLE.equals(predicate)) {
171d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin      if (!uiElement.isVisible()) {
172d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin        element.setAttribute(BaseUiElement.ATTRIB_NOT_VISIBLE, "");
1735c9d7f7e2db9c7dd52543e455abff0449e21a90bKevin Jin      } else if (!uiElement.getVisibleBounds().equals(uiElement.getBounds())) {
174d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin        element.setAttribute(BaseUiElement.ATTRIB_VISIBLE_BOUNDS, uiElement.getVisibleBounds()
175d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin            .toShortString());
176d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin      }
177d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin    }
178d2abd0b28789a4a187343b0485e2b8e3fc9ef7acKevin Jin
17974676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin    for (BaseUiElement<?, ?> child : uiElement.getChildren(predicate)) {
180c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin      element.appendChild(getDomNode(child, predicate));
18179884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    }
18279884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    return element;
18379884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  }
18479884ac460f8d357c28091ec3f42fe369964b809Kevin Jin
18579884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  private static void setAttribute(Element element, Attribute attr, String value) {
18679884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    if (value != null) {
18779884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      element.setAttribute(attr.getName(), value);
18879884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    }
189c134b29628280a680fdf2696480166de56074b1eKevin Jin  }
1908e4d4bb6a6c2b2e4a470c0804833de2a92f154d5Kevin Jin
19179884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  // add attribute only if it's true
19279884ac460f8d357c28091ec3f42fe369964b809Kevin Jin  private static void setAttribute(Element element, Attribute attr, boolean value) {
19379884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    if (value) {
19479884ac460f8d357c28091ec3f42fe369964b809Kevin Jin      element.setAttribute(attr.getName(), "");
19579884ac460f8d357c28091ec3f42fe369964b809Kevin Jin    }
1968e4d4bb6a6c2b2e4a470c0804833de2a92f154d5Kevin Jin  }
1976316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin
19874676fdd3c8a9e599eddd13bea56898674d9916aKevin Jin  public static boolean dumpDom(String path, BaseUiElement<?, ?> uiElement) {
1996316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin    BufferedOutputStream bos = null;
2006316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin    try {
2016316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin      bos = FileUtils.open(path);
2026316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin      Transformer transformer = TransformerFactory.newInstance().newTransformer();
2036316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin      transformer.setOutputProperty(OutputKeys.INDENT, "yes");
20417342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      // find() filters invisible UiElements, but this is for debugging and
20517342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      // invisible UiElements may be of interest.
20617342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      clearData();
20717342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      Element domNode = getDomNode(uiElement, null);
20817342a5115d7575d44a99fed9c7032e3ab316dccKevin Jin      transformer.transform(new DOMSource(domNode), new StreamResult(bos));
2096316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin      Logs.log(Log.INFO, "Wrote dom to " + path);
2106316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin    } catch (Exception e) {
2110b9344441daed36d371df59ca4735d1e0e008189Kevin Jin      Logs.log(Log.ERROR, e, "Failed to transform node");
2126316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin      return false;
2136316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin    } finally {
214c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin      // We built DOM with invisible UiElements. Don't use it for find()!
215c39b04e0e5d3962153cd860d1430857fe625da90Kevin Jin      clearData();
2166316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin      if (bos != null) {
2176316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin        try {
2186316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin          bos.close();
2196316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin        } catch (Exception e) {
2206316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin          // ignore
2216316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin        }
2226316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin      }
2236316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin    }
2246316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin    return true;
2256316362de61fca700d7d5a455ad5c0ac9717c365Kevin Jin  }
226c134b29628280a680fdf2696480166de56074b1eKevin Jin}
227