summaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin
diff options
context:
space:
mode:
authorMika Murtojarvi <mika@vaadin.com>2014-12-03 17:53:02 +0200
committerArtur Signell <artur@vaadin.com>2014-12-04 14:47:21 +0000
commitbe35a9b3f1250d9139baf5eeb3f48cb4a20c2822 (patch)
treed42906cf8bfc825fd060a4fc55ef2e61b5dfffc2 /server/src/com/vaadin
parentccbed3fcfa5870c376014036bde8455aaa952384 (diff)
downloadvaadin-framework-be35a9b3f1250d9139baf5eeb3f48cb4a20c2822.tar.gz
vaadin-framework-be35a9b3f1250d9139baf5eeb3f48cb4a20c2822.zip
Fixes for the public API of DesignContext.
Also adds tests for parsing and for finding components by id. Change-Id: I3202a19f1699ee906f97cc57b08a9b2fd540f51b
Diffstat (limited to 'server/src/com/vaadin')
-rw-r--r--server/src/com/vaadin/ui/AbstractOrderedLayout.java1
-rw-r--r--server/src/com/vaadin/ui/declarative/ComponentInstantiationException.java18
-rw-r--r--server/src/com/vaadin/ui/declarative/DesignContext.java143
-rw-r--r--server/src/com/vaadin/ui/declarative/DesignException.java18
-rw-r--r--server/src/com/vaadin/ui/declarative/LayoutHandler.java78
-rw-r--r--server/src/com/vaadin/ui/declarative/LayoutInflaterException.java18
6 files changed, 128 insertions, 148 deletions
diff --git a/server/src/com/vaadin/ui/AbstractOrderedLayout.java b/server/src/com/vaadin/ui/AbstractOrderedLayout.java
index 139a4eb545..87b2ff6f48 100644
--- a/server/src/com/vaadin/ui/AbstractOrderedLayout.java
+++ b/server/src/com/vaadin/ui/AbstractOrderedLayout.java
@@ -541,7 +541,6 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements
DesignSynchronizable childComponent = (DesignSynchronizable) child;
Element childNode = designContext.createNode(childComponent);
designElement.appendChild(childNode);
- childComponent.synchronizeToDesign(childNode, designContext);
// handle alignment
Alignment alignment = getComponentAlignment(child);
if (alignment.isMiddle()) {
diff --git a/server/src/com/vaadin/ui/declarative/ComponentInstantiationException.java b/server/src/com/vaadin/ui/declarative/ComponentInstantiationException.java
deleted file mode 100644
index 5a8ec05d87..0000000000
--- a/server/src/com/vaadin/ui/declarative/ComponentInstantiationException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.vaadin.ui.declarative;
-
-@SuppressWarnings("serial")
-public class ComponentInstantiationException extends RuntimeException {
-
- public ComponentInstantiationException() {
- super();
- }
-
- public ComponentInstantiationException(String message) {
- super(message);
- }
-
- public ComponentInstantiationException(String message, Throwable e) {
- super(message, e);
- }
-
-}
diff --git a/server/src/com/vaadin/ui/declarative/DesignContext.java b/server/src/com/vaadin/ui/declarative/DesignContext.java
index 961affd5b0..a846edff2c 100644
--- a/server/src/com/vaadin/ui/declarative/DesignContext.java
+++ b/server/src/com/vaadin/ui/declarative/DesignContext.java
@@ -31,7 +31,9 @@ import com.vaadin.ui.DesignSynchronizable;
/**
* This class contains contextual information that is collected when a component
- * tree is constructed based on HTML design template.
+ * tree is constructed based on HTML design template. This information includes
+ * mappings from local ids, global ids and captions to components , as well as a
+ * mapping between prefixes and package names (such as "v" -> "com.vaadin.ui").
*
* @since 7.4
* @author Vaadin Ltd
@@ -48,12 +50,12 @@ public class DesignContext {
// id
public static final String ID_ATTRIBUTE = "id";
public static final String CAPTION_ATTRIBUTE = "caption";
- public static final String LOCAL_ID_PREFIX = "_";
- // Mappings from IDs to components. Modified when synchronizing from design.
- private Map<String, DesignSynchronizable> globalIdToComponent = new HashMap<String, DesignSynchronizable>();
+ public static final String LOCAL_ID_ATTRIBUTE = "_id";
+ // Mappings from ids to components. Modified when synchronizing from design.
+ private Map<String, DesignSynchronizable> idToComponent = new HashMap<String, DesignSynchronizable>();
private Map<String, DesignSynchronizable> localIdToComponent = new HashMap<String, DesignSynchronizable>();
private Map<String, DesignSynchronizable> captionToComponent = new HashMap<String, DesignSynchronizable>();
- // Mapping from components to local IDs. Accessed when synchronizing to
+ // Mapping from components to local ids. Accessed when synchronizing to
// design. Modified when synchronizing from design.
private Map<DesignSynchronizable, String> componentToLocalId = new HashMap<DesignSynchronizable, String>();
private Document doc; // required for calling createElement(String)
@@ -80,36 +82,73 @@ public class DesignContext {
}
/**
+ * Returns a component having the specified local id. If no component is
+ * found, returns null.
+ *
+ * @param localId
+ * The local id of the component
+ * @return a component whose local id equals localId
+ */
+ public DesignSynchronizable getComponentByLocalId(String localId) {
+ return localIdToComponent.get(localId);
+ }
+
+ /**
+ * Returns a component having the specified global id. If no component is
+ * found, returns null.
+ *
+ * @param globalId
+ * The global id of the component
+ * @return a component whose global id equals globalId
+ */
+ public DesignSynchronizable getComponentById(String globalId) {
+ return idToComponent.get(globalId);
+ }
+
+ /**
+ * Returns a component having the specified caption. If no component is
+ * found, returns null.
+ *
+ * @param caption
+ * The caption of the component
+ * @return a component whose caption equals the caption given as a parameter
+ */
+ public DesignSynchronizable getComponentByCaption(String caption) {
+ return captionToComponent.get(caption);
+ }
+
+ /**
* Creates a mapping between the given global id and the component. Returns
- * true if globalId was already mapped to some component or if component was
- * mapped to some string. Otherwise returns false. Also sets the id of the
- * component to globalId.
+ * true if globalId was already mapped to some component. Otherwise returns
+ * false. Also sets the id of the component to globalId.
+ *
+ * If there is a mapping from the component to a global id (gid) different
+ * from globalId, the mapping from gid to component is removed.
*
* If the string was mapped to a component c different from the given
* component, the mapping from c to the string is removed. Similarly, if
* component was mapped to some string s different from globalId, the
* mapping from s to component is removed.
*
- * @since
* @param globalId
* The new global id of the component.
* @param component
* The component whose global id is to be set.
* @return true, if there already was a global id mapping from the string to
- * some component or from the component to some string. Otherwise
- * returns false.
+ * some component.
*/
- public boolean mapGlobalId(String globalId, DesignSynchronizable component) {
- DesignSynchronizable oldComponent = globalIdToComponent.get(globalId);
+ private boolean mapId(String globalId, DesignSynchronizable component) {
+ DesignSynchronizable oldComponent = idToComponent.get(globalId);
if (oldComponent != null && !oldComponent.equals(component)) {
oldComponent.setId(null);
}
String oldGID = component.getId();
if (oldGID != null && !oldGID.equals(globalId)) {
- globalIdToComponent.remove(oldGID);
+ idToComponent.remove(oldGID);
}
component.setId(globalId);
- return oldComponent != null || oldGID != null;
+ idToComponent.put(globalId, component);
+ return oldComponent != null && !oldComponent.equals(component);
}
/**
@@ -122,8 +161,7 @@ public class DesignContext {
* component was mapped to some string s different from localId, the mapping
* from s to component is removed.
*
- * @since
- * @param globalId
+ * @param localId
* The new local id of the component.
* @param component
* The component whose local id is to be set.
@@ -131,7 +169,7 @@ public class DesignContext {
* some component or from the component to some string. Otherwise
* returns false.
*/
- public boolean mapLocalId(String localId, DesignSynchronizable component) {
+ private boolean mapLocalId(String localId, DesignSynchronizable component) {
return twoWayMap(localId, component, localIdToComponent,
componentToLocalId);
}
@@ -146,7 +184,6 @@ public class DesignContext {
* a given caption can be found using the map captionToComponent. Hence, any
* captions that are used to identify an object should be unique.
*
- * @since
* @param caption
* The new caption of the component.
* @param component
@@ -154,7 +191,7 @@ public class DesignContext {
* @return true, if there already was a caption mapping from the string to
* some component.
*/
- public boolean mapCaption(String caption, DesignSynchronizable component) {
+ private boolean mapCaption(String caption, DesignSynchronizable component) {
return captionToComponent.put(caption, component) != null;
}
@@ -168,7 +205,6 @@ public class DesignContext {
* Returns true if there already was a mapping from key to some value v or
* if there was a mapping from value to some key k. Otherwise returns false.
*
- * @since
* @param key
* The new key in keyToValue.
* @param value
@@ -198,7 +234,6 @@ public class DesignContext {
* true if prefix was already mapped to some package name or packageName to
* some prefix.
*
- * @since
* @param prefix
* the prefix name without an ending dash (for instance, "v" is
* always used for "com.vaadin.ui")
@@ -207,7 +242,7 @@ public class DesignContext {
* @return whether there was a mapping from prefix to some package name or
* from packageName to some prefix.
*/
- public boolean mapPrefixToPackage(String prefix, String packageName) {
+ private boolean mapPrefixToPackage(String prefix, String packageName) {
return twoWayMap(prefix, packageName, prefixToPackage, packageToPrefix);
}
@@ -240,7 +275,6 @@ public class DesignContext {
* Get and store the mappings from prefixes to package names from meta tags
* located under <head> in the html document.
*
- * @since
*/
public void getPrefixes(Document doc) {
Element head = doc.head();
@@ -258,7 +292,7 @@ public class DesignContext {
String contentString = attributes.get("content");
String[] parts = contentString.split(":");
if (parts.length != 2) {
- throw new LayoutInflaterException("The meta tag '"
+ throw new DesignException("The meta tag '"
+ child.toString() + "' cannot be parsed.");
}
String prefixName = parts[0];
@@ -297,7 +331,6 @@ public class DesignContext {
* the synchronizeToDesign() call, this method creates the entire subtree
* rooted at the returned Node.
*
- * @since
* @param childComponent
* A component implementing the DesignSynchronizable interface.
* @return An html tree node corresponding to the given component. The tag
@@ -321,8 +354,7 @@ public class DesignContext {
// care of by synchronizeToDesign.
String localId = componentToLocalId.get(childComponent);
if (localId != null) {
- localId = LOCAL_ID_PREFIX + localId;
- newElement.attr(localId, "");
+ newElement.attr(LOCAL_ID_ATTRIBUTE, localId);
}
return newElement;
}
@@ -333,7 +365,6 @@ public class DesignContext {
* inserting a dash before the letter. No dash is inserted before the first
* letter of the class name.
*
- * @since
* @param className
* the name of the class without a package name
* @return the html tag name corresponding to className
@@ -359,7 +390,6 @@ public class DesignContext {
* node. Also calls synchronizeFromDesign() for the created node, in effect
* creating the entire component hierarchy rooted at the returned component.
*
- * @since
* @param componentDesign
* The html tree node containing the description of the component
* to be created.
@@ -370,16 +400,16 @@ public class DesignContext {
// Create the component.
DesignSynchronizable component = instantiateComponent(componentDesign);
component.synchronizeFromDesign(componentDesign, this);
- // Get the IDs and the caption of the component and store them in the
+ // Get the ids and the caption of the component and store them in the
// maps of this design context.
org.jsoup.nodes.Attributes attributes = componentDesign.attributes();
// global id: only update the mapping, the id has already been set for
// the component
- String id = component.getCaption();
+ String id = component.getId();
if (id != null && id.length() > 0) {
- boolean mappingExists = mapGlobalId(id, component);
+ boolean mappingExists = mapId(id, component);
if (mappingExists) {
- throw new LayoutInflaterException(
+ throw new DesignException(
"The following global id is not unique: " + id);
}
}
@@ -387,17 +417,12 @@ public class DesignContext {
// from the attributes of componentDesign
String localId = null;
for (Attribute attribute : attributes.asList()) {
- if (attribute.getKey().startsWith(LOCAL_ID_PREFIX)) {
+ if (attribute.getKey().equals(LOCAL_ID_ATTRIBUTE)) {
if (localId != null) {
- throw new LayoutInflaterException(
- "Duplicate local ids specified: "
- + localId
- + " and "
- + attribute.getKey().substring(
- LOCAL_ID_PREFIX.length()));
+ throw new DesignException("Duplicate local ids specified: "
+ + localId + ".");
}
- localId = attribute.getKey()
- .substring(LOCAL_ID_PREFIX.length());
+ localId = attribute.getValue();
mapLocalId(localId, component); // two-way map
}
}
@@ -413,7 +438,6 @@ public class DesignContext {
* Creates a DesignSynchronizable component corresponding to the given node.
* Does not set the attributes for the created object.
*
- * @since
* @param node
* a node of an html tree
* @return a DesignSynchronizable object corresponding to node, with no
@@ -427,16 +451,15 @@ public class DesignContext {
DesignSynchronizable newComponent = componentClass.newInstance();
return newComponent;
} catch (Exception e) {
- throw createException(e, qualifiedClassName);
+ throw new DesignException("No component class could be found for "
+ + node.nodeName() + ".", e);
}
}
/**
* Returns the qualified class name corresponding to the given html tree
- * node. If the node is not a span or a div, the class name is extracted
- * from the tag name of node.
+ * node. The class name is extracted from the tag name of node.
*
- * @since
* @param node
* an html tree node
* @return The qualified class name corresponding to the given node.
@@ -445,22 +468,19 @@ public class DesignContext {
String tagName = node.nodeName();
if (tagName.equals("v-addon")) {
return node.attr("class");
- } else if (tagName.toLowerCase(Locale.ENGLISH).equals("span")
- || tagName.toLowerCase(Locale.ENGLISH).equals("div")) {
- return "com.vaadin.ui.Label";
}
// Otherwise, get the full class name using the prefix to package
// mapping. Example: "v-vertical-layout" ->
// "com.vaadin.ui.VerticalLayout"
String[] parts = tagName.split("-");
if (parts.length < 2) {
- throw new LayoutInflaterException("The tagname '" + tagName
+ throw new DesignException("The tagname '" + tagName
+ "' is invalid: missing prefix.");
}
String prefixName = parts[0];
String packageName = prefixToPackage.get(prefixName);
if (packageName == null) {
- throw new LayoutInflaterException("Unknown tag: " + tagName);
+ throw new DesignException("Unknown tag: " + tagName);
}
int firstCharacterIndex = prefixName.length() + 1; // +1 is for '-'
tagName = tagName.substring(firstCharacterIndex,
@@ -498,20 +518,6 @@ public class DesignContext {
}
}
- /*
- * Create a new ComponentInstantiationException.
- */
- private ComponentInstantiationException createException(Exception e,
- String qualifiedClassName) {
- String message = String.format(
- "Couldn't instantiate a component for %s.", qualifiedClassName);
- if (e != null) {
- return new ComponentInstantiationException(message, e);
- } else {
- return new ComponentInstantiationException(message);
- }
- }
-
/**
* Returns {@code true} if the given {@link Class} implements the
* {@link Component} interface of Vaadin Framework otherwise {@code false}.
@@ -532,7 +538,6 @@ public class DesignContext {
/**
* Returns the root component of a created component hierarchy.
*
- * @since
* @return
*/
public DesignSynchronizable getComponentRoot() {
@@ -545,4 +550,4 @@ public class DesignContext {
public void setComponentRoot(DesignSynchronizable componentRoot) {
this.componentRoot = componentRoot;
}
-} \ No newline at end of file
+}
diff --git a/server/src/com/vaadin/ui/declarative/DesignException.java b/server/src/com/vaadin/ui/declarative/DesignException.java
new file mode 100644
index 0000000000..74bcd4cdc6
--- /dev/null
+++ b/server/src/com/vaadin/ui/declarative/DesignException.java
@@ -0,0 +1,18 @@
+package com.vaadin.ui.declarative;
+
+@SuppressWarnings("serial")
+public class DesignException extends RuntimeException {
+
+ public DesignException() {
+ super();
+ }
+
+ public DesignException(String message) {
+ super(message);
+ }
+
+ public DesignException(String message, Throwable e) {
+ super(message, e);
+ }
+
+}
diff --git a/server/src/com/vaadin/ui/declarative/LayoutHandler.java b/server/src/com/vaadin/ui/declarative/LayoutHandler.java
index 045fb43b2c..84d454bb01 100644
--- a/server/src/com/vaadin/ui/declarative/LayoutHandler.java
+++ b/server/src/com/vaadin/ui/declarative/LayoutHandler.java
@@ -16,11 +16,8 @@
package com.vaadin.ui.declarative;
import java.io.BufferedWriter;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.OutputStreamWriter;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
@@ -38,18 +35,17 @@ import com.vaadin.ui.DesignSynchronizable;
* component hierarchy must contain a single root.
*
*
- * @since
+ * @since 7.4
* @author Vaadin Ltd
*/
public class LayoutHandler {
/**
* Constructs a component hierarchy from the design specified as an html
- * document. The hierarchy must contain exactly one top-level Component. The
- * component should be located under <body>, but also invalid html
- * containing the hierarchy without <html>, <head> and <body> tags is
+ * document. The component hierarchy must contain exactly one top-level
+ * Component. The component should be located under <body>, but also invalid
+ * html containing the hierarchy without <html>, <head> and <body> tags is
* accepted.
*
- * @since
* @param html
* the html document describing the component design
* @return the DesignContext created while traversing the tree. The
@@ -63,9 +59,37 @@ public class LayoutHandler {
try {
doc = Jsoup.parse(html, "UTF-8", "", Parser.htmlParser());
} catch (IOException e) {
- throw new LayoutInflaterException(
- "The html document cannot be parsed.");
+ throw new DesignException("The html document cannot be parsed.");
}
+ return parse(doc);
+ }
+
+ /**
+ * Constructs a component hierarchy from the design specified as an html
+ * document given as a string. The component hierarchy must contain exactly
+ * one top-level Component. The component should be located under <body>,
+ * but also invalid html containing the hierarchy without <html>, <head> and
+ * <body> tags is accepted.
+ *
+ * @param html
+ * the html document describing the component design
+ * @return the DesignContext created while traversing the tree. The
+ * top-level component of the created component hierarchy can be
+ * accessed using result.getRootComponent(), where result is the
+ * object returned by this method.
+ * @throws IOException
+ */
+ public static DesignContext parse(String html) {
+ Document doc = Jsoup.parse(html);
+ return parse(doc);
+ }
+
+ /**
+ * Constructs a component hierarchy from the design specified as an html
+ * tree.
+ *
+ */
+ private static DesignContext parse(Document doc) {
DesignContext designContext = new DesignContext(doc);
designContext.getPrefixes(doc);
// No special handling for a document without a body element - should be
@@ -75,7 +99,7 @@ public class LayoutHandler {
for (Node element : root.childNodes()) {
if (element instanceof Element) {
if (componentRoot != null) {
- throw new LayoutInflaterException(
+ throw new DesignException(
"The first level of a component hierarchy should contain a single root component, but found "
+ "two: "
+ componentRoot
@@ -96,7 +120,6 @@ public class LayoutHandler {
* the tree. The generated tree corresponds to a valid html document.
*
*
- * @since
* @param root
* the root of the component hierarchy
* @return an html tree representation of the component hierarchy
@@ -126,7 +149,6 @@ public class LayoutHandler {
* Generates an html file corresponding to the component hierarchy with the
* given root.
*
- * @since
* @param writer
* @param root
* @throws IOException
@@ -136,32 +158,4 @@ public class LayoutHandler {
String docAsString = createHtml(ctx).toString();
writer.write(docAsString);
}
-
- /**
- * Used for testing only.
- *
- * This method reads and constructs a component hierarchy from a file whose
- * name is given as the first parameter. The constructed hierarchy is then
- * transformed back to html form, the resulting html being written to a file
- * whose name is given as the second parameter.
- *
- * This is useful for checking that: 1) a design can be successfully parsed
- * 2) a component hierarchy can be transformed to html form and 3) No
- * relevant information is lost in these conversions.
- *
- */
- public static void main(String[] args) throws IOException {
- String inputFileName = args[0];
- String outputFileName = args[1];
- // Read and parse the contents of the output file.
- FileInputStream fis = new FileInputStream(inputFileName);
- DesignContext ctx = parse(fis);
- DesignSynchronizable root = ctx.getComponentRoot();
- // Write
- BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
- new FileOutputStream(outputFileName)));
- createHtml(writer, ctx);
- fis.close();
- writer.close();
- }
-} \ No newline at end of file
+}
diff --git a/server/src/com/vaadin/ui/declarative/LayoutInflaterException.java b/server/src/com/vaadin/ui/declarative/LayoutInflaterException.java
deleted file mode 100644
index 3482856645..0000000000
--- a/server/src/com/vaadin/ui/declarative/LayoutInflaterException.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.vaadin.ui.declarative;
-
-@SuppressWarnings("serial")
-public class LayoutInflaterException extends RuntimeException {
-
- public LayoutInflaterException(String message) {
- super(message);
- }
-
- public LayoutInflaterException(String message, Throwable e) {
- super(message, e);
- }
-
- public LayoutInflaterException(Throwable e) {
- super(e);
- }
-
-}