1 package com.atlassian.webtest.ui.keys;
2
3 import com.google.common.base.Function;
4 import com.google.common.collect.ImmutableMap;
5 import com.google.common.collect.Lists;
6 import com.google.common.collect.Maps;
7
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.Collection;
11 import java.util.EnumSet;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15
16 import static com.google.common.base.Preconditions.checkNotNull;
17
18
19
20
21
22 public final class KeySequenceBuilder implements KeyEventAware
23 {
24 private static final int ASCII_CACHE_START = 40;
25 private static final int ASCII_CACHE_END = 125;
26
27 private static final Map<Character, CharacterKey> KEY_CACHE = createKeyCache();
28
29 private static Map<Character, CharacterKey> createKeyCache()
30 {
31 Map<Character, CharacterKey> cache = Maps.newHashMap();
32 for (char ch= ASCII_CACHE_START; ch<=ASCII_CACHE_END; ch++)
33 {
34 cache.put(ch, new DefaultCharacterKey(ch));
35 }
36 return ImmutableMap.copyOf(cache);
37 }
38
39 private final List<Key> keys = new ArrayList<Key>();
40 private final List<Character> charKeys = new ArrayList<Character>();
41 private final EnumSet<ModifierKey> pressed = EnumSet.noneOf(ModifierKey.class);
42 private EnumSet<KeyEventType> events = EnumSet.allOf(KeyEventType.class);
43 private TypeMode typeMode = TypeMode.DEFAULT;
44
45 public KeySequenceBuilder()
46 {
47 }
48
49 public KeySequenceBuilder(List<Key> keys)
50 {
51 this.keys.addAll(keys);
52 }
53
54 public KeySequenceBuilder(Key... keys)
55 {
56 this(Arrays.asList(keys));
57 }
58
59 public KeySequenceBuilder(String characterKeys)
60 {
61 append(characterKeys);
62 }
63
64 public KeySequenceBuilder append(String characterKeys)
65 {
66 for (char ch : characterKeys.toCharArray())
67 {
68 keys.add(keyFor(ch));
69 charKeys.add(ch);
70 }
71 return this;
72 }
73
74 public KeySequenceBuilder append(Key... keys)
75 {
76 this.keys.addAll(Arrays.asList(keys));
77 return this;
78 }
79
80 public KeySequenceBuilder append(Collection<Key> keys)
81 {
82 this.keys.addAll(keys);
83 return this;
84 }
85
86 public KeySequenceBuilder withPressed(ModifierKey key)
87 {
88 pressed.add(key);
89 return this;
90 }
91
92 public KeySequenceBuilder keyEvents(KeyEventType... events)
93 {
94 this.events = toEnumSet(Arrays.asList(events));
95 return this;
96 }
97
98 public KeySequenceBuilder keyEvents(Collection<KeyEventType> events)
99 {
100 this.events = toEnumSet(events);
101 return this;
102 }
103
104 public Set<KeyEventType> keyEvents()
105 {
106 return events.clone();
107 }
108
109 private EnumSet<KeyEventType> toEnumSet(Collection<KeyEventType> events)
110 {
111 return events.isEmpty() ? EnumSet.noneOf(KeyEventType.class) : EnumSet.copyOf(events);
112 }
113
114 public KeySequenceBuilder typeMode(TypeMode typeMode)
115 {
116 this.typeMode = checkNotNull(typeMode);
117 return this;
118 }
119
120 public int size()
121 {
122 return keys.size();
123 }
124
125 public KeySequence build()
126 {
127 if (keys.size() == charKeys.size())
128 {
129 return new CharacterKeySequence(buildString(), typeMode, pressed, events);
130 }
131 if (specialKeysOnly(keys))
132 {
133 return new SpecialKeysSequence(toSpecialKeys(), typeMode, pressed, events);
134 }
135 return new DefaultKeySequence(keys, typeMode, pressed, events);
136 }
137
138 private boolean specialKeysOnly(final List<Key> keys)
139 {
140 for (Key key : keys)
141 {
142 if (!SpecialKeys.class.isInstance(key))
143 {
144 return false;
145 }
146 }
147 return true;
148 }
149
150 private List<SpecialKeys> toSpecialKeys()
151 {
152 return Lists.transform(keys, new Function<Key,SpecialKeys>()
153 {
154 public SpecialKeys apply(Key input)
155 {
156 return (SpecialKeys) input;
157 }
158 });
159 }
160
161 private String buildString()
162 {
163 StringBuilder answer = new StringBuilder(charKeys.size());
164 for (Character character : charKeys)
165 {
166 answer.append(character);
167 }
168 return answer.toString();
169 }
170
171
172 static Key keyFor(char ch)
173 {
174 Key fromCache = KEY_CACHE.get(ch);
175 if (fromCache != null)
176 {
177 return fromCache;
178 }
179 else
180 {
181 return new DefaultCharacterKey(ch);
182 }
183 }
184
185
186
187 }