Change-Id: I8eb917186886aa1a9c63939d2dfd1f59df973aa2tags/7.4.0.beta1
@@ -1206,7 +1206,7 @@ public abstract class AbstractComponent extends AbstractClientConnector | |||
private static final String[] customAttributes = new String[] { "width", | |||
"height", "debug-id", "error", "width-auto", "height-auto", | |||
"width-full", "height-full", "size-auto", "size-full", | |||
"responsive", "immediate", "locale" }; | |||
"responsive", "immediate", "locale", "read-only" }; | |||
/* | |||
* (non-Javadoc) |
@@ -25,6 +25,10 @@ import java.util.Iterator; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Locale; | |||
import java.util.logging.Logger; | |||
import org.jsoup.nodes.Attributes; | |||
import org.jsoup.nodes.Element; | |||
import com.vaadin.data.Buffered; | |||
import com.vaadin.data.Property; | |||
@@ -43,6 +47,8 @@ import com.vaadin.server.CompositeErrorMessage; | |||
import com.vaadin.server.ErrorMessage; | |||
import com.vaadin.shared.AbstractFieldState; | |||
import com.vaadin.shared.util.SharedUtil; | |||
import com.vaadin.ui.declarative.DesignAttributeHandler; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
/** | |||
* <p> | |||
@@ -1751,4 +1757,63 @@ public abstract class AbstractField<T> extends AbstractComponent implements | |||
isListeningToPropertyEvents = false; | |||
} | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see | |||
* com.vaadin.ui.AbstractComponent#synchronizeFromDesign(org.jsoup.nodes | |||
* .Element, com.vaadin.ui.declarative.DesignContext) | |||
*/ | |||
@Override | |||
public void synchronizeFromDesign(Element design, | |||
DesignContext designContext) { | |||
super.synchronizeFromDesign(design, designContext); | |||
AbstractField def = designContext.getDefaultInstance(this.getClass()); | |||
Attributes attr = design.attributes(); | |||
boolean readOnly = DesignAttributeHandler.readAttribute("readonly", | |||
attr, def.isReadOnly(), Boolean.class); | |||
setReadOnly(readOnly); | |||
// tabindex | |||
int tabIndex = DesignAttributeHandler.readAttribute("tabindex", attr, | |||
def.getTabIndex(), Integer.class); | |||
setTabIndex(tabIndex); | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.vaadin.ui.AbstractComponent#getCustomAttributes() | |||
*/ | |||
@Override | |||
protected Collection<String> getCustomAttributes() { | |||
Collection<String> attributes = super.getCustomAttributes(); | |||
attributes.add("readonly"); | |||
attributes.add("tabindex"); | |||
return attributes; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see | |||
* com.vaadin.ui.AbstractComponent#synchronizeToDesign(org.jsoup.nodes.Element | |||
* , com.vaadin.ui.declarative.DesignContext) | |||
*/ | |||
@Override | |||
public void synchronizeToDesign(Element design, DesignContext designContext) { | |||
super.synchronizeToDesign(design, designContext); | |||
AbstractField def = designContext.getDefaultInstance(this.getClass()); | |||
Attributes attr = design.attributes(); | |||
// handle readonly | |||
DesignAttributeHandler.writeAttribute("readonly", attr, | |||
super.isReadOnly(), def.isReadOnly(), Boolean.class); | |||
// handle tab index | |||
DesignAttributeHandler.writeAttribute("tabindex", attr, getTabIndex(), | |||
def.getTabIndex(), Integer.class); | |||
} | |||
private static final Logger getLogger() { | |||
return Logger.getLogger(AbstractField.class.getName()); | |||
} | |||
} |
@@ -99,6 +99,7 @@ public class DesignAttributeHandler implements Serializable { | |||
* @param defaultInstance | |||
* the default instance of the class for fetching the default | |||
* values | |||
* @return true on success | |||
*/ | |||
public static boolean readAttribute(DesignSynchronizable component, | |||
String attribute, Attributes attributes, | |||
@@ -236,6 +237,69 @@ public class DesignAttributeHandler implements Serializable { | |||
} | |||
} | |||
/** | |||
* Reads the given attribute from attributes. If the attribute is not found, | |||
* the provided default value is returned | |||
* | |||
* @param attribute | |||
* the attribute key | |||
* @param attributes | |||
* the set of attributes to read from | |||
* @param defaultValue | |||
* the default value that is returned if the attribute is not | |||
* found | |||
* @param outputType | |||
* the output type for the attribute | |||
* @return the attribute value or the default value if the attribute is not | |||
* found | |||
*/ | |||
@SuppressWarnings("unchecked") | |||
public static <T> T readAttribute(String attribute, Attributes attributes, | |||
T defaultValue, Class<T> outputType) { | |||
if (!isSupported(outputType)) { | |||
throw new IllegalArgumentException("output type: " | |||
+ outputType.getName() + " not supported"); | |||
} | |||
if (!attributes.hasKey(attribute)) { | |||
return defaultValue; | |||
} else { | |||
try { | |||
String value = attributes.get(attribute); | |||
return (T) fromAttributeValue(outputType, value); | |||
} catch (Exception e) { | |||
throw new DesignException("Failed to read attribute " | |||
+ attribute, e); | |||
} | |||
} | |||
} | |||
/** | |||
* Writes the given attribute value to attributes if it differs from the | |||
* default attribute value | |||
* | |||
* @param attribute | |||
* the attribute key | |||
* @param attributes | |||
* the set of attributes where the new attribute is written | |||
* @param value | |||
* the attribute value | |||
* @param defaultValue | |||
* the default attribute value | |||
* @param the | |||
* type of the input value | |||
*/ | |||
public static <T> void writeAttribute(String attribute, | |||
Attributes attributes, T value, T defaultValue, Class<T> inputType) { | |||
if (!isSupported(inputType)) { | |||
throw new IllegalArgumentException("input type: " | |||
+ inputType.getName() + " not supported"); | |||
} | |||
if (!SharedUtil.equals(value, defaultValue)) { | |||
String attributeValue = toAttributeValue(value.getClass(), value); | |||
attributes.put(attribute, attributeValue); | |||
} | |||
} | |||
/** | |||
* Formats the given design attribute value. The method is provided to | |||
* ensure consistent number formatting for design attribute values |
@@ -0,0 +1,46 @@ | |||
/* | |||
* Copyright 2000-2014 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.layoutparser; | |||
import java.io.FileInputStream; | |||
import java.io.FileNotFoundException; | |||
import junit.framework.TestCase; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
import com.vaadin.ui.declarative.LayoutHandler; | |||
/** | |||
* Just top level test case that contains all synchronizable components | |||
* | |||
* @author Vaadin Ltd | |||
*/ | |||
public class ParseAllSupportedComponentsTest extends TestCase { | |||
public void testParsing() { | |||
try { | |||
DesignContext ctx = LayoutHandler | |||
.parse(new FileInputStream( | |||
"server/tests/src/com/vaadin/tests/layoutparser/all-components.html")); | |||
assertNotNull("The returned design context can not be null", ctx); | |||
assertNotNull("the component root can not be null", | |||
ctx.getComponentRoot()); | |||
} catch (FileNotFoundException e) { | |||
e.printStackTrace(); | |||
fail("Template parsing threw exception"); | |||
} | |||
} | |||
} |
@@ -0,0 +1,46 @@ | |||
<!DOCTYPE html> | |||
<html> | |||
<head> | |||
<meta name="package-mapping" content="my:com.addon.mypackage"/> | |||
</head> | |||
<body> | |||
<v-vertical-layout> | |||
<!-- abstract component --> | |||
<v-button primary-style-name="button" id="foo" style-name="red" caption="Some caption" icon="vaadin://themes/runo/icons/16/ok.png" description="My tooltip" error="Something went wrong" locale="en_US"></v-button> | |||
<!-- absolute layout --> | |||
<v-absolute-layout> | |||
<v-button :top="100px" :left="0px" :z-index=21>OK</v-button> | |||
<v-button :bottom="0px" :right="0px">Cancel</v-button> | |||
</v-absolute-layout> | |||
<!-- vertical layout --> | |||
<v-vertical-layout spacing margin> | |||
<v-button :top>OK</v-button> | |||
<v-table size-full :expand=1 /> | |||
</v-vertical-layout> | |||
<!-- horizontal layout --> | |||
<v-horizontal-layout spacing margin> | |||
<v-button :top>OK</v-button> | |||
<v-table size-full :expand=1 /> | |||
</v-horizontal-layout> | |||
<!-- form layout --> | |||
<v-form-layout spacing margin> | |||
<v-button :top>OK</v-button> | |||
<v-table size-full :expand=1 /> | |||
</v-form-layout> | |||
<!-- css layout --> | |||
<v-css-layout> | |||
<v-button>OK</v-button> | |||
<v-table size-full /> | |||
</v-css-layout> | |||
<!-- abstract field --> | |||
<v-text-field buffered validation-visible=false invalid-committed invalid-allowed=false required required-error="This is a required field" conversion-error="Input {0} cannot be parsed" tabindex=3 readonly/> | |||
</v-vertical-layout> | |||
</body> | |||
</html> |
@@ -32,7 +32,7 @@ import com.vaadin.ui.Label; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
/** | |||
* Test case for reading the attributes of the abstract component from design | |||
* Test case for reading the attributes of the AbstractComponent from design | |||
* | |||
* @author Vaadin Ltd | |||
*/ |
@@ -37,7 +37,7 @@ import com.vaadin.ui.HorizontalSplitPanel; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
/** | |||
* Test case for reading the attributes of the abstract component from design | |||
* Test case for writing the attributes of the AbstractComponent to design | |||
* | |||
* @author Vaadin Ltd | |||
*/ |
@@ -0,0 +1,71 @@ | |||
/* | |||
* Copyright 2000-2014 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.server.component.abstractfield; | |||
import junit.framework.TestCase; | |||
import org.jsoup.nodes.Attributes; | |||
import org.jsoup.nodes.Element; | |||
import org.jsoup.parser.Tag; | |||
import com.vaadin.ui.AbstractField; | |||
import com.vaadin.ui.TextField; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
/** | |||
* | |||
* Test case for reading the attributes of the AbstractField from design | |||
* | |||
* @author Vaadin Ltd | |||
*/ | |||
public class TestSynchronizeFromDesign extends TestCase { | |||
private DesignContext ctx; | |||
@Override | |||
protected void setUp() throws Exception { | |||
super.setUp(); | |||
ctx = new DesignContext(); | |||
} | |||
public void testSynchronizeReadOnly() { | |||
Element design = createDesign("readonly", ""); | |||
AbstractField component = getComponent(); | |||
component.synchronizeFromDesign(design, ctx); | |||
assertEquals(true, component.isReadOnly()); | |||
design = createDesign("readonly", "false"); | |||
component.synchronizeFromDesign(design, ctx); | |||
assertEquals(false, component.isReadOnly()); | |||
} | |||
public void testSynchronizeTabIndex() { | |||
Element design = createDesign("tabindex", "2"); | |||
AbstractField component = getComponent(); | |||
component.synchronizeFromDesign(design, ctx); | |||
assertEquals("Tab index must be 2", 2, component.getTabIndex()); | |||
} | |||
private AbstractField getComponent() { | |||
return new TextField(); | |||
} | |||
private Element createDesign(String key, String value) { | |||
Attributes attributes = new Attributes(); | |||
attributes.put(key, value); | |||
Element node = new Element(Tag.valueOf("v-text-field"), "", attributes); | |||
return node; | |||
} | |||
} |
@@ -0,0 +1,77 @@ | |||
/* | |||
* Copyright 2000-2014 Vaadin Ltd. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not | |||
* use this file except in compliance with the License. You may obtain a copy of | |||
* the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||
* License for the specific language governing permissions and limitations under | |||
* the License. | |||
*/ | |||
package com.vaadin.tests.server.component.abstractfield; | |||
import junit.framework.TestCase; | |||
import org.jsoup.nodes.Attributes; | |||
import org.jsoup.nodes.Element; | |||
import org.jsoup.parser.Tag; | |||
import com.vaadin.data.util.ObjectProperty; | |||
import com.vaadin.ui.AbstractField; | |||
import com.vaadin.ui.TextField; | |||
import com.vaadin.ui.declarative.DesignContext; | |||
/** | |||
* Test case for writing the attributes of the AbstractField to design | |||
* | |||
* @author Vaadin Ltd | |||
*/ | |||
public class TestSynchronizeToDesign extends TestCase { | |||
private DesignContext ctx; | |||
@Override | |||
protected void setUp() throws Exception { | |||
super.setUp(); | |||
ctx = new DesignContext(); | |||
} | |||
public void testSynchronizeReadOnly() { | |||
Element design = createDesign(); | |||
AbstractField component = getComponent(); | |||
component.setReadOnly(true); | |||
component.synchronizeToDesign(design, ctx); | |||
// we only changed one of the attributes, others are at default values | |||
assertEquals(1, design.attributes().size()); | |||
assertTrue("Design must contain readonly", design.hasAttr("readonly")); | |||
assertTrue("Readonly must be true", design.attr("readonly").equals("") | |||
|| design.attr("readonly").equals("true")); | |||
} | |||
public void testSynchronizeModelReadOnly() { | |||
Element design = createDesign(); | |||
AbstractField component = getComponent(); | |||
ObjectProperty property = new ObjectProperty<String>("test"); | |||
property.setReadOnly(true); | |||
component.setPropertyDataSource(property); | |||
component.synchronizeToDesign(design, ctx); | |||
// make sure that property readonly is not written to design | |||
assertFalse("Design must not contain readonly", | |||
design.hasAttr("readonly")); | |||
} | |||
private AbstractField getComponent() { | |||
return new TextField(); | |||
} | |||
private Element createDesign() { | |||
Attributes attr = new Attributes(); | |||
attr.put("should_be_removed", "foo"); | |||
return new Element(Tag.valueOf("v-text-field"), "", attr); | |||
} | |||
} |