You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DesignAttributeHandler.java 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * 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, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.ui.declarative;
  17. import java.beans.BeanInfo;
  18. import java.beans.IntrospectionException;
  19. import java.beans.Introspector;
  20. import java.beans.PropertyDescriptor;
  21. import java.io.File;
  22. import java.io.Serializable;
  23. import java.lang.reflect.Method;
  24. import java.text.DecimalFormat;
  25. import java.text.DecimalFormatSymbols;
  26. import java.util.ArrayList;
  27. import java.util.Arrays;
  28. import java.util.Collection;
  29. import java.util.Collections;
  30. import java.util.HashMap;
  31. import java.util.List;
  32. import java.util.Locale;
  33. import java.util.Map;
  34. import java.util.logging.Level;
  35. import java.util.logging.Logger;
  36. import org.jsoup.nodes.Attribute;
  37. import org.jsoup.nodes.Attributes;
  38. import org.jsoup.nodes.Element;
  39. import org.jsoup.nodes.Node;
  40. import com.vaadin.event.ShortcutAction;
  41. import com.vaadin.event.ShortcutAction.KeyCode;
  42. import com.vaadin.event.ShortcutAction.ModifierKey;
  43. import com.vaadin.server.ExternalResource;
  44. import com.vaadin.server.FileResource;
  45. import com.vaadin.server.FontAwesome;
  46. import com.vaadin.server.Resource;
  47. import com.vaadin.server.ThemeResource;
  48. import com.vaadin.shared.util.SharedUtil;
  49. import com.vaadin.ui.DesignSynchronizable;
  50. /**
  51. * Default attribute handler implementation used when parsing designs to
  52. * component trees. Handles all the component attributes that do not require
  53. * custom handling.
  54. *
  55. * @since 7.4
  56. * @author Vaadin Ltd
  57. */
  58. public class DesignAttributeHandler implements Serializable {
  59. protected static Logger getLogger() {
  60. return Logger.getLogger(DesignAttributeHandler.class.getName());
  61. }
  62. private static Map<Class, AttributeCacheEntry> cache = Collections
  63. .synchronizedMap(new HashMap<Class, AttributeCacheEntry>());
  64. /**
  65. * Clears the children and attributes of the given element
  66. *
  67. * @param design
  68. * the element to be cleared
  69. */
  70. public static void clearElement(Element design) {
  71. Attributes attr = design.attributes();
  72. for (Attribute a : attr.asList()) {
  73. attr.remove(a.getKey());
  74. }
  75. List<Node> children = new ArrayList<Node>();
  76. children.addAll(design.childNodes());
  77. for (Node node : children) {
  78. node.remove();
  79. }
  80. }
  81. /**
  82. * Assigns the specified design attribute to the given component. If the
  83. * attribute is not present, (value is null) the corresponding property is
  84. * got from the <code>defaultInstance</code>
  85. *
  86. * @param component
  87. * the component to which the attribute should be set
  88. * @param attribute
  89. * the attribute to be set
  90. * @param attributes
  91. * the attribute map. If the attributes does not contain the
  92. * requested attribute, the value is retrieved from the
  93. * <code> defaultInstance</code>
  94. * @param defaultInstance
  95. * the default instance of the class for fetching the default
  96. * values
  97. * @return true on success
  98. */
  99. public static boolean readAttribute(DesignSynchronizable component,
  100. String attribute, Attributes attributes,
  101. DesignSynchronizable defaultInstance) {
  102. String value = null;
  103. if (component == null || attribute == null || attributes == null
  104. || defaultInstance == null) {
  105. throw new IllegalArgumentException(
  106. "Parameters with null value not allowed");
  107. }
  108. if (attributes.hasKey(attribute)) {
  109. value = attributes.get(attribute);
  110. }
  111. boolean success = false;
  112. try {
  113. Method setter = findSetterForAttribute(component.getClass(),
  114. attribute);
  115. if (setter == null) {
  116. // if we don't have the setter, there is no point in continuing
  117. success = false;
  118. } else if (value != null) {
  119. // we have a value from design attributes, let's use that
  120. Object param = fromAttributeValue(
  121. setter.getParameterTypes()[0], value);
  122. setter.invoke(component, param);
  123. success = true;
  124. } else {
  125. // otherwise find the getter for the attribute
  126. Method getter = findGetterForAttribute(component.getClass(),
  127. attribute);
  128. // read the default value from defaults
  129. Object defaultValue = getter.invoke(defaultInstance);
  130. setter.invoke(component, defaultValue);
  131. success = true;
  132. }
  133. } catch (Exception e) {
  134. getLogger().log(Level.WARNING,
  135. "Failed to set attribute " + attribute, e);
  136. }
  137. if (!success) {
  138. getLogger().info(
  139. "property " + attribute
  140. + " ignored by default attribute handler");
  141. }
  142. return success;
  143. }
  144. /**
  145. * Searches for supported setter and getter types from the specified class
  146. * and returns the list of corresponding design attributes
  147. *
  148. * @param clazz
  149. * the class scanned for setters
  150. * @return the list of supported design attributes
  151. */
  152. public static Collection<String> getSupportedAttributes(Class<?> clazz) {
  153. resolveSupportedAttributes(clazz);
  154. return cache.get(clazz).getAttributes();
  155. }
  156. /**
  157. * Resolves the supported attributes and corresponding getters and setters
  158. * for the class using introspection. After resolving, the information is
  159. * cached internally by this class
  160. *
  161. * @param clazz
  162. * the class to resolve the supported attributes for
  163. */
  164. private static void resolveSupportedAttributes(Class<?> clazz) {
  165. if (clazz == null) {
  166. throw new IllegalArgumentException("The clazz can not be null");
  167. }
  168. if (cache.containsKey(clazz.getCanonicalName())) {
  169. // NO-OP
  170. return;
  171. }
  172. BeanInfo beanInfo;
  173. try {
  174. beanInfo = Introspector.getBeanInfo(clazz);
  175. } catch (IntrospectionException e) {
  176. throw new RuntimeException(
  177. "Could not get supported attributes for class "
  178. + clazz.getName());
  179. }
  180. AttributeCacheEntry entry = new AttributeCacheEntry();
  181. for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
  182. Method getter = descriptor.getReadMethod();
  183. Method setter = descriptor.getWriteMethod();
  184. if (getter != null && setter != null
  185. && isSupported(descriptor.getPropertyType())) {
  186. String attribute = toAttributeName(descriptor.getName());
  187. entry.addAttribute(attribute, getter, setter);
  188. }
  189. }
  190. cache.put(clazz, entry);
  191. }
  192. /**
  193. * Writes the specified attribute to the design if it differs from the
  194. * default value got from the <code> defaultInstance <code>
  195. *
  196. * @param component
  197. * the component used to get the attribute value
  198. * @param attribute
  199. * the key for the attribute
  200. * @param attr
  201. * the attribute list where the attribute will be written
  202. * @param defaultInstance
  203. * the default instance for comparing default values
  204. */
  205. public static void writeAttribute(DesignSynchronizable component,
  206. String attribute, Attributes attr,
  207. DesignSynchronizable defaultInstance) {
  208. Method getter = findGetterForAttribute(component.getClass(), attribute);
  209. if (getter == null) {
  210. getLogger().warning(
  211. "Could not find getter for attribute " + attribute);
  212. } else {
  213. try {
  214. // compare the value with default value
  215. Object value = getter.invoke(component);
  216. Object defaultValue = getter.invoke(defaultInstance);
  217. // if the values are not equal, write the data
  218. if (!SharedUtil.equals(value, defaultValue)) {
  219. String attributeValue = toAttributeValue(
  220. getter.getReturnType(), value);
  221. attr.put(attribute, attributeValue);
  222. }
  223. } catch (Exception e) {
  224. getLogger()
  225. .log(Level.SEVERE,
  226. "Failed to invoke getter for attribute "
  227. + attribute, e);
  228. }
  229. }
  230. }
  231. /**
  232. * Reads the given attribute from attributes. If the attribute is not found,
  233. * the provided default value is returned
  234. *
  235. * @param attribute
  236. * the attribute key
  237. * @param attributes
  238. * the set of attributes to read from
  239. * @param defaultValue
  240. * the default value that is returned if the attribute is not
  241. * found
  242. * @param outputType
  243. * the output type for the attribute
  244. * @return the attribute value or the default value if the attribute is not
  245. * found
  246. */
  247. @SuppressWarnings("unchecked")
  248. public static <T> T readAttribute(String attribute, Attributes attributes,
  249. T defaultValue, Class<T> outputType) {
  250. if (!isSupported(outputType)) {
  251. throw new IllegalArgumentException("output type: "
  252. + outputType.getName() + " not supported");
  253. }
  254. if (!attributes.hasKey(attribute)) {
  255. return defaultValue;
  256. } else {
  257. try {
  258. String value = attributes.get(attribute);
  259. return (T) fromAttributeValue(outputType, value);
  260. } catch (Exception e) {
  261. throw new DesignException("Failed to read attribute "
  262. + attribute, e);
  263. }
  264. }
  265. }
  266. /**
  267. * Writes the given attribute value to attributes if it differs from the
  268. * default attribute value
  269. *
  270. * @param attribute
  271. * the attribute key
  272. * @param attributes
  273. * the set of attributes where the new attribute is written
  274. * @param value
  275. * the attribute value
  276. * @param defaultValue
  277. * the default attribute value
  278. * @param the
  279. * type of the input value
  280. */
  281. public static <T> void writeAttribute(String attribute,
  282. Attributes attributes, T value, T defaultValue, Class<T> inputType) {
  283. if (!isSupported(inputType)) {
  284. throw new IllegalArgumentException("input type: "
  285. + inputType.getName() + " not supported");
  286. }
  287. if (!SharedUtil.equals(value, defaultValue)) {
  288. String attributeValue = toAttributeValue(inputType, value);
  289. attributes.put(attribute, attributeValue);
  290. }
  291. }
  292. /**
  293. * Formats the given design attribute value. The method is provided to
  294. * ensure consistent number formatting for design attribute values
  295. *
  296. * @param number
  297. * the number to be formatted
  298. * @return the formatted number
  299. */
  300. public static String formatFloat(float number) {
  301. return getDecimalFormat().format(number);
  302. }
  303. /**
  304. * Formats the given design attribute value. The method is provided to
  305. * ensure consistent number formatting for design attribute values
  306. *
  307. * @param number
  308. * the number to be formatted
  309. * @return the formatted number
  310. */
  311. public static String formatDouble(double number) {
  312. return getDecimalFormat().format(number);
  313. }
  314. /**
  315. * Convert ShortcutAction to attribute string presentation
  316. *
  317. * @param shortcut
  318. * the shortcut action
  319. * @return the action as attribute string presentation
  320. */
  321. private static String formatShortcutAction(ShortcutAction shortcut) {
  322. StringBuilder sb = new StringBuilder();
  323. // handle modifiers
  324. if (shortcut.getModifiers() != null) {
  325. for (int modifier : shortcut.getModifiers()) {
  326. sb.append(ShortcutKeyMapper.getStringForKeycode(modifier))
  327. .append("-");
  328. }
  329. }
  330. // handle keycode
  331. sb.append(ShortcutKeyMapper.getStringForKeycode(shortcut.getKeyCode()));
  332. return sb.toString();
  333. }
  334. /**
  335. * Reads shortcut action from attribute presentation
  336. *
  337. * @param attributeValue
  338. * attribute presentation of shortcut action
  339. * @return shortcut action with keycode and modifier keys from attribute
  340. * value
  341. */
  342. private static ShortcutAction readShortcutAction(String attributeValue) {
  343. if (attributeValue.length() == 0) {
  344. return null;
  345. }
  346. String[] parts = attributeValue.split("-");
  347. // handle keycode
  348. String keyCodePart = parts[parts.length - 1];
  349. int keyCode = ShortcutKeyMapper.getKeycodeForString(keyCodePart);
  350. if (keyCode < 0) {
  351. throw new IllegalArgumentException("Invalid shortcut definition "
  352. + attributeValue);
  353. }
  354. // handle modifiers
  355. int[] modifiers = null;
  356. if (parts.length > 1) {
  357. modifiers = new int[parts.length - 1];
  358. }
  359. for (int i = 0; i < parts.length - 1; i++) {
  360. int modifier = ShortcutKeyMapper.getKeycodeForString(parts[i]);
  361. if (modifier > 0) {
  362. modifiers[i] = modifier;
  363. } else {
  364. throw new IllegalArgumentException(
  365. "Invalid shortcut definition " + attributeValue);
  366. }
  367. }
  368. return new ShortcutAction(null, keyCode, modifiers);
  369. }
  370. /**
  371. * Creates the decimal format used when writing attributes to the design
  372. *
  373. * @since 7.4
  374. * @return the decimal format
  375. */
  376. private static DecimalFormat getDecimalFormat() {
  377. DecimalFormatSymbols symbols = new DecimalFormatSymbols(new Locale(
  378. "en_US"));
  379. DecimalFormat fmt = new DecimalFormat("0.###", symbols);
  380. fmt.setGroupingUsed(false);
  381. return fmt;
  382. }
  383. /**
  384. * Returns the design attribute name corresponding the given method name.
  385. * For example given a method name <code>setPrimaryStyleName</code> the
  386. * return value would be <code>primary-style-name</code>
  387. *
  388. * @param propertyName
  389. * the property name returned by {@link IntroSpector}
  390. * @return the design attribute name corresponding the given method name
  391. */
  392. private static String toAttributeName(String propertyName) {
  393. String[] words = propertyName.split("(?<!^)(?=[A-Z])");
  394. StringBuilder builder = new StringBuilder();
  395. for (int i = 0; i < words.length; i++) {
  396. if (builder.length() > 0) {
  397. builder.append("-");
  398. }
  399. builder.append(words[i].toLowerCase());
  400. }
  401. return builder.toString();
  402. }
  403. /**
  404. * Parses the given attribute value to specified target type
  405. *
  406. * @param targetType
  407. * the target type for the value
  408. * @param value
  409. * the parsed value
  410. * @return the object of specified target type
  411. */
  412. private static Object fromAttributeValue(Class<?> targetType, String value) {
  413. if (targetType == String.class) {
  414. return value;
  415. }
  416. // special handling for boolean type. The attribute evaluates to true if
  417. // it is present and the value is not "false" or "FALSE". Thus empty
  418. // value evaluates to true.
  419. if (targetType == Boolean.TYPE || targetType == Boolean.class) {
  420. return !value.equalsIgnoreCase("false");
  421. }
  422. if (targetType == Integer.TYPE || targetType == Integer.class) {
  423. return Integer.valueOf(value);
  424. }
  425. if (targetType == Byte.TYPE || targetType == Byte.class) {
  426. return Byte.valueOf(value);
  427. }
  428. if (targetType == Short.TYPE || targetType == Short.class) {
  429. return Short.valueOf(value);
  430. }
  431. if (targetType == Long.TYPE || targetType == Long.class) {
  432. return Long.valueOf(value);
  433. }
  434. if (targetType == Character.TYPE || targetType == Character.class) {
  435. return value.charAt(0);
  436. }
  437. if (targetType == Float.TYPE || targetType == Float.class) {
  438. return Float.valueOf(value);
  439. }
  440. if (targetType == Double.TYPE || targetType == Double.class) {
  441. return Double.valueOf(value);
  442. }
  443. if (targetType == Resource.class) {
  444. return parseResource(value);
  445. }
  446. if (Enum.class.isAssignableFrom(targetType)) {
  447. return Enum.valueOf((Class<? extends Enum>) targetType,
  448. value.toUpperCase());
  449. }
  450. if (targetType == ShortcutAction.class) {
  451. return readShortcutAction(value);
  452. }
  453. return null;
  454. }
  455. /**
  456. * Serializes the given value to valid design attribute representation
  457. *
  458. * @param sourceType
  459. * the type of the value
  460. * @param value
  461. * the value to be serialized
  462. * @return the given value as design attribute representation
  463. */
  464. private static String toAttributeValue(Class<?> sourceType, Object value) {
  465. if (value == null) {
  466. // TODO: Handle corner case where sourceType is String and default
  467. // value is not null. How to represent null value in attributes?
  468. return "";
  469. }
  470. if (sourceType == Resource.class) {
  471. if (value instanceof ExternalResource) {
  472. return ((ExternalResource) value).getURL();
  473. } else if (value instanceof ThemeResource) {
  474. return "theme://" + ((ThemeResource) value).getResourceId();
  475. } else if (value instanceof FontAwesome) {
  476. return "font://" + ((FontAwesome) value).name();
  477. } else if (value instanceof FileResource) {
  478. String path = ((FileResource) value).getSourceFile().getPath();
  479. if (File.separatorChar != '/') {
  480. // make sure we use '/' as file separator in templates
  481. return path.replace(File.separatorChar, '/');
  482. } else {
  483. return path;
  484. }
  485. } else {
  486. getLogger().warning(
  487. "Unknown resource type " + value.getClass().getName());
  488. return null;
  489. }
  490. } else if (sourceType == Float.class || sourceType == Float.TYPE) {
  491. return formatFloat(((Float) value).floatValue());
  492. } else if (sourceType == Double.class || sourceType == Double.TYPE) {
  493. return formatDouble(((Double) value).doubleValue());
  494. } else if (sourceType == ShortcutAction.class) {
  495. return formatShortcutAction((ShortcutAction) value);
  496. } else {
  497. return value.toString();
  498. }
  499. }
  500. /**
  501. * Parses the given attribute value as resource
  502. *
  503. * @param value
  504. * the attribute value to be parsed
  505. * @return resource instance based on the attribute value
  506. */
  507. private static Resource parseResource(String value) {
  508. if (value.startsWith("http://")) {
  509. return new ExternalResource(value);
  510. } else if (value.startsWith("theme://")) {
  511. return new ThemeResource(value.substring(8));
  512. } else if (value.startsWith("font://")) {
  513. return FontAwesome.valueOf(value.substring(7));
  514. } else {
  515. return new FileResource(new File(value));
  516. }
  517. }
  518. /**
  519. * Returns a setter that can be used for assigning the given design
  520. * attribute to the class
  521. *
  522. * @param clazz
  523. * the class that is scanned for setters
  524. * @param attribute
  525. * the design attribute to find setter for
  526. * @return the setter method or null if not found
  527. */
  528. private static Method findSetterForAttribute(Class<?> clazz,
  529. String attribute) {
  530. resolveSupportedAttributes(clazz);
  531. return cache.get(clazz).getSetter(attribute);
  532. }
  533. /**
  534. * Returns a getter that can be used for reading the given design attribute
  535. * value from the class
  536. *
  537. * @param clazz
  538. * the class that is scanned for getters
  539. * @param attribute
  540. * the design attribute to find getter for
  541. * @return the getter method or null if not found
  542. */
  543. private static Method findGetterForAttribute(Class<?> clazz,
  544. String attribute) {
  545. resolveSupportedAttributes(clazz);
  546. return cache.get(clazz).getGetter(attribute);
  547. }
  548. // supported property types
  549. private static final List<Class<?>> supportedClasses = Arrays
  550. .asList(new Class<?>[] { String.class, Boolean.class,
  551. Integer.class, Byte.class, Short.class, Long.class,
  552. Character.class, Float.class, Double.class, Resource.class,
  553. ShortcutAction.class });
  554. /**
  555. * Returns true if the specified value type is supported by this class.
  556. * Currently the handler supports primitives, {@link Locale.class} and
  557. * {@link Resource.class}.
  558. *
  559. * @param valueType
  560. * the value type to be tested
  561. * @return true if the value type is supported, otherwise false
  562. */
  563. private static boolean isSupported(Class<?> valueType) {
  564. return valueType != null
  565. && (valueType.isPrimitive()
  566. || supportedClasses.contains(valueType) || Enum.class
  567. .isAssignableFrom(valueType));
  568. }
  569. /**
  570. * Cache object for caching supported attributes and their getters and
  571. * setters
  572. *
  573. * @author Vaadin Ltd
  574. */
  575. private static class AttributeCacheEntry implements Serializable {
  576. private Map<String, Method[]> accessMethods = Collections
  577. .synchronizedMap(new HashMap<String, Method[]>());
  578. private void addAttribute(String attribute, Method getter, Method setter) {
  579. Method[] methods = new Method[2];
  580. methods[0] = getter;
  581. methods[1] = setter;
  582. accessMethods.put(attribute, methods);
  583. }
  584. private Collection<String> getAttributes() {
  585. ArrayList<String> attributes = new ArrayList<String>();
  586. attributes.addAll(accessMethods.keySet());
  587. return attributes;
  588. }
  589. private Method getGetter(String attribute) {
  590. Method[] methods = accessMethods.get(attribute);
  591. return (methods != null && methods.length > 0) ? methods[0] : null;
  592. }
  593. private Method getSetter(String attribute) {
  594. Method[] methods = accessMethods.get(attribute);
  595. return (methods != null && methods.length > 1) ? methods[1] : null;
  596. }
  597. }
  598. /**
  599. * Provides mappings between shortcut keycodes and their representation in
  600. * design attributes
  601. *
  602. * @since 7.4
  603. * @author Vaadin Ltd
  604. */
  605. private static class ShortcutKeyMapper implements Serializable {
  606. private static Map<Integer, String> keyCodeMap = Collections
  607. .synchronizedMap(new HashMap<Integer, String>());
  608. private static Map<String, Integer> presentationMap = Collections
  609. .synchronizedMap(new HashMap<String, Integer>());
  610. static {
  611. // map modifiers
  612. mapKey(ModifierKey.ALT, "alt");
  613. mapKey(ModifierKey.CTRL, "ctrl");
  614. mapKey(ModifierKey.META, "meta");
  615. mapKey(ModifierKey.SHIFT, "shift");
  616. // map keys
  617. mapKey(KeyCode.ENTER, "enter");
  618. mapKey(KeyCode.ESCAPE, "escape");
  619. mapKey(KeyCode.PAGE_UP, "pageup");
  620. mapKey(KeyCode.PAGE_DOWN, "pagedown");
  621. mapKey(KeyCode.TAB, "tab");
  622. mapKey(KeyCode.ARROW_LEFT, "left");
  623. mapKey(KeyCode.ARROW_UP, "up");
  624. mapKey(KeyCode.ARROW_RIGHT, "right");
  625. mapKey(KeyCode.ARROW_DOWN, "down");
  626. mapKey(KeyCode.BACKSPACE, "backspace");
  627. mapKey(KeyCode.DELETE, "delete");
  628. mapKey(KeyCode.INSERT, "insert");
  629. mapKey(KeyCode.END, "end");
  630. mapKey(KeyCode.HOME, "home");
  631. mapKey(KeyCode.F1, "f1");
  632. mapKey(KeyCode.F2, "f2");
  633. mapKey(KeyCode.F3, "f3");
  634. mapKey(KeyCode.F4, "f4");
  635. mapKey(KeyCode.F5, "f5");
  636. mapKey(KeyCode.F6, "f6");
  637. mapKey(KeyCode.F7, "f7");
  638. mapKey(KeyCode.F8, "f8");
  639. mapKey(KeyCode.F9, "f9");
  640. mapKey(KeyCode.F10, "f10");
  641. mapKey(KeyCode.F11, "f11");
  642. mapKey(KeyCode.F12, "f12");
  643. mapKey(KeyCode.NUM0, "0");
  644. mapKey(KeyCode.NUM1, "1");
  645. mapKey(KeyCode.NUM2, "2");
  646. mapKey(KeyCode.NUM3, "3");
  647. mapKey(KeyCode.NUM4, "4");
  648. mapKey(KeyCode.NUM5, "5");
  649. mapKey(KeyCode.NUM6, "6");
  650. mapKey(KeyCode.NUM7, "7");
  651. mapKey(KeyCode.NUM8, "8");
  652. mapKey(KeyCode.NUM9, "9");
  653. mapKey(KeyCode.SPACEBAR, "spacebar");
  654. mapKey(KeyCode.A, "a");
  655. mapKey(KeyCode.B, "b");
  656. mapKey(KeyCode.C, "c");
  657. mapKey(KeyCode.D, "d");
  658. mapKey(KeyCode.E, "e");
  659. mapKey(KeyCode.F, "f");
  660. mapKey(KeyCode.G, "g");
  661. mapKey(KeyCode.H, "h");
  662. mapKey(KeyCode.I, "i");
  663. mapKey(KeyCode.J, "j");
  664. mapKey(KeyCode.K, "k");
  665. mapKey(KeyCode.L, "l");
  666. mapKey(KeyCode.M, "m");
  667. mapKey(KeyCode.N, "n");
  668. mapKey(KeyCode.O, "o");
  669. mapKey(KeyCode.P, "p");
  670. mapKey(KeyCode.Q, "q");
  671. mapKey(KeyCode.R, "r");
  672. mapKey(KeyCode.S, "s");
  673. mapKey(KeyCode.T, "t");
  674. mapKey(KeyCode.U, "u");
  675. mapKey(KeyCode.V, "v");
  676. mapKey(KeyCode.X, "x");
  677. mapKey(KeyCode.Y, "y");
  678. mapKey(KeyCode.Z, "z");
  679. }
  680. private static void mapKey(int keyCode, String presentation) {
  681. keyCodeMap.put(keyCode, presentation);
  682. presentationMap.put(presentation, keyCode);
  683. }
  684. private static int getKeycodeForString(String attributePresentation) {
  685. Integer code = presentationMap.get(attributePresentation);
  686. return code != null ? code.intValue() : -1;
  687. }
  688. private static String getStringForKeycode(int keyCode) {
  689. return keyCodeMap.get(keyCode);
  690. }
  691. }
  692. }