|
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
|
2 // Use of this source code is governed by a BSD-style license that can be |
|
3 // found in the LICENSE file. |
|
4 |
|
5 package org.chromium.sdk.internal; |
|
6 |
|
7 import java.util.Collections; |
|
8 import java.util.Comparator; |
|
9 import java.util.List; |
|
10 import java.util.Map; |
|
11 import java.util.SortedMap; |
|
12 import java.util.TreeMap; |
|
13 |
|
14 import org.chromium.sdk.JsArray; |
|
15 import org.chromium.sdk.JsVariable; |
|
16 import org.chromium.sdk.internal.tools.v8.MethodIsBlockingException; |
|
17 |
|
18 /** |
|
19 * A generic implementation of the JsArray interface. |
|
20 */ |
|
21 class JsArrayImpl extends JsObjectImpl implements JsArray { |
|
22 |
|
23 /** |
|
24 * An indexed sparse array of elements. Keys are indices, values are elements. |
|
25 */ |
|
26 private SortedMap<Integer, JsVariableImpl> indexToElementMap; |
|
27 |
|
28 /** |
|
29 * This constructor implies lazy resolution of object properties. |
|
30 * |
|
31 * @param callFrame this array belongs in |
|
32 * @param parentFqn the fully qualified name of this array parent |
|
33 * @param valueState the mirror corresponding to this array |
|
34 */ |
|
35 JsArrayImpl(CallFrameImpl callFrame, String parentFqn, ValueMirror valueState) { |
|
36 super(callFrame, parentFqn, valueState); |
|
37 } |
|
38 |
|
39 private synchronized void ensureElementsMap() throws MethodIsBlockingException { |
|
40 if (indexToElementMap != null) { |
|
41 return; |
|
42 } |
|
43 SortedMap<Integer, JsVariableImpl> map = |
|
44 // TODO(peter.rybin): do we need this comparator at all? |
|
45 new TreeMap<Integer, JsVariableImpl>(new Comparator<Integer>() { |
|
46 public int compare(Integer o1, Integer o2) { |
|
47 return o1 - o2; |
|
48 } |
|
49 }); |
|
50 |
|
51 for (JsVariableImpl prop : getProperties()) { |
|
52 String name = prop.getRawName(); |
|
53 Integer index = getAsArrayIndex(name); |
|
54 if (index == null) { |
|
55 continue; |
|
56 } |
|
57 map.put(index, prop); |
|
58 } |
|
59 indexToElementMap = Collections.unmodifiableSortedMap(map); |
|
60 } |
|
61 |
|
62 public JsVariable get(int index) throws MethodIsBlockingException { |
|
63 ensureElementsMap(); |
|
64 return indexToElementMap.get(index); |
|
65 } |
|
66 |
|
67 public SortedMap<Integer, ? extends JsVariable> toSparseArray() throws MethodIsBlockingException { |
|
68 ensureElementsMap(); |
|
69 return indexToElementMap; |
|
70 } |
|
71 |
|
72 public int length() { |
|
73 // TODO(peter.rybin) optimize it: either read "length" from remote or count PropertyReference |
|
74 // rather than JsVariableImpl |
|
75 int lastIndex = -1; |
|
76 List<JsVariableImpl> properties = getSubpropertiesHelper().getPropertiesLazily(); |
|
77 // TODO(peter.rybin): rename propRefs |
|
78 for (JsVariableImpl prop : properties) { |
|
79 String name = prop.getRawName(); |
|
80 Integer index = getAsArrayIndex(name); |
|
81 if (index == null) { |
|
82 continue; |
|
83 } |
|
84 if (index > lastIndex) { |
|
85 lastIndex = index; |
|
86 } |
|
87 } |
|
88 return lastIndex + 1; |
|
89 } |
|
90 |
|
91 @Override |
|
92 public String toString() { |
|
93 SortedMap<Integer, ? extends JsVariable> elements; |
|
94 try { |
|
95 elements = toSparseArray(); |
|
96 } catch (MethodIsBlockingException e) { |
|
97 return "[JsArray: Exception in retrieving data]"; |
|
98 } |
|
99 StringBuilder result = new StringBuilder(); |
|
100 result.append("[JsArray: length=").append(elements.size()); |
|
101 for (Map.Entry<Integer, ? extends JsVariable> entry : elements.entrySet()) { |
|
102 result.append(',').append(entry.getKey()).append('=').append(entry.getValue()); |
|
103 } |
|
104 result.append(']'); |
|
105 return result.toString(); |
|
106 } |
|
107 |
|
108 @Override |
|
109 public JsArrayImpl asArray() { |
|
110 return this; |
|
111 } |
|
112 |
|
113 @Override |
|
114 protected JsVariableImpl.NameDecorator getChildPropertyNameDecorator() { |
|
115 return ARRAY_ELEMENT_DECORATOR; |
|
116 } |
|
117 |
|
118 /** |
|
119 * @return integer representation of the index or null if it is not an integer |
|
120 */ |
|
121 static Integer getAsArrayIndex(String varName) { |
|
122 if (!JsonUtil.isInteger(varName)) { |
|
123 return null; |
|
124 } |
|
125 try { |
|
126 int index = Integer.parseInt(varName); |
|
127 return index; |
|
128 } catch (NumberFormatException e) { |
|
129 return null; |
|
130 } |
|
131 } |
|
132 |
|
133 private final static JsVariableImpl.NameDecorator ARRAY_ELEMENT_DECORATOR = |
|
134 new JsVariableImpl.NameDecorator() { |
|
135 @Override |
|
136 String decorateVarName(String rawName) { |
|
137 Integer index = getAsArrayIndex(rawName); |
|
138 if (index == null) { |
|
139 return rawName; |
|
140 } |
|
141 // Fix array element indices |
|
142 return OPEN_BRACKET + rawName + CLOSE_BRACKET; |
|
143 } |
|
144 @Override |
|
145 String buildAccessSuffix(String rawName) { |
|
146 Integer index = getAsArrayIndex(rawName); |
|
147 if (index == null) { |
|
148 return NOOP.buildAccessSuffix(rawName); |
|
149 } |
|
150 return OPEN_BRACKET + rawName + CLOSE_BRACKET; |
|
151 } |
|
152 private static final String OPEN_BRACKET = "["; |
|
153 private static final String CLOSE_BRACKET = "]"; |
|
154 }; |
|
155 } |