summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java15
-rw-r--r--client-compiler/src/com/vaadin/server/widgetsetutils/ClassPathExplorer.java2
-rw-r--r--client/src/com/vaadin/client/ApplicationConnection.java245
-rw-r--r--client/src/com/vaadin/client/communication/StateChangeEvent.java181
-rw-r--r--client/src/com/vaadin/client/metadata/Type.java5
-rw-r--r--client/src/com/vaadin/client/metadata/TypeDataStore.java18
-rw-r--r--common.xml1
-rw-r--r--server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java14
-rw-r--r--server/src/com/vaadin/data/util/AbstractProperty.java11
-rw-r--r--server/src/com/vaadin/data/util/IndexedContainer.java11
-rw-r--r--server/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java11
-rw-r--r--server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java6
-rw-r--r--server/src/com/vaadin/server/AbstractCommunicationManager.java106
-rw-r--r--server/src/com/vaadin/server/DragAndDropService.java7
-rw-r--r--server/src/com/vaadin/server/GAEVaadinServlet.java17
-rw-r--r--server/src/com/vaadin/server/JsonPaintTarget.java21
-rw-r--r--server/src/com/vaadin/server/ServerRpcManager.java8
-rw-r--r--server/src/com/vaadin/server/VaadinPortlet.java6
-rw-r--r--server/src/com/vaadin/server/VaadinService.java15
-rw-r--r--server/src/com/vaadin/server/VaadinServlet.java55
-rw-r--r--server/src/com/vaadin/ui/AbstractMedia.java7
-rw-r--r--server/src/com/vaadin/ui/ConnectorTracker.java76
-rw-r--r--server/src/com/vaadin/ui/Tree.java34
-rw-r--r--server/src/com/vaadin/ui/UI.java1
-rw-r--r--server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java5
-rw-r--r--server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java41
-rw-r--r--theme-compiler/src/com/vaadin/sass/SassCompiler.java7
-rw-r--r--theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java14
-rw-r--r--theme-compiler/src/com/vaadin/sass/internal/util/StringUtil.java78
-rw-r--r--theme-compiler/src/com/vaadin/sass/internal/visitor/ExtendNodeHandler.java57
-rw-r--r--theme-compiler/tests/resources/automatic/css/extend-in-nested-block.css7
-rw-r--r--theme-compiler/tests/resources/automatic/css/extend-selector-in-different-levels.css15
-rw-r--r--theme-compiler/tests/resources/automatic/css/extending-non-exist-selector-with-same-beginning.css7
-rw-r--r--theme-compiler/tests/resources/automatic/css/extending-same-selector.css7
-rw-r--r--theme-compiler/tests/resources/automatic/css/extending-selector-with-same-beginning.css7
-rw-r--r--theme-compiler/tests/resources/automatic/scss/extend-in-nested-block.scss11
-rw-r--r--theme-compiler/tests/resources/automatic/scss/extend-selector-in-different-levels.scss26
-rw-r--r--theme-compiler/tests/resources/automatic/scss/extending-non-exist-selector-with-same-beginning.scss8
-rw-r--r--theme-compiler/tests/resources/automatic/scss/extending-same-selector.scss8
-rw-r--r--theme-compiler/tests/resources/automatic/scss/extending-selector-with-same-beginning.scss8
-rw-r--r--theme-compiler/tests/resources/sasslang/css/19-test_control_flow_if.css (renamed from theme-compiler/tests/resources/sasslangbroken/css/19-test_control_flow_if.css)0
-rw-r--r--theme-compiler/tests/resources/sasslang/scss/19-test_control_flow_if.scss (renamed from theme-compiler/tests/resources/sasslangbroken/scss/19-test_control_flow_if.scss)0
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/internal/util/StringUtilTest.java58
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AbstractDirectoryScanningSassTests.java6
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTests.java7
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTestsBroken.java28
-rw-r--r--theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassTestRunner.java2
-rw-r--r--uitest/src/com/vaadin/tests/widgetset/client/DelegateWidget.java1
48 files changed, 915 insertions, 366 deletions
diff --git a/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java b/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java
index 76952bb329..588fe1aba1 100644
--- a/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java
+++ b/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright 2000-2013 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.buildhelpers;
import java.io.IOException;
diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/ClassPathExplorer.java b/client-compiler/src/com/vaadin/server/widgetsetutils/ClassPathExplorer.java
index 90877c47c8..34024f1616 100644
--- a/client-compiler/src/com/vaadin/server/widgetsetutils/ClassPathExplorer.java
+++ b/client-compiler/src/com/vaadin/server/widgetsetutils/ClassPathExplorer.java
@@ -239,7 +239,7 @@ public class ClassPathExplorer {
classpath = classpath.substring(0, classpath.length() - 1);
}
- getLogger().fine("Classpath: " + classpath);
+ getLogger().log(Level.FINE, "Classpath: {0}", classpath);
String[] split = classpath.split(pathSep);
for (int i = 0; i < split.length; i++) {
diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java
index de034a65a6..1a637e3161 100644
--- a/client/src/com/vaadin/client/ApplicationConnection.java
+++ b/client/src/com/vaadin/client/ApplicationConnection.java
@@ -16,15 +16,13 @@
package com.vaadin.client;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -113,8 +111,15 @@ public class ApplicationConnection {
* Helper used to return two values when updating the connector hierarchy.
*/
private static class ConnectorHierarchyUpdateResult {
- private List<ConnectorHierarchyChangeEvent> events = new LinkedList<ConnectorHierarchyChangeEvent>();
- private List<ServerConnector> parentChanged = new LinkedList<ServerConnector>();
+ /**
+ * Needed at a later point when the created events are fired
+ */
+ private JsArrayObject<ConnectorHierarchyChangeEvent> events = JavaScriptObject
+ .createArray().cast();
+ /**
+ * Needed to know where captions might need to get updated
+ */
+ private FastStringSet parentChangedIds = FastStringSet.create();
}
public static final String MODIFIED_CLASSNAME = "v-modified";
@@ -153,9 +158,6 @@ public class ApplicationConnection {
// will hold the UIDL security key (for XSS protection) once received
private String uidlSecurityKey = "init";
- private static final FastStringMap<FastStringSet> allStateFieldsCache = FastStringMap
- .create();
-
private final HashMap<String, String> resourcesMap = new HashMap<String, String>();
/**
@@ -217,8 +219,6 @@ public class ApplicationConnection {
/** redirectTimer scheduling interval in seconds */
private int sessionExpirationInterval;
- private ArrayList<Widget> componentCaptionSizeChanges = new ArrayList<Widget>();
-
private Date requestStartTime;
private boolean validatingLayouts = false;
@@ -1424,16 +1424,14 @@ public class ApplicationConnection {
redirectTimer.schedule(1000 * sessionExpirationInterval);
}
- componentCaptionSizeChanges.clear();
-
double processUidlStart = Duration.currentTimeMillis();
// Ensure that all connectors that we are about to update exist
- Set<ServerConnector> createdConnectors = createConnectorsIfNeeded(json);
+ JsArrayString createdConnectorIds = createConnectorsIfNeeded(json);
// Update states, do not fire events
- Collection<StateChangeEvent> pendingStateChangeEvents = updateConnectorState(
- json, createdConnectors);
+ JsArrayObject<StateChangeEvent> pendingStateChangeEvents = updateConnectorState(
+ json, createdConnectorIds);
// Update hierarchy, do not fire events
ConnectorHierarchyUpdateResult connectorHierarchyUpdateResult = updateConnectorHierarchy(json);
@@ -1442,7 +1440,7 @@ public class ApplicationConnection {
sendHierarchyChangeEvents(connectorHierarchyUpdateResult.events);
updateCaptions(pendingStateChangeEvents,
- connectorHierarchyUpdateResult.parentChanged);
+ connectorHierarchyUpdateResult.parentChangedIds);
delegateToWidget(pendingStateChangeEvents);
@@ -1566,27 +1564,35 @@ public class ApplicationConnection {
}
private void updateCaptions(
- Collection<StateChangeEvent> pendingStateChangeEvents,
- Collection<ServerConnector> parentChanged) {
+ JsArrayObject<StateChangeEvent> pendingStateChangeEvents,
+ FastStringSet parentChangedIds) {
Profiler.enter("updateCaptions");
/*
* Find all components that might need a caption update based on
* pending state and hierarchy changes
*/
- HashSet<ServerConnector> needsCaptionUpdate = new HashSet<ServerConnector>(
- parentChanged);
+ FastStringSet needsCaptionUpdate = FastStringSet.create();
+ needsCaptionUpdate.addAll(parentChangedIds);
// Find components with potentially changed caption state
- for (StateChangeEvent event : pendingStateChangeEvents) {
+ int size = pendingStateChangeEvents.size();
+ for (int i = 0; i < size; i++) {
+ StateChangeEvent event = pendingStateChangeEvents.get(i);
if (VCaption.mightChange(event)) {
ServerConnector connector = event.getConnector();
- needsCaptionUpdate.add(connector);
+ needsCaptionUpdate.add(connector.getConnectorId());
}
}
+ ConnectorMap connectorMap = getConnectorMap();
+
// Update captions for all suitable candidates
- for (ServerConnector child : needsCaptionUpdate) {
+ JsArrayString dump = needsCaptionUpdate.dump();
+ int needsUpdateLength = dump.length();
+ for (int i = 0; i < needsUpdateLength; i++) {
+ String childId = dump.get(i);
+ ServerConnector child = connectorMap.getConnector(childId);
if (child instanceof ComponentConnector
&& ((ComponentConnector) child)
.delegateCaptionHandling()) {
@@ -1604,29 +1610,45 @@ public class ApplicationConnection {
}
private void delegateToWidget(
- Collection<StateChangeEvent> pendingStateChangeEvents) {
+ JsArrayObject<StateChangeEvent> pendingStateChangeEvents) {
Profiler.enter("@DelegateToWidget");
VConsole.log(" * Running @DelegateToWidget");
- for (StateChangeEvent sce : pendingStateChangeEvents) {
+ // Keep track of types that have no @DelegateToWidget in their
+ // state to optimize performance
+ FastStringSet noOpTypes = FastStringSet.create();
+
+ int size = pendingStateChangeEvents.size();
+ for (int eventIndex = 0; eventIndex < size; eventIndex++) {
+ StateChangeEvent sce = pendingStateChangeEvents
+ .get(eventIndex);
ServerConnector connector = sce.getConnector();
if (connector instanceof ComponentConnector) {
+ String className = connector.getClass().getName();
+ if (noOpTypes.contains(className)) {
+ continue;
+ }
ComponentConnector component = (ComponentConnector) connector;
Type stateType = AbstractConnector
.getStateType(component);
+ JsArrayString delegateToWidgetProperties = stateType
+ .getDelegateToWidgetProperties();
+ if (delegateToWidgetProperties == null) {
+ noOpTypes.add(className);
+ continue;
+ }
- FastStringSet changedProperties = sce
- .getChangedPropertiesFastSet();
- JsArrayString dump = changedProperties.dump();
- for (int i = 0; i < dump.length(); i++) {
- String propertyName = dump.get(i);
- Property property = stateType
- .getProperty(propertyName);
- String method = property
- .getDelegateToWidgetMethodName();
- if (method != null) {
+ int length = delegateToWidgetProperties.length();
+ for (int i = 0; i < length; i++) {
+ String propertyName = delegateToWidgetProperties
+ .get(i);
+ if (sce.hasPropertyChanged(propertyName)) {
+ Property property = stateType
+ .getProperty(propertyName);
+ String method = property
+ .getDelegateToWidgetMethodName();
Profiler.enter("doDelegateToWidget");
doDelegateToWidget(component, property, method);
Profiler.leave("doDelegateToWidget");
@@ -1673,11 +1695,13 @@ public class ApplicationConnection {
* The events to send
*/
private void sendStateChangeEvents(
- Collection<StateChangeEvent> pendingStateChangeEvents) {
+ JsArrayObject<StateChangeEvent> pendingStateChangeEvents) {
Profiler.enter("sendStateChangeEvents");
VConsole.log(" * Sending state change events");
- for (StateChangeEvent sce : pendingStateChangeEvents) {
+ int size = pendingStateChangeEvents.size();
+ for (int i = 0; i < size; i++) {
+ StateChangeEvent sce = pendingStateChangeEvents.get(i);
try {
sce.getConnector().fireEvent(sce);
} catch (final Throwable e) {
@@ -1723,29 +1747,29 @@ public class ApplicationConnection {
Profiler.leave("unregisterRemovedConnectors");
}
- private Set<ServerConnector> createConnectorsIfNeeded(ValueMap json) {
+ private JsArrayString createConnectorsIfNeeded(ValueMap json) {
VConsole.log(" * Creating connectors (if needed)");
+ JsArrayString createdConnectors = JavaScriptObject
+ .createArray().cast();
if (!json.containsKey("types")) {
- return Collections.emptySet();
+ return createdConnectors;
}
Profiler.enter("Creating connectors");
- Set<ServerConnector> createdConnectors = new HashSet<ServerConnector>();
-
ValueMap types = json.getValueMap("types");
JsArrayString keyArray = types.getKeyArray();
for (int i = 0; i < keyArray.length(); i++) {
try {
String connectorId = keyArray.get(i);
- int connectorType = Integer.parseInt(types
- .getString((connectorId)));
ServerConnector connector = connectorMap
.getConnector(connectorId);
if (connector != null) {
continue;
}
+ int connectorType = Integer.parseInt(types
+ .getString(connectorId));
Class<? extends ServerConnector> connectorClass = configuration
.getConnectorClassByEncodedTag(connectorType);
@@ -1757,7 +1781,7 @@ public class ApplicationConnection {
connector = getConnector(connectorId, connectorType);
Profiler.leave("ApplicationConnection.getConnector");
- createdConnectors.add(connector);
+ createdConnectors.push(connectorId);
} else {
// First UIConnector update. Before this the
// UIConnector has been created but not
@@ -1767,7 +1791,7 @@ public class ApplicationConnection {
uIConnector);
uIConnector.doInit(connectorId,
ApplicationConnection.this);
- createdConnectors.add(uIConnector);
+ createdConnectors.push(connectorId);
}
} catch (final Throwable e) {
VConsole.error(e);
@@ -1829,14 +1853,16 @@ public class ApplicationConnection {
}
private void sendHierarchyChangeEvents(
- Collection<ConnectorHierarchyChangeEvent> pendingHierarchyChangeEvents) {
- if (pendingHierarchyChangeEvents.isEmpty()) {
+ JsArrayObject<ConnectorHierarchyChangeEvent> events) {
+ int eventCount = events.size();
+ if (eventCount == 0) {
return;
}
Profiler.enter("sendHierarchyChangeEvents");
VConsole.log(" * Sending hierarchy change events");
- for (ConnectorHierarchyChangeEvent event : pendingHierarchyChangeEvents) {
+ for (int i = 0; i < eventCount; i++) {
+ ConnectorHierarchyChangeEvent event = events.get(i);
try {
logHierarchyChange(event);
event.getConnector().fireEvent(event);
@@ -1871,9 +1897,10 @@ public class ApplicationConnection {
VConsole.log(newChildren);
}
- private Collection<StateChangeEvent> updateConnectorState(
- ValueMap json, Set<ServerConnector> newConnectors) {
- ArrayList<StateChangeEvent> events = new ArrayList<StateChangeEvent>();
+ private JsArrayObject<StateChangeEvent> updateConnectorState(
+ ValueMap json, JsArrayString createdConnectorIds) {
+ JsArrayObject<StateChangeEvent> events = JavaScriptObject
+ .createArray().cast();
VConsole.log(" * Updating connector states");
if (!json.containsKey("state")) {
return events;
@@ -1881,8 +1908,8 @@ public class ApplicationConnection {
Profiler.enter("updateConnectorState");
- HashSet<ServerConnector> remainingNewConnectors = new HashSet<ServerConnector>(
- newConnectors);
+ FastStringSet remainingNewConnectors = FastStringSet.create();
+ remainingNewConnectors.addAll(createdConnectorIds);
// set states for all paintables mentioned in "state"
ValueMap states = json.getValueMap("state");
@@ -1923,22 +1950,15 @@ public class ApplicationConnection {
}
Profiler.enter("updateConnectorState create event");
- FastStringSet changedProperties = FastStringSet
- .create();
- addJsonFields(stateJson, changedProperties, "");
-
- if (newConnectors.contains(connector)) {
- remainingNewConnectors.remove(connector);
- // Fire events for properties using the default
- // value for newly created connectors
- FastStringSet allStateFields = getAllStateFields(AbstractConnector
- .getStateType(connector));
- changedProperties.addAll(allStateFields);
+
+ boolean isNewConnector = remainingNewConnectors
+ .contains(connectorId);
+ if (isNewConnector) {
+ remainingNewConnectors.remove(connectorId);
}
StateChangeEvent event = new StateChangeEvent(
- connector, changedProperties);
-
+ connector, stateJson, isNewConnector);
events.add(event);
Profiler.leave("updateConnectorState create event");
@@ -1952,12 +1972,15 @@ public class ApplicationConnection {
Profiler.enter("updateConnectorState newWithoutState");
// Fire events for properties using the default value for newly
// created connectors even if there were no state changes
- for (ServerConnector connector : remainingNewConnectors) {
- FastStringSet changedProperties = getAllStateFields(AbstractConnector
- .getStateType(connector));
+ JsArrayString dump = remainingNewConnectors.dump();
+ int length = dump.length();
+ for (int i = 0; i < length; i++) {
+ String connectorId = dump.get(i);
+ ServerConnector connector = connectorMap
+ .getConnector(connectorId);
StateChangeEvent event = new StateChangeEvent(connector,
- changedProperties);
+ new JSONObject(), true);
events.add(event);
@@ -1969,80 +1992,6 @@ public class ApplicationConnection {
return events;
}
- private FastStringSet getAllStateFields(Type type) {
- FastStringSet fields;
- fields = allStateFieldsCache.get(type.getBaseTypeName());
- if (fields == null) {
- Profiler.enter("getAllStateFields create");
- fields = FastStringSet.create();
- addAllStateFields(type, fields, "");
- allStateFieldsCache.put(type.getBaseTypeName(), fields);
- Profiler.leave("getAllStateFields create");
- }
- return fields;
- }
-
- /**
- * Recursively adds the names of all properties in the provided
- * state type.
- *
- * @param type
- * the type to process
- * @param foundProperties
- * a set of all currently added properties
- * @param context
- * the base name of the current object
- */
- private void addAllStateFields(Type type,
- FastStringSet foundProperties, String context) {
- try {
- JsArrayObject<Property> properties = type
- .getPropertiesAsArray();
- int size = properties.size();
- for (int i = 0; i < size; i++) {
- Property property = properties.get(i);
- String propertyName = context + property.getName();
- foundProperties.add(propertyName);
-
- Type propertyType = property.getType();
- if (propertyType.hasProperties()) {
- addAllStateFields(propertyType, foundProperties,
- propertyName + ".");
- }
- }
- } catch (NoDataException e) {
- throw new IllegalStateException(
- "No property info for "
- + type
- + ". Did you remember to compile the right widgetset?",
- e);
- }
- }
-
- /**
- * Recursively adds the names of all fields in all objects in the
- * provided json object.
- *
- * @param json
- * the json object to process
- * @param fields
- * a set of all currently added fields
- * @param context
- * the base name of the current object
- */
- private void addJsonFields(JSONObject json, FastStringSet fields,
- String context) {
- for (String key : json.keySet()) {
- String fieldName = context + key;
- fields.add(fieldName);
-
- JSONObject object = json.get(key).isObject();
- if (object != null) {
- addJsonFields(object, fields, fieldName + ".");
- }
- }
- }
-
/**
* Updates the connector hierarchy and returns a list of events that
* should be fired after update of the hierarchy and the state is
@@ -2106,7 +2055,7 @@ public class ApplicationConnection {
}
if (childConnector.getParent() != parentConnector) {
childConnector.setParent(parentConnector);
- result.parentChanged.add(childConnector);
+ result.parentChangedIds.add(childConnectorId);
// Not detached even if previously removed from
// parent
maybeDetached.remove(childConnectorId);
@@ -2191,7 +2140,7 @@ public class ApplicationConnection {
}
private void recursivelyDetach(ServerConnector connector,
- List<ConnectorHierarchyChangeEvent> events) {
+ JsArrayObject<ConnectorHierarchyChangeEvent> events) {
/*
* Reset state in an attempt to keep it consistent with the
@@ -3089,9 +3038,11 @@ public class ApplicationConnection {
*
* @param component
* the Paintable whose caption has changed
+ * @deprecated As of 7.0.2, has not had any effect for a long time
*/
+ @Deprecated
public void captionSizeUpdated(Widget widget) {
- componentCaptionSizeChanges.add(widget);
+ // This doesn't do anything, it's just kept here for compatibility
}
/**
diff --git a/client/src/com/vaadin/client/communication/StateChangeEvent.java b/client/src/com/vaadin/client/communication/StateChangeEvent.java
index 35187b03d4..e17a56aa69 100644
--- a/client/src/com/vaadin/client/communication/StateChangeEvent.java
+++ b/client/src/com/vaadin/client/communication/StateChangeEvent.java
@@ -19,10 +19,17 @@ import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
+import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.event.shared.EventHandler;
+import com.google.gwt.json.client.JSONObject;
import com.vaadin.client.FastStringSet;
+import com.vaadin.client.JsArrayObject;
+import com.vaadin.client.Profiler;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler;
+import com.vaadin.client.metadata.NoDataException;
+import com.vaadin.client.metadata.Property;
+import com.vaadin.client.ui.AbstractConnector;
public class StateChangeEvent extends
AbstractServerConnectorEvent<StateChangeHandler> {
@@ -31,14 +38,24 @@ public class StateChangeEvent extends
*/
public static final Type<StateChangeHandler> TYPE = new Type<StateChangeHandler>();
- private final FastStringSet changedProperties;
+ /**
+ * Used to cache a FastStringSet representation of the properties that have
+ * changed if one is needed.
+ */
+ @Deprecated
+ private FastStringSet changedProperties;
/**
* Used to cache a Set representation of the changedProperties if one is
* needed.
*/
+ @Deprecated
private Set<String> changedPropertiesSet;
+ private boolean isNewConnector = false;
+
+ private JSONObject stateJson;
+
@Override
public Type<StateChangeHandler> getAssociatedType() {
return TYPE;
@@ -52,14 +69,16 @@ public class StateChangeEvent extends
* @param changedPropertiesSet
* a set of names of the changed properties
* @deprecated As of 7.0.1, use
- * {@link #StateChangeEvent(ServerConnector, FastStringSet)}
+ * {@link #StateChangeEvent(ServerConnector, JSONObject, boolean)}
* instead for improved performance.
*/
@Deprecated
public StateChangeEvent(ServerConnector connector,
Set<String> changedPropertiesSet) {
setConnector(connector);
+ // Keep instance around for caching
this.changedPropertiesSet = changedPropertiesSet;
+
changedProperties = FastStringSet.create();
for (String property : changedPropertiesSet) {
changedProperties.add(property);
@@ -73,13 +92,35 @@ public class StateChangeEvent extends
* the event whose state has changed
* @param changedProperties
* a set of names of the changed properties
+ * @deprecated As of 7.0.2, use
+ * {@link #StateChangeEvent(ServerConnector, JSONObject, boolean)}
+ * instead for improved performance.
*/
+ @Deprecated
public StateChangeEvent(ServerConnector connector,
FastStringSet changedProperties) {
setConnector(connector);
this.changedProperties = changedProperties;
}
+ /**
+ * /** Creates a new state change event.
+ *
+ * @param connector
+ * the event whose state has changed
+ * @param stateJson
+ * the JSON representation of the state change
+ * @param isNewConnector
+ * <code>true</code> if the state change is for a new connector,
+ * otherwise <code>false</code>
+ */
+ public StateChangeEvent(ServerConnector connector, JSONObject stateJson,
+ boolean isNewConnector) {
+ setConnector(connector);
+ this.stateJson = stateJson;
+ this.isNewConnector = isNewConnector;
+ }
+
@Override
public void dispatch(StateChangeHandler listener) {
listener.onStateChanged(this);
@@ -108,15 +149,16 @@ public class StateChangeEvent extends
*
* @return a set of names of the changed properties
*
- * @deprecated As of 7.0.1, use {@link #getChangedPropertiesFastSet()} or
- * {@link #hasPropertyChanged(String)} instead for improved
- * performance.
+ * @deprecated As of 7.0.1, use {@link #hasPropertyChanged(String)} instead
+ * for improved performance.
*/
@Deprecated
public Set<String> getChangedProperties() {
if (changedPropertiesSet == null) {
+ Profiler.enter("StateChangeEvent.getChangedProperties populate");
changedPropertiesSet = new HashSet<String>();
- changedProperties.addAllTo(changedPropertiesSet);
+ getChangedPropertiesFastSet().addAllTo(changedPropertiesSet);
+ Profiler.leave("StateChangeEvent.getChangedProperties populate");
}
return changedPropertiesSet;
}
@@ -126,8 +168,24 @@ public class StateChangeEvent extends
*
* @return a set of names of the changed properties
*
+ * @deprecated As of 7.0.1, use {@link #hasPropertyChanged(String)} instead
+ * for improved performance.
*/
+ @Deprecated
public FastStringSet getChangedPropertiesFastSet() {
+ if (changedProperties == null) {
+ Profiler.enter("StateChangeEvent.getChangedPropertiesFastSet populate");
+ changedProperties = FastStringSet.create();
+
+ addJsonFields(stateJson, changedProperties, "");
+ if (isNewConnector) {
+ addAllStateFields(
+ AbstractConnector.getStateType(getConnector()),
+ changedProperties, "");
+ }
+
+ Profiler.leave("StateChangeEvent.getChangedPropertiesFastSet populate");
+ }
return changedProperties;
}
@@ -140,6 +198,115 @@ public class StateChangeEvent extends
* <code>false></code>
*/
public boolean hasPropertyChanged(String property) {
- return changedProperties.contains(property);
+ if (isNewConnector) {
+ // Everything has changed for a new connector
+ return true;
+ } else if (stateJson != null) {
+ // Check whether it's in the json object
+ return isInJson(property, stateJson.getJavaScriptObject());
+ } else {
+ // Legacy cases
+ if (changedProperties != null) {
+ // Check legacy stuff
+ return changedProperties.contains(property);
+ } else if (changedPropertiesSet != null) {
+ // Check legacy stuff
+ return changedPropertiesSet.contains(property);
+ } else {
+ throw new IllegalStateException(
+ "StateChangeEvent should have either stateJson, changedProperties or changePropertiesSet");
+ }
+ }
+ }
+
+ /**
+ * Checks whether the given property name (which might contains dots) is
+ * defined in some JavaScript object.
+ *
+ * @param property
+ * the name of the property, might include dots to reference
+ * inner objects
+ * @param target
+ * the JavaScript object to check
+ * @return true if the property is defined
+ */
+ private static native final boolean isInJson(String property,
+ JavaScriptObject target)
+ /*-{
+ var segments = property.split('.');
+ while (typeof target == 'object') {
+ var nextSegment = segments.shift();
+ if (!(nextSegment in target)) {
+ // Abort if segment is not found
+ return false;
+ } else if (segments.length == 0) {
+ // Done if there are no more segments
+ return true;
+ } else {
+ // Else just go deeper
+ target = target[nextSegment];
+ }
+ }
+ // Not defined if we reach something that isn't an object
+ return false;
+ }-*/;
+
+ /**
+ * Recursively adds the names of all properties in the provided state type.
+ *
+ * @param type
+ * the type to process
+ * @param changedProperties
+ * a set of all currently added properties
+ * @param context
+ * the base name of the current object
+ */
+ @Deprecated
+ private static void addAllStateFields(com.vaadin.client.metadata.Type type,
+ FastStringSet changedProperties, String context) {
+ try {
+ JsArrayObject<Property> properties = type.getPropertiesAsArray();
+ int size = properties.size();
+ for (int i = 0; i < size; i++) {
+ Property property = properties.get(i);
+ String propertyName = context + property.getName();
+ changedProperties.add(propertyName);
+
+ com.vaadin.client.metadata.Type propertyType = property
+ .getType();
+ if (propertyType.hasProperties()) {
+ addAllStateFields(propertyType, changedProperties,
+ propertyName + ".");
+ }
+ }
+ } catch (NoDataException e) {
+ throw new IllegalStateException("No property info for " + type
+ + ". Did you remember to compile the right widgetset?", e);
+ }
+ }
+
+ /**
+ * Recursively adds the names of all fields in all objects in the provided
+ * json object.
+ *
+ * @param json
+ * the json object to process
+ * @param changedProperties
+ * a set of all currently added fields
+ * @param context
+ * the base name of the current object
+ */
+ @Deprecated
+ private static void addJsonFields(JSONObject json,
+ FastStringSet changedProperties, String context) {
+ for (String key : json.keySet()) {
+ String fieldName = context + key;
+ changedProperties.add(fieldName);
+
+ JSONObject object = json.get(key).isObject();
+ if (object != null) {
+ addJsonFields(object, changedProperties, fieldName + ".");
+ }
+ }
}
}
diff --git a/client/src/com/vaadin/client/metadata/Type.java b/client/src/com/vaadin/client/metadata/Type.java
index 9c8a52d8e5..c09dffa638 100644
--- a/client/src/com/vaadin/client/metadata/Type.java
+++ b/client/src/com/vaadin/client/metadata/Type.java
@@ -17,6 +17,7 @@ package com.vaadin.client.metadata;
import java.util.Collection;
+import com.google.gwt.core.client.JsArrayString;
import com.vaadin.client.JsArrayObject;
import com.vaadin.client.communication.JSONSerializer;
@@ -138,4 +139,8 @@ public class Type {
return TypeDataStore.hasProperties(this);
}
+ public JsArrayString getDelegateToWidgetProperties() {
+ return TypeDataStore.getDelegateToWidgetProperites(this);
+ }
+
}
diff --git a/client/src/com/vaadin/client/metadata/TypeDataStore.java b/client/src/com/vaadin/client/metadata/TypeDataStore.java
index c1eca0a168..dff02749f8 100644
--- a/client/src/com/vaadin/client/metadata/TypeDataStore.java
+++ b/client/src/com/vaadin/client/metadata/TypeDataStore.java
@@ -19,6 +19,7 @@ import java.util.ArrayList;
import java.util.Collection;
import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.client.JsArrayString;
import com.vaadin.client.FastStringMap;
import com.vaadin.client.FastStringSet;
import com.vaadin.client.JsArrayObject;
@@ -35,6 +36,8 @@ public class TypeDataStore {
.create();
private final FastStringMap<JsArrayObject<Property>> properties = FastStringMap
.create();
+ private final FastStringMap<JsArrayString> delegateToWidgetProperties = FastStringMap
+ .create();
private final FastStringSet delayedMethods = FastStringSet.create();
private final FastStringSet lastOnlyMethods = FastStringSet.create();
@@ -118,11 +121,22 @@ public class TypeDataStore {
return get().delegateToWidget.get(property.getSignature());
}
+ public static JsArrayString getDelegateToWidgetProperites(Type type) {
+ return get().delegateToWidgetProperties.get(type.getSignature());
+ }
+
public void setDelegateToWidget(Class<?> clazz, String propertyName,
String delegateValue) {
- delegateToWidget.put(
- new Property(getType(clazz), propertyName).getSignature(),
+ Type type = getType(clazz);
+ delegateToWidget.put(new Property(type, propertyName).getSignature(),
delegateValue);
+ JsArrayString typeProperties = delegateToWidgetProperties.get(type
+ .getSignature());
+ if (typeProperties == null) {
+ typeProperties = JavaScriptObject.createArray().cast();
+ delegateToWidgetProperties.put(type.getSignature(), typeProperties);
+ }
+ typeProperties.push(propertyName);
}
public void setReturnType(Class<?> type, String methodName, Type returnType) {
diff --git a/common.xml b/common.xml
index 57d96664f0..6e88bf6113 100644
--- a/common.xml
+++ b/common.xml
@@ -316,6 +316,7 @@
<exclude name="com/vaadin/tests/data/bean/*" />
<exclude name="com/vaadin/tests/util/*" />
<exclude name="**/VaadinClasses.java" />
+ <exclude name="**TestRunner.java" />
<exclude name="**/SQLTestsConstants.java" />
</fileset>
</batchtest>
diff --git a/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java b/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java
index 55bd3a4641..502394f38a 100644
--- a/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java
+++ b/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java
@@ -143,8 +143,7 @@ public class BeanFieldGroup<T> extends FieldGroup {
return (BeanItem<T>) super.getItemDataSource();
}
- @Override
- public void bind(Field field, Object propertyId) {
+ private void ensureNestedPropertyAdded(Object propertyId) {
if (getItemDataSource() != null) {
// The data source is set so the property must be found in the item.
// If it is not we try to add it.
@@ -156,11 +155,22 @@ public class BeanFieldGroup<T> extends FieldGroup {
getItemDataSource().addNestedProperty((String) propertyId);
}
}
+ }
+ @Override
+ public void bind(Field field, Object propertyId) {
+ ensureNestedPropertyAdded(propertyId);
super.bind(field, propertyId);
}
@Override
+ public Field<?> buildAndBind(String caption, Object propertyId)
+ throws BindException {
+ ensureNestedPropertyAdded(propertyId);
+ return super.buildAndBind(caption, propertyId);
+ }
+
+ @Override
protected void configureField(Field<?> field) {
super.configureField(field);
// Add Bean validators if there are annotations
diff --git a/server/src/com/vaadin/data/util/AbstractProperty.java b/server/src/com/vaadin/data/util/AbstractProperty.java
index 9eafca6051..499421a8b4 100644
--- a/server/src/com/vaadin/data/util/AbstractProperty.java
+++ b/server/src/com/vaadin/data/util/AbstractProperty.java
@@ -18,6 +18,7 @@ package com.vaadin.data.util;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
+import java.util.logging.Level;
import java.util.logging.Logger;
import com.vaadin.data.Property;
@@ -81,11 +82,11 @@ public abstract class AbstractProperty<T> implements Property<T>,
@Override
public String toString() {
getLogger()
- .warning(
- "You are using Property.toString() instead of getValue() to get the value for a "
- + getClass().getSimpleName()
- + ". This will not be supported starting from Vaadin 7.1 "
- + "(your debugger might call toString() and cause this message to appear).");
+ .log(Level.WARNING,
+ "You are using Property.toString() instead of getValue() to get the value for a {0}."
+ + "This will not be supported starting from Vaadin 7.1 "
+ + "(your debugger might call toString() and cause this message to appear).",
+ getClass().getSimpleName());
T v = getValue();
if (v == null) {
return null;
diff --git a/server/src/com/vaadin/data/util/IndexedContainer.java b/server/src/com/vaadin/data/util/IndexedContainer.java
index 306f9cf23b..1df4dd9bfb 100644
--- a/server/src/com/vaadin/data/util/IndexedContainer.java
+++ b/server/src/com/vaadin/data/util/IndexedContainer.java
@@ -28,6 +28,7 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.logging.Level;
import java.util.logging.Logger;
import com.vaadin.data.Container;
@@ -966,11 +967,11 @@ public class IndexedContainer extends
@Override
public String toString() {
getLogger()
- .warning(
- "You are using IndexedContainerProperty.toString() instead of getValue() to get the value for a "
- + getClass().getSimpleName()
- + ". This will not be supported starting from Vaadin 7.1 "
- + "(your debugger might call toString() and cause this message to appear).");
+ .log(Level.WARNING,
+ "You are using IndexedContainerProperty.toString() instead of getValue() to get the value for a {0}."
+ + " This will not be supported starting from Vaadin 7.1 "
+ + "(your debugger might call toString() and cause this message to appear).",
+ getClass().getSimpleName());
Object v = getValue();
if (v == null) {
return null;
diff --git a/server/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java b/server/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java
index 785c7b22af..378372d044 100644
--- a/server/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java
+++ b/server/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java
@@ -18,6 +18,7 @@ package com.vaadin.data.util.sqlcontainer;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
+import java.util.logging.Level;
import java.util.logging.Logger;
import com.vaadin.data.Property;
@@ -264,11 +265,11 @@ final public class ColumnProperty implements Property {
@Override
public String toString() {
getLogger()
- .warning(
- "You are using ColumnProperty.toString() instead of getValue() to get the value for a "
- + getClass().getSimpleName()
- + ". This will not be supported starting from Vaadin 7.1 "
- + "(your debugger might call toString() and cause this message to appear).");
+ .log(Level.WARNING,
+ "You are using ColumnProperty.toString() instead of getValue() to get the value for a {0}. "
+ + "This will not be supported starting from Vaadin 7.1 (your debugger might call toString() "
+ + "and cause this message to appear).",
+ getClass().getSimpleName());
Object v = getValue();
if (v == null) {
return null;
diff --git a/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java b/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java
index 4d60a8cc17..63e4b3362c 100644
--- a/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java
+++ b/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java
@@ -610,8 +610,10 @@ public class TableQuery implements QueryDelegate,
@Override
public boolean removeRow(RowItem row) throws UnsupportedOperationException,
SQLException {
- getLogger().log(Level.FINE, "Removing row with id: {0}",
- row.getId().getId()[0].toString());
+ if (getLogger().isLoggable(Level.FINE)) {
+ getLogger().log(Level.FINE, "Removing row with id: {0}",
+ row.getId().getId()[0]);
+ }
if (executeUpdate(sqlGenerator.generateDeleteQuery(getTableName(),
primaryKeyColumns, versionColumn, row)) == 1) {
return true;
diff --git a/server/src/com/vaadin/server/AbstractCommunicationManager.java b/server/src/com/vaadin/server/AbstractCommunicationManager.java
index cd7279e601..17bbbda737 100644
--- a/server/src/com/vaadin/server/AbstractCommunicationManager.java
+++ b/server/src/com/vaadin/server/AbstractCommunicationManager.java
@@ -791,10 +791,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
dirtyVisibleConnectors
.addAll(getDirtyVisibleConnectors(uiConnectorTracker));
- getLogger().log(
- Level.FINE,
- "Found " + dirtyVisibleConnectors.size()
- + " dirty connectors to paint");
+ getLogger().log(Level.FINE, "Found {0} dirty connectors to paint",
+ dirtyVisibleConnectors.size());
for (ClientConnector connector : dirtyVisibleConnectors) {
boolean initialized = uiConnectorTracker
.isClientSideInitialized(connector);
@@ -1226,10 +1224,10 @@ public abstract class AbstractCommunicationManager implements Serializable {
null, stateType, uI.getConnectorTracker());
diffState = encodeResult.getEncodedValue();
} catch (Exception e) {
- getLogger().log(
- Level.WARNING,
- "Error creating reference object for state of type "
- + stateType.getName());
+ getLogger()
+ .log(Level.WARNING,
+ "Error creating reference object for state of type {0}",
+ stateType.getName());
}
}
EncodeResult encodeResult = JsonCodec.encode(state, diffState,
@@ -1276,10 +1274,10 @@ public abstract class AbstractCommunicationManager implements Serializable {
if (publishedFileContexts.containsKey(name)) {
Class<?> oldContext = publishedFileContexts.get(name);
if (oldContext != context) {
- getLogger().warning(
- name + " published by both " + context + " and "
- + oldContext + ". File from " + oldContext
- + " will be used.");
+ getLogger()
+ .log(Level.WARNING,
+ "{0} published by both {1} and {2}. File from {2} will be used.",
+ new Object[] { name, context, oldContext });
}
} else {
publishedFileContexts.put(name, context);
@@ -1312,9 +1310,13 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
sortByHierarchy((List) legacyComponents);
for (LegacyComponent c : legacyComponents) {
- getLogger().fine(
- "Painting LegacyComponent " + c.getClass().getName() + "@"
- + Integer.toHexString(c.hashCode()));
+ if (getLogger().isLoggable(Level.FINE)) {
+ getLogger().log(
+ Level.FINE,
+ "Painting LegacyComponent {0}@{1}",
+ new Object[] { c.getClass().getName(),
+ Integer.toHexString(c.hashCode()) });
+ }
paintTarget.startTag("change");
final String pid = c.getConnectorId();
paintTarget.addAttribute("pid", pid);
@@ -1705,9 +1707,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
}
}
} catch (JSONException e) {
- getLogger().warning(
- "Unable to parse RPC call from the client: "
- + e.getMessage());
+ getLogger().log(Level.WARNING,
+ "Unable to parse RPC call from the client: {0}",
+ e.getMessage());
// TODO or return success = false?
throw new RuntimeException(e);
}
@@ -1853,11 +1855,11 @@ public abstract class AbstractCommunicationManager implements Serializable {
* corresponding to the received method invocation has been
* registered.
*/
- getLogger().warning(
- "Ignoring RPC call to " + interfaceName + "." + methodName
- + " in connector " + connector.getClass().getName()
- + "(" + connectorId
- + ") as no RPC implementation is regsitered");
+ getLogger()
+ .log(Level.WARNING,
+ "Ignoring RPC call to {0}.{1} in connector {2} ({3}) as no RPC implementation is regsitered",
+ new Object[] { interfaceName, methodName,
+ connector.getClass().getName(), connectorId });
return null;
}
@@ -2056,9 +2058,8 @@ public abstract class AbstractCommunicationManager implements Serializable {
DateFormat dateFormat = DateFormat.getDateTimeInstance(
DateFormat.SHORT, DateFormat.SHORT, l);
if (!(dateFormat instanceof SimpleDateFormat)) {
- getLogger().warning(
- "Unable to get default date pattern for locale "
- + l.toString());
+ getLogger().log(Level.WARNING,
+ "Unable to get default date pattern for locale {0}", l);
dateFormat = new SimpleDateFormat();
}
final String df = ((SimpleDateFormat) dateFormat).toPattern();
@@ -2218,8 +2219,10 @@ public abstract class AbstractCommunicationManager implements Serializable {
if (id == null) {
id = nextTypeKey++;
typeToKey.put(class1, id);
- getLogger().log(Level.FINE,
- "Mapping " + class1.getName() + " to " + id);
+ if (getLogger().isLoggable(Level.FINE)) {
+ getLogger().log(Level.FINE, "Mapping {0} to {1}",
+ new Object[] { class1.getName(), id });
+ }
}
return id.toString();
}
@@ -2434,11 +2437,12 @@ public abstract class AbstractCommunicationManager implements Serializable {
reinitUI(retainedUI, request);
return retainedUI;
} else {
- getLogger().info(
- "Not using retained UI in " + windowName
- + " because retained UI was of type "
- + retainedUI.getClass() + " but " + uiClass
- + " is expected for the request.");
+ getLogger().log(
+ Level.INFO,
+ "Not using retained UI in {0} because retained UI was of type {1}"
+ + " but {2} is expected for the request.",
+ new Object[] { windowName, retainedUI.getClass(),
+ uiClass });
}
}
}
@@ -2469,9 +2473,10 @@ public abstract class AbstractCommunicationManager implements Serializable {
if (vaadinService.preserveUIOnRefresh(provider, event)) {
// Remember this UI
if (windowName == null) {
- getLogger().warning(
- "There is no window.name available for UI " + uiClass
- + " that should be preserved.");
+ getLogger()
+ .log(Level.WARNING,
+ "There is no window.name available for UI {0} that should be preserved.",
+ uiClass);
} else {
session.getPreserveOnRefreshUIs().put(windowName, uiId);
}
@@ -2523,7 +2528,7 @@ public abstract class AbstractCommunicationManager implements Serializable {
writeUidlResponse(request, true, pWriter, uI, false);
pWriter.print("}");
String initialUIDL = sWriter.toString();
- getLogger().log(Level.FINE, "Initial UIDL:" + initialUIDL);
+ getLogger().log(Level.FINE, "Initial UIDL:{0}", initialUIDL);
return initialUIDL;
}
@@ -2554,9 +2559,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
// Security check: avoid accidentally serving from the UI of the
// classpath instead of relative to the context class
if (fileName.startsWith("/")) {
- getLogger().warning(
- "Published file request starting with / rejected: "
- + fileName);
+ getLogger().log(Level.WARNING,
+ "Published file request starting with / rejected: {0}",
+ fileName);
response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName);
return;
}
@@ -2570,9 +2575,10 @@ public abstract class AbstractCommunicationManager implements Serializable {
// Security check: don't serve resource if the name hasn't been
// registered in the map
if (context == null) {
- getLogger().warning(
- "Rejecting published file request for file that has not been published: "
- + fileName);
+ getLogger()
+ .log(Level.WARNING,
+ "Rejecting published file request for file that has not been published: {0}",
+ fileName);
response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName);
return;
}
@@ -2580,12 +2586,14 @@ public abstract class AbstractCommunicationManager implements Serializable {
// Resolve file relative to the location of the context class
InputStream in = context.getResourceAsStream(fileName);
if (in == null) {
- getLogger().warning(
- fileName + " published by " + context.getName()
- + " not found. Verify that the file "
- + context.getPackage().getName().replace('.', '/')
- + '/' + fileName
- + " is available on the classpath.");
+ getLogger()
+ .log(Level.WARNING,
+ "{0} published by {1} not found. Verify that the file {2}/{3} is available on the classpath.",
+ new Object[] {
+ fileName,
+ context.getName(),
+ context.getPackage().getName()
+ .replace('.', '/'), fileName });
response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName);
return;
}
diff --git a/server/src/com/vaadin/server/DragAndDropService.java b/server/src/com/vaadin/server/DragAndDropService.java
index a3690cf040..5a54b5ae3a 100644
--- a/server/src/com/vaadin/server/DragAndDropService.java
+++ b/server/src/com/vaadin/server/DragAndDropService.java
@@ -21,6 +21,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONException;
@@ -97,9 +98,9 @@ public class DragAndDropService implements VariableOwner, ClientConnector {
DropHandler dropHandler = (dropTarget).getDropHandler();
if (dropHandler == null) {
// No dropHandler returned so no drop can be performed.
- getLogger().fine(
- "DropTarget.getDropHandler() returned null for owner: "
- + dropTarget);
+ getLogger().log(Level.FINE,
+ "DropTarget.getDropHandler() returned null for owner: {0}",
+ dropTarget);
return;
}
diff --git a/server/src/com/vaadin/server/GAEVaadinServlet.java b/server/src/com/vaadin/server/GAEVaadinServlet.java
index 140ed54828..0d2063d446 100644
--- a/server/src/com/vaadin/server/GAEVaadinServlet.java
+++ b/server/src/com/vaadin/server/GAEVaadinServlet.java
@@ -272,7 +272,8 @@ public class GAEVaadinServlet extends VaadinServlet {
ds.put(entity);
} catch (DeadlineExceededException e) {
- getLogger().warning("DeadlineExceeded for " + session.getId());
+ getLogger().log(Level.WARNING, "DeadlineExceeded for {0}",
+ session.getId());
sendDeadlineExceededNotification(request, response);
} catch (NotSerializableException e) {
getLogger().log(Level.SEVERE, "Not serializable!", e);
@@ -421,9 +422,10 @@ public class GAEVaadinServlet extends VaadinServlet {
List<Entity> entities = pq.asList(Builder
.withLimit(CLEANUP_LIMIT));
if (entities != null) {
- getLogger().info(
- "Vaadin cleanup deleting " + entities.size()
- + " expired Vaadin sessions.");
+ getLogger()
+ .log(Level.INFO,
+ "Vaadin cleanup deleting {0} expired Vaadin sessions.",
+ entities.size());
List<Key> keys = new ArrayList<Key>();
for (Entity e : entities) {
keys.add(e.getKey());
@@ -441,9 +443,10 @@ public class GAEVaadinServlet extends VaadinServlet {
List<Entity> entities = pq.asList(Builder
.withLimit(CLEANUP_LIMIT));
if (entities != null) {
- getLogger().info(
- "Vaadin cleanup deleting " + entities.size()
- + " expired appengine sessions.");
+ getLogger()
+ .log(Level.INFO,
+ "Vaadin cleanup deleting {0} expired appengine sessions.",
+ entities.size());
List<Key> keys = new ArrayList<Key>();
for (Entity e : entities) {
keys.add(e.getKey());
diff --git a/server/src/com/vaadin/server/JsonPaintTarget.java b/server/src/com/vaadin/server/JsonPaintTarget.java
index 12d2c93272..11bfb33fe1 100644
--- a/server/src/com/vaadin/server/JsonPaintTarget.java
+++ b/server/src/com/vaadin/server/JsonPaintTarget.java
@@ -25,6 +25,7 @@ import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;
+import java.util.logging.Level;
import java.util.logging.Logger;
import com.vaadin.ui.Alignment;
@@ -663,9 +664,13 @@ public class JsonPaintTarget implements PaintTarget {
throws PaintException {
boolean topLevelPaintable = openPaintables.isEmpty();
- getLogger().fine(
- "startPaintable for " + connector.getClass().getName() + "@"
- + Integer.toHexString(connector.hashCode()));
+ if (getLogger().isLoggable(Level.FINE)) {
+ getLogger().log(
+ Level.FINE,
+ "startPaintable for {0}@{1}",
+ new Object[] { connector.getClass().getName(),
+ Integer.toHexString(connector.hashCode()) });
+ }
startTag(tagName, true);
openPaintables.push(connector);
@@ -687,9 +692,13 @@ public class JsonPaintTarget implements PaintTarget {
@Override
public void endPaintable(Component paintable) throws PaintException {
- getLogger().fine(
- "endPaintable for " + paintable.getClass().getName() + "@"
- + Integer.toHexString(paintable.hashCode()));
+ if (getLogger().isLoggable(Level.FINE)) {
+ getLogger().log(
+ Level.FINE,
+ "endPaintable for {0}@{1}",
+ new Object[] { paintable.getClass().getName(),
+ Integer.toHexString(paintable.hashCode()) });
+ }
ClientConnector openPaintable = openPaintables.peek();
if (paintable != openPaintable) {
diff --git a/server/src/com/vaadin/server/ServerRpcManager.java b/server/src/com/vaadin/server/ServerRpcManager.java
index d712eafe37..ec25ce83ca 100644
--- a/server/src/com/vaadin/server/ServerRpcManager.java
+++ b/server/src/com/vaadin/server/ServerRpcManager.java
@@ -119,11 +119,9 @@ public class ServerRpcManager<T extends ServerRpc> implements Serializable {
} else {
getLogger()
.log(Level.WARNING,
- "RPC call received for RpcTarget "
- + target.getClass().getName()
- + " ("
- + invocation.getConnectorId()
- + ") but the target has not registered any RPC interfaces");
+ "RPC call received for RpcTarget {0} ({1}) but the target has not registered any RPC interfaces",
+ new Object[] { target.getClass().getName(),
+ invocation.getConnectorId() });
}
}
diff --git a/server/src/com/vaadin/server/VaadinPortlet.java b/server/src/com/vaadin/server/VaadinPortlet.java
index e13a64682e..b4a2390fa5 100644
--- a/server/src/com/vaadin/server/VaadinPortlet.java
+++ b/server/src/com/vaadin/server/VaadinPortlet.java
@@ -28,6 +28,7 @@ import java.security.GeneralSecurityException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
+import java.util.logging.Level;
import java.util.logging.Logger;
import javax.portlet.ActionRequest;
@@ -606,9 +607,8 @@ public class VaadinPortlet extends GenericPortlet implements Constants,
os.write(buffer, 0, bytes);
}
} else {
- getLogger().info(
- "Requested resource [" + resourceID
- + "] could not be found");
+ getLogger().log(Level.INFO,
+ "Requested resource [{0}] could not be found", resourceID);
response.setProperty(ResourceResponse.HTTP_STATUS_CODE,
Integer.toString(HttpServletResponse.SC_NOT_FOUND));
}
diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java
index d35cf74262..ada0fac107 100644
--- a/server/src/com/vaadin/server/VaadinService.java
+++ b/server/src/com/vaadin/server/VaadinService.java
@@ -25,6 +25,7 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
+import java.util.logging.Level;
import java.util.logging.Logger;
import javax.portlet.PortletContext;
@@ -786,9 +787,8 @@ public abstract class VaadinService implements Serializable {
if (!session.isClosing()) {
closeSession(session);
if (session.getSession() != null) {
- getLogger().fine(
- "Closing inactive session "
- + session.getSession().getId());
+ getLogger().log(Level.FINE, "Closing inactive session {0}",
+ session.getSession().getId());
}
}
if (session.getSession() != null) {
@@ -817,7 +817,8 @@ public abstract class VaadinService implements Serializable {
private void removeClosedUIs(VaadinSession session) {
for (UI ui : new ArrayList<UI>(session.getUIs())) {
if (ui.isClosing()) {
- getLogger().finer("Removing closed UI " + ui.getUIId());
+ getLogger().log(Level.FINER, "Removing closed UI {0}",
+ ui.getUIId());
session.removeUI(ui);
}
}
@@ -833,9 +834,9 @@ public abstract class VaadinService implements Serializable {
String sessionId = session.getSession().getId();
for (UI ui : session.getUIs()) {
if (!isUIActive(ui) && !ui.isClosing()) {
- getLogger().fine(
- "Closing inactive UI #" + ui.getUIId() + " in session "
- + sessionId);
+ getLogger().log(Level.FINE,
+ "Closing inactive UI #{0} in session {1}",
+ new Object[] { ui.getUIId(), sessionId });
ui.close();
}
}
diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java
index b3f9b36a83..4fde870f74 100644
--- a/server/src/com/vaadin/server/VaadinServlet.java
+++ b/server/src/com/vaadin/server/VaadinServlet.java
@@ -792,10 +792,10 @@ public class VaadinServlet extends HttpServlet implements Constants {
} else {
// cannot serve requested file
getLogger()
- .info("Requested resource ["
- + filename
- + "] not found from filesystem or through class loader."
- + " Add widgetset and/or theme JAR to your classpath or add files to WebContent/VAADIN folder.");
+ .log(Level.INFO,
+ "Requested resource [{0}] not found from filesystem or through class loader."
+ + " Add widgetset and/or theme JAR to your classpath or add files to WebContent/VAADIN folder.",
+ filename);
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
return;
@@ -805,9 +805,9 @@ public class VaadinServlet extends HttpServlet implements Constants {
// directory
if (!isAllowedVAADINResourceUrl(request, resourceUrl)) {
getLogger()
- .info("Requested resource ["
- + filename
- + "] not accessible in the VAADIN directory or access to it is forbidden.");
+ .log(Level.INFO,
+ "Requested resource [{0}] not accessible in the VAADIN directory or access to it is forbidden.",
+ filename);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
return;
}
@@ -945,9 +945,9 @@ public class VaadinServlet extends HttpServlet implements Constants {
// directory
if (!isAllowedVAADINResourceUrl(request, scssUrl)) {
getLogger()
- .info("Requested resource ["
- + filename
- + "] not accessible in the VAADIN directory or access to it is forbidden.");
+ .log(Level.INFO,
+ "Requested resource [{0}] not accessible in the VAADIN directory or access to it is forbidden.",
+ filename);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
// Handled, return true so no further processing is done
return true;
@@ -968,16 +968,14 @@ public class VaadinServlet extends HttpServlet implements Constants {
if (scss == null) {
getLogger()
- .warning(
- "Scss file "
- + scssFilename
- + " exists but ScssStylesheet was not able to find it");
+ .log(Level.WARNING,
+ "Scss file {0} exists but ScssStylesheet was not able to find it",
+ scssFilename);
return false;
}
try {
- getLogger().fine(
- "Compiling " + realFilename + " for request to "
- + filename);
+ getLogger().log(Level.FINE, "Compiling {0} for request to {1}",
+ new Object[] { realFilename, filename });
scss.compile();
} catch (Exception e) {
e.printStackTrace();
@@ -1028,14 +1026,15 @@ public class VaadinServlet extends HttpServlet implements Constants {
// loader sees it.
if (!resourceUrl.getPath().contains("!/VAADIN/")) {
- getLogger().info(
- "Blocked attempt to access a JAR entry not starting with /VAADIN/: "
- + resourceUrl);
+ getLogger()
+ .log(Level.INFO,
+ "Blocked attempt to access a JAR entry not starting with /VAADIN/: {0}",
+ resourceUrl);
return false;
}
- getLogger().fine(
- "Accepted access to a JAR entry using a class loader: "
- + resourceUrl);
+ getLogger().log(Level.FINE,
+ "Accepted access to a JAR entry using a class loader: {0}",
+ resourceUrl);
return true;
} else {
// Some servers such as GlassFish extract files from JARs (file:)
@@ -1045,13 +1044,13 @@ public class VaadinServlet extends HttpServlet implements Constants {
// "/../"
if (!resourceUrl.getPath().contains("/VAADIN/")
|| resourceUrl.getPath().contains("/../")) {
- getLogger().info(
- "Blocked attempt to access file: " + resourceUrl);
+ getLogger().log(Level.INFO,
+ "Blocked attempt to access file: {0}", resourceUrl);
return false;
}
- getLogger().fine(
- "Accepted access to a file using a class loader: "
- + resourceUrl);
+ getLogger().log(Level.FINE,
+ "Accepted access to a file using a class loader: {0}",
+ resourceUrl);
return true;
}
}
diff --git a/server/src/com/vaadin/ui/AbstractMedia.java b/server/src/com/vaadin/ui/AbstractMedia.java
index 55464aeb8d..41677467bb 100644
--- a/server/src/com/vaadin/ui/AbstractMedia.java
+++ b/server/src/com/vaadin/ui/AbstractMedia.java
@@ -19,6 +19,7 @@ package com.vaadin.ui;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -88,9 +89,9 @@ public abstract class AbstractMedia extends AbstractComponent {
int sourceIndex = Integer.parseInt(matcher.group(1));
if (sourceIndex < 0 || sourceIndex >= sources.size()) {
- getLogger().warning(
- "Requested source index " + sourceIndex
- + " is out of bounds");
+ getLogger().log(Level.WARNING,
+ "Requested source index {0} is out of bounds",
+ sourceIndex);
return false;
}
diff --git a/server/src/com/vaadin/ui/ConnectorTracker.java b/server/src/com/vaadin/ui/ConnectorTracker.java
index a888f9f7eb..a229003224 100644
--- a/server/src/com/vaadin/ui/ConnectorTracker.java
+++ b/server/src/com/vaadin/ui/ConnectorTracker.java
@@ -120,17 +120,23 @@ public class ConnectorTracker implements Serializable {
if (previouslyRegistered == null) {
connectorIdToConnector.put(connectorId, connector);
uninitializedConnectors.add(connector);
- getLogger().fine(
- "Registered " + connector.getClass().getSimpleName() + " ("
- + connectorId + ")");
+ if (getLogger().isLoggable(Level.FINE)) {
+ getLogger().log(
+ Level.FINE,
+ "Registered {0} ({1})",
+ new Object[] { connector.getClass().getSimpleName(),
+ connectorId });
+ }
} else if (previouslyRegistered != connector) {
throw new RuntimeException("A connector with id " + connectorId
+ " is already registered!");
} else if (!wasUnregistered) {
- getLogger().warning(
- "An already registered connector was registered again: "
- + connector.getClass().getSimpleName() + " ("
- + connectorId + ")");
+ getLogger()
+ .log(Level.WARNING,
+ "An already registered connector was registered again: {0} ({1})",
+ new Object[] {
+ connector.getClass().getSimpleName(),
+ connectorId });
}
dirtyConnectors.add(connector);
}
@@ -149,10 +155,11 @@ public class ConnectorTracker implements Serializable {
public void unregisterConnector(ClientConnector connector) {
String connectorId = connector.getConnectorId();
if (!connectorIdToConnector.containsKey(connectorId)) {
- getLogger().warning(
- "Tried to unregister "
- + connector.getClass().getSimpleName() + " ("
- + connectorId + ") which is not registered");
+ getLogger().log(
+ Level.WARNING,
+ "Tried to unregister {0} ({1}) which is not registered",
+ new Object[] { connector.getClass().getSimpleName(),
+ connectorId });
return;
}
if (connectorIdToConnector.get(connectorId) != connector) {
@@ -163,14 +170,19 @@ public class ConnectorTracker implements Serializable {
dirtyConnectors.remove(connector);
if (unregisteredConnectors.add(connector)) {
- getLogger().fine(
- "Unregistered " + connector.getClass().getSimpleName()
- + " (" + connectorId + ")");
+ if (getLogger().isLoggable(Level.FINE)) {
+ getLogger().log(
+ Level.FINE,
+ "Unregistered {0} ({1})",
+ new Object[] { connector.getClass().getSimpleName(),
+ connectorId });
+ }
} else {
- getLogger().warning(
- "Unregistered " + connector.getClass().getSimpleName()
- + " (" + connectorId
- + ") that was already unregistered.");
+ getLogger().log(
+ Level.WARNING,
+ "Unregistered {0} ({1}) that was already unregistered.",
+ new Object[] { connector.getClass().getSimpleName(),
+ connectorId });
}
}
@@ -273,11 +285,11 @@ public class ConnectorTracker implements Serializable {
// This code should never be called as cleanup should take place
// in detach()
+
getLogger()
- .warning(
- "cleanConnectorMap unregistered connector "
- + getConnectorAndParentInfo(connector)
- + "). This should have been done when the connector was detached.");
+ .log(Level.WARNING,
+ "cleanConnectorMap unregistered connector {0}. This should have been done when the connector was detached.",
+ getConnectorAndParentInfo(connector));
removeFromGlobalResourceHandler(connector);
uninitializedConnectors.remove(connector);
@@ -288,10 +300,12 @@ public class ConnectorTracker implements Serializable {
&& !uninitializedConnectors.contains(connector)) {
uninitializedConnectors.add(connector);
diffStates.remove(connector);
- getLogger().fine(
- "cleanConnectorMap removed state for "
- + getConnectorAndParentInfo(connector)
- + " as it is not visible");
+ if (getLogger().isLoggable(Level.FINE)) {
+ getLogger()
+ .log(Level.FINE,
+ "cleanConnectorMap removed state for {0} as it is not visible",
+ getConnectorAndParentInfo(connector));
+ }
}
}
@@ -335,9 +349,8 @@ public class ConnectorTracker implements Serializable {
if (getLogger().isLoggable(Level.FINE)) {
if (!dirtyConnectors.contains(connector)) {
- getLogger().fine(
- getConnectorAndParentInfo(connector) + " "
- + "is now dirty");
+ getLogger().log(Level.FINE, "{0} is now dirty",
+ getConnectorAndParentInfo(connector));
}
}
@@ -353,9 +366,8 @@ public class ConnectorTracker implements Serializable {
public void markClean(ClientConnector connector) {
if (getLogger().isLoggable(Level.FINE)) {
if (dirtyConnectors.contains(connector)) {
- getLogger().fine(
- getConnectorAndParentInfo(connector) + " "
- + "is no longer dirty");
+ getLogger().log(Level.FINE, "{0} is no longer dirty",
+ getConnectorAndParentInfo(connector));
}
}
diff --git a/server/src/com/vaadin/ui/Tree.java b/server/src/com/vaadin/ui/Tree.java
index 32c5712f0f..a6dbea51ba 100644
--- a/server/src/com/vaadin/ui/Tree.java
+++ b/server/src/com/vaadin/ui/Tree.java
@@ -75,7 +75,7 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
/**
* Set of expanded nodes.
*/
- private final HashSet<Object> expanded = new HashSet<Object>();
+ private HashSet<Object> expanded = new HashSet<Object>();
/**
* List of action handlers.
@@ -845,9 +845,20 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
newDataSource));
}
- // Ensure previous expanded items are cleaned up if they don't exist in
- // the new container
- cleanupExpandedItems();
+ /*
+ * Ensure previous expanded items are cleaned up if they don't exist in
+ * the new container
+ */
+ if (expanded != null) {
+ /*
+ * We need to check that the expanded-field is not null since
+ * setContainerDataSource() is called from the parent constructor
+ * (AbstractSelect()) and at that time the expanded field is not yet
+ * initialized.
+ */
+ cleanupExpandedItems();
+ }
+
}
/* Expand event and listener */
@@ -1652,7 +1663,6 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
target.addAttribute("depth", depthToCheck);
target.addAttribute("key", key(rootId));
}
-
}
/**
@@ -1677,24 +1687,16 @@ public class Tree extends AbstractSelect implements Container.Hierarchical,
return itemDescriptionGenerator;
}
- @Override
- public void containerItemSetChange(
- com.vaadin.data.Container.ItemSetChangeEvent event) {
-
- // Ensure removed items are cleaned up from expanded list
- cleanupExpandedItems();
-
- super.containerItemSetChange(event);
- }
-
private void cleanupExpandedItems() {
+ Set<Object> removedItemIds = new HashSet<Object>();
for (Object expandedItemId : expanded) {
if (getItem(expandedItemId) == null) {
- expanded.remove(expandedItemId);
+ removedItemIds.add(expandedItemId);
if (this.expandedItemId == expandedItemId) {
this.expandedItemId = null;
}
}
}
+ expanded.removeAll(removedItemIds);
}
}
diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java
index 7e54aa01a0..d12c8d89c9 100644
--- a/server/src/com/vaadin/ui/UI.java
+++ b/server/src/com/vaadin/ui/UI.java
@@ -43,6 +43,7 @@ import com.vaadin.shared.ui.ui.ScrollClientRpc;
import com.vaadin.shared.ui.ui.UIConstants;
import com.vaadin.shared.ui.ui.UIServerRpc;
import com.vaadin.shared.ui.ui.UIState;
+import com.vaadin.ui.Component.Focusable;
import com.vaadin.util.CurrentInstance;
/**
diff --git a/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java b/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java
index 0d3dea28ac..3172b759a1 100644
--- a/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java
+++ b/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java
@@ -48,6 +48,11 @@ public class TestClassesSerializable extends TestCase {
"com\\.vaadin\\.server\\.AbstractCommunicationManager.*", //
"com\\.vaadin\\.server\\.CommunicationManager.*", //
"com\\.vaadin\\.server\\.PortletCommunicationManager.*", //
+ "com\\.vaadin\\.buildhelpers.*", //
+ "com\\.vaadin\\.util\\.ReflectTools.*", //
+ "com\\.vaadin\\.data\\.util\\.ReflectTools.*", //
+ "com\\.vaadin\\.sass.*", //
+ "com\\.vaadin\\.util\\.CurrentInstance\\$1", //
};
/**
diff --git a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java
new file mode 100644
index 0000000000..68c1133dc0
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java
@@ -0,0 +1,41 @@
+package com.vaadin.tests.server.component.fieldgroup;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import com.vaadin.data.fieldgroup.BeanFieldGroup;
+
+public class BeanFieldGroupTest {
+
+ public static class MyBean {
+
+ private MyNestedBean nestedBean = new MyNestedBean();
+
+ public MyNestedBean getNestedBean() {
+ return nestedBean;
+ }
+ }
+
+ public static class MyNestedBean {
+
+ private String hello = "Hello world";
+
+ public String getHello() {
+ return hello;
+ }
+ }
+
+ @Test
+ public void buildAndBindNestedProperty() {
+
+ MyBean bean = new MyBean();
+
+ BeanFieldGroup<MyBean> bfg = new BeanFieldGroup<MyBean>(MyBean.class);
+ bfg.setItemDataSource(bean);
+
+ com.vaadin.ui.Field<?> helloField = bfg.buildAndBind("Hello string",
+ "nestedBean.hello");
+ assertEquals(bean.nestedBean.hello, helloField.getValue().toString());
+ }
+
+}
diff --git a/theme-compiler/src/com/vaadin/sass/SassCompiler.java b/theme-compiler/src/com/vaadin/sass/SassCompiler.java
index 840badfc25..48b2d24c46 100644
--- a/theme-compiler/src/com/vaadin/sass/SassCompiler.java
+++ b/theme-compiler/src/com/vaadin/sass/SassCompiler.java
@@ -17,6 +17,7 @@
package com.vaadin.sass;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
@@ -48,6 +49,12 @@ public class SassCompiler {
// ScssStylesheet.setStylesheetResolvers(new VaadinResolver());
ScssStylesheet scss = ScssStylesheet.get(input);
+ if(scss == null){
+ System.err.println("The scss file " + input
+ + " could not be found.");
+ return;
+ }
+
scss.compile();
if (output == null) {
System.out.println(scss.toString());
diff --git a/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java b/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java
index 64279ad1e7..dbb3e571dc 100644
--- a/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java
+++ b/theme-compiler/src/com/vaadin/sass/internal/ScssStylesheet.java
@@ -42,6 +42,7 @@ import com.vaadin.sass.internal.tree.MixinDefNode;
import com.vaadin.sass.internal.tree.Node;
import com.vaadin.sass.internal.tree.VariableNode;
import com.vaadin.sass.internal.tree.controldirective.IfElseDefNode;
+import com.vaadin.sass.internal.visitor.ExtendNodeHandler;
import com.vaadin.sass.internal.visitor.ImportNodeHandler;
public class ScssStylesheet extends Node {
@@ -77,7 +78,8 @@ public class ScssStylesheet extends Node {
* ScssStylesheet tree out of it. Calling compile() on it will transform
* SASS into CSS. Calling toString() will print out the SCSS/CSS.
*
- * @param file
+ * @param identifier
+ * The file path. If null then null is returned.
* @return
* @throws CSSException
* @throws IOException
@@ -92,7 +94,8 @@ public class ScssStylesheet extends Node {
* builds up a ScssStylesheet tree out of it. Calling compile() on it will
* transform SASS into CSS. Calling toString() will print out the SCSS/CSS.
*
- * @param file
+ * @param identifier
+ * The file path. If null then null is returned.
* @param encoding
* @return
* @throws CSSException
@@ -108,6 +111,12 @@ public class ScssStylesheet extends Node {
*
* @charset declaration, the default one is ASCII.
*/
+
+ if (identifier == null) {
+ return null;
+ }
+
+ // FIXME Is this actually intended? /John 1.3.2013
File file = new File(identifier);
file = file.getCanonicalFile();
@@ -172,6 +181,7 @@ public class ScssStylesheet extends Node {
variables.clear();
ifElseDefNodes.clear();
lastNodeAdded.clear();
+ ExtendNodeHandler.clear();
importOtherFiles(this);
populateDefinitions(this);
traverse(this);
diff --git a/theme-compiler/src/com/vaadin/sass/internal/util/StringUtil.java b/theme-compiler/src/com/vaadin/sass/internal/util/StringUtil.java
index cf227fe3a3..b20e8bab61 100644
--- a/theme-compiler/src/com/vaadin/sass/internal/util/StringUtil.java
+++ b/theme-compiler/src/com/vaadin/sass/internal/util/StringUtil.java
@@ -17,8 +17,10 @@
package com.vaadin.sass.internal.util;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
@@ -145,14 +147,7 @@ public class StringUtil {
* @return true if the text contains the SCSS variable, false if not
*/
public static boolean containsVariable(String text, String varName) {
- StringBuilder builder = new StringBuilder();
- // (?![\\w-]) means lookahead, the next one shouldn't be a word
- // character nor a dash.
- builder.append("\\$").append(Pattern.quote(varName))
- .append("(?![\\w-])");
- Pattern pattern = Pattern.compile(builder.toString());
- Matcher matcher = pattern.matcher(text);
- return matcher.find();
+ return containsSubString(text, "$" + varName);
}
/**
@@ -162,18 +157,81 @@ public class StringUtil {
* @param text
* text which contains the SCSS variable
* @param varName
- * SCSS variable name
+ * SCSS variable name (Without '$' sign)
* @param value
* the value of the SCSS variable
* @return the String after replacing
*/
public static String replaceVariable(String text, String varName,
String value) {
+ return replaceSubString(text, "$" + varName, value);
+ }
+
+ /**
+ * Check if a String contains a sub string, using whole word match.
+ *
+ * @param text
+ * text to be checked
+ * @Param sub Sub String to be checked.
+ * @return true if the text contains the sub string, false if not
+ */
+ public static boolean containsSubString(String text, String sub) {
StringBuilder builder = new StringBuilder();
// (?![\\w-]) means lookahead, the next one shouldn't be a word
// character nor a dash.
- builder.append("\\$").append(Pattern.quote(varName))
+ builder.append("(?<![\\w-])").append(Pattern.quote(sub))
+ .append("(?![\\w-])");
+ Pattern pattern = Pattern.compile(builder.toString());
+ Matcher matcher = pattern.matcher(text);
+ return matcher.find();
+ }
+
+ /**
+ * Replace the sub string in a String to a value, using whole word match.
+ *
+ * @param text
+ * text which contains the sub string
+ * @param sub
+ * the sub string
+ * @param value
+ * the new value
+ * @return the String after replacing
+ */
+ public static String replaceSubString(String text, String sub, String value) {
+ StringBuilder builder = new StringBuilder();
+ // (?![\\w-]) means lookahead, the next one shouldn't be a word
+ // character nor a dash.
+ builder.append("(?<![\\w-])").append(Pattern.quote(sub))
.append("(?![\\w-])");
return text.replaceAll(builder.toString(), value);
}
+
+ /**
+ * Remove duplicated sub string in a String given a splitter. Can be used to
+ * removed duplicated selectors, e.g., in ".error.error", one duplicated
+ * ".error" can be removed.
+ *
+ * @param motherString
+ * string which may contains duplicated sub strings
+ * @param splitter
+ * the splitter splits the mother string to sub strings
+ * @return the mother string with duplicated sub strings removed
+ */
+ public static String removeDuplicatedSubString(String motherString,
+ String splitter) {
+ List<String> subStrings = Arrays.asList(motherString.split(Pattern
+ .quote(splitter)));
+ LinkedHashSet<String> uniqueSubStrings = new LinkedHashSet<String>(
+ subStrings);
+ StringBuilder builder = new StringBuilder();
+ int count = 0;
+ for (String uniqueSubString : uniqueSubStrings) {
+ count++;
+ builder.append(uniqueSubString);
+ if (count < uniqueSubStrings.size()) {
+ builder.append(splitter);
+ }
+ }
+ return builder.toString();
+ }
}
diff --git a/theme-compiler/src/com/vaadin/sass/internal/visitor/ExtendNodeHandler.java b/theme-compiler/src/com/vaadin/sass/internal/visitor/ExtendNodeHandler.java
index f7917fff6e..e4a69ea5f3 100644
--- a/theme-compiler/src/com/vaadin/sass/internal/visitor/ExtendNodeHandler.java
+++ b/theme-compiler/src/com/vaadin/sass/internal/visitor/ExtendNodeHandler.java
@@ -26,6 +26,7 @@ import com.vaadin.sass.internal.ScssStylesheet;
import com.vaadin.sass.internal.tree.BlockNode;
import com.vaadin.sass.internal.tree.ExtendNode;
import com.vaadin.sass.internal.tree.Node;
+import com.vaadin.sass.internal.util.StringUtil;
public class ExtendNodeHandler {
private static Map<String, List<ArrayList<String>>> extendsMap = new HashMap<String, List<ArrayList<String>>>();
@@ -35,6 +36,12 @@ public class ExtendNodeHandler {
modifyTree(ScssStylesheet.get());
}
+ public static void clear() {
+ if (extendsMap != null) {
+ extendsMap.clear();
+ }
+ }
+
private static void modifyTree(Node node) throws Exception {
for (Node child : node.getChildren()) {
if (child instanceof BlockNode) {
@@ -51,7 +58,8 @@ public class ExtendNodeHandler {
} else {
for (Entry<String, List<ArrayList<String>>> entry : extendsMap
.entrySet()) {
- if (selectorString.contains(entry.getKey())) {
+ if (StringUtil.containsSubString(selectorString,
+ entry.getKey())) {
for (ArrayList<String> sList : entry.getValue()) {
ArrayList<String> clone = (ArrayList<String>) sList
.clone();
@@ -71,22 +79,36 @@ public class ExtendNodeHandler {
if (extendsMap.get(extendedString) == null) {
extendsMap.put(extendedString, new ArrayList<ArrayList<String>>());
}
- extendsMap.get(extendedString).add(
- ((BlockNode) node.getParentNode()).getSelectorList());
+ // prevent a selector extends itself, e.g. .test{ @extend .test}
+ String parentSelectorString = ((BlockNode) node.getParentNode())
+ .getSelectors();
+ if (!parentSelectorString.equals(extendedString)) {
+ extendsMap.get(extendedString).add(
+ ((BlockNode) node.getParentNode()).getSelectorList());
+ }
}
private static void addAdditionalSelectorListToBlockNode(
- BlockNode blockNode, ArrayList<String> list, String selectorString) {
- if (list != null) {
- for (int i = 0; i < list.size(); i++) {
- if (selectorString == null) {
- blockNode.getSelectorList().add(list.get(i));
+ BlockNode blockNode, ArrayList<String> extendingSelectors,
+ String extendedSelector) {
+ if (extendingSelectors != null) {
+ for (String extendingSelector : extendingSelectors) {
+ if (extendedSelector == null) {
+ blockNode.getSelectorList().add(extendingSelector);
} else {
ArrayList<String> newTags = new ArrayList<String>();
- for (final String existing : blockNode.getSelectorList()) {
- if (existing.contains(selectorString)) {
- newTags.add(existing.replace(selectorString,
- list.get(i)));
+ for (final String selectorString : blockNode
+ .getSelectorList()) {
+ if (StringUtil.containsSubString(selectorString,
+ extendedSelector)) {
+ String newTag = generateExtendingSelectors(
+ selectorString, extendedSelector,
+ extendingSelector);
+ // prevent adding duplicated selector list
+ if (!blockNode.getSelectorList().contains(newTag)
+ && !newTags.contains(newTag)) {
+ newTags.add(newTag);
+ }
}
}
blockNode.getSelectorList().addAll(newTags);
@@ -94,4 +116,15 @@ public class ExtendNodeHandler {
}
}
}
+
+ private static String generateExtendingSelectors(String selectorString,
+ String extendedSelector, String extendingSelector) {
+ String result = StringUtil.replaceSubString(selectorString,
+ extendedSelector, extendingSelector);
+ // remove duplicated class selectors.
+ if (result.startsWith(".")) {
+ result = StringUtil.removeDuplicatedSubString(result, ".");
+ }
+ return result;
+ }
}
diff --git a/theme-compiler/tests/resources/automatic/css/extend-in-nested-block.css b/theme-compiler/tests/resources/automatic/css/extend-in-nested-block.css
new file mode 100644
index 0000000000..29f1550dd7
--- /dev/null
+++ b/theme-compiler/tests/resources/automatic/css/extend-in-nested-block.css
@@ -0,0 +1,7 @@
+.test .error, .test .seriousError {
+ border: 1px #f00;
+ background-color: #fdd;
+}
+.test .seriousError {
+ border-width: 3px;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/automatic/css/extend-selector-in-different-levels.css b/theme-compiler/tests/resources/automatic/css/extend-selector-in-different-levels.css
new file mode 100644
index 0000000000..4de05d8d82
--- /dev/null
+++ b/theme-compiler/tests/resources/automatic/css/extend-selector-in-different-levels.css
@@ -0,0 +1,15 @@
+.test .middle .error, .test .middle .seriousError {
+ border: 1px #f00;
+ background-color: #fdd;
+}
+.test .seriousError {
+ border-width: 3px;
+}
+
+.test1 .error1, .test1 .middle1 .seriousError1 {
+ border: 1px #f00;
+ background-color: #fdd;
+}
+.test1 .middle1 .seriousError1 {
+ border-width: 3px;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/automatic/css/extending-non-exist-selector-with-same-beginning.css b/theme-compiler/tests/resources/automatic/css/extending-non-exist-selector-with-same-beginning.css
new file mode 100644
index 0000000000..d138a79e4a
--- /dev/null
+++ b/theme-compiler/tests/resources/automatic/css/extending-non-exist-selector-with-same-beginning.css
@@ -0,0 +1,7 @@
+.test1 {
+ color: blue;
+}
+
+.test2 {
+ background: red;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/automatic/css/extending-same-selector.css b/theme-compiler/tests/resources/automatic/css/extending-same-selector.css
new file mode 100644
index 0000000000..1a85c0c23e
--- /dev/null
+++ b/theme-compiler/tests/resources/automatic/css/extending-same-selector.css
@@ -0,0 +1,7 @@
+.test {
+ color: blue;
+}
+
+.test {
+ background: red;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/automatic/css/extending-selector-with-same-beginning.css b/theme-compiler/tests/resources/automatic/css/extending-selector-with-same-beginning.css
new file mode 100644
index 0000000000..097d7a8655
--- /dev/null
+++ b/theme-compiler/tests/resources/automatic/css/extending-selector-with-same-beginning.css
@@ -0,0 +1,7 @@
+.test1, .test2 {
+ color: blue;
+}
+
+.test2 {
+ background: red;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/automatic/scss/extend-in-nested-block.scss b/theme-compiler/tests/resources/automatic/scss/extend-in-nested-block.scss
new file mode 100644
index 0000000000..d62ead937e
--- /dev/null
+++ b/theme-compiler/tests/resources/automatic/scss/extend-in-nested-block.scss
@@ -0,0 +1,11 @@
+.test{
+ .error {
+ border: 1px #f00;
+ background-color: #fdd;
+ }
+
+ .seriousError {
+ @extend .error;
+ border-width: 3px;
+ }
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/automatic/scss/extend-selector-in-different-levels.scss b/theme-compiler/tests/resources/automatic/scss/extend-selector-in-different-levels.scss
new file mode 100644
index 0000000000..977ead8d62
--- /dev/null
+++ b/theme-compiler/tests/resources/automatic/scss/extend-selector-in-different-levels.scss
@@ -0,0 +1,26 @@
+.test{
+ .middle{
+ .error {
+ border: 1px #f00;
+ background-color: #fdd;
+ }
+ }
+
+ .seriousError {
+ @extend .error;
+ border-width: 3px;
+ }
+}
+
+.test1{
+ .error1 {
+ border: 1px #f00;
+ background-color: #fdd;
+ }
+ .middle1{
+ .seriousError1 {
+ @extend .error1;
+ border-width: 3px;
+ }
+ }
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/automatic/scss/extending-non-exist-selector-with-same-beginning.scss b/theme-compiler/tests/resources/automatic/scss/extending-non-exist-selector-with-same-beginning.scss
new file mode 100644
index 0000000000..538f17da1d
--- /dev/null
+++ b/theme-compiler/tests/resources/automatic/scss/extending-non-exist-selector-with-same-beginning.scss
@@ -0,0 +1,8 @@
+.test1 {
+ color: blue;
+}
+
+.test2 {
+ @extend .test;
+ background: red;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/automatic/scss/extending-same-selector.scss b/theme-compiler/tests/resources/automatic/scss/extending-same-selector.scss
new file mode 100644
index 0000000000..fbfaed9b20
--- /dev/null
+++ b/theme-compiler/tests/resources/automatic/scss/extending-same-selector.scss
@@ -0,0 +1,8 @@
+.test {
+ color: blue;
+}
+
+.test {
+ @extend .test;
+ background: red;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/automatic/scss/extending-selector-with-same-beginning.scss b/theme-compiler/tests/resources/automatic/scss/extending-selector-with-same-beginning.scss
new file mode 100644
index 0000000000..c7a9e5e921
--- /dev/null
+++ b/theme-compiler/tests/resources/automatic/scss/extending-selector-with-same-beginning.scss
@@ -0,0 +1,8 @@
+.test1 {
+ color: blue;
+}
+
+.test2 {
+ @extend .test1;
+ background: red;
+} \ No newline at end of file
diff --git a/theme-compiler/tests/resources/sasslangbroken/css/19-test_control_flow_if.css b/theme-compiler/tests/resources/sasslang/css/19-test_control_flow_if.css
index 14a1c6ef3c..14a1c6ef3c 100644
--- a/theme-compiler/tests/resources/sasslangbroken/css/19-test_control_flow_if.css
+++ b/theme-compiler/tests/resources/sasslang/css/19-test_control_flow_if.css
diff --git a/theme-compiler/tests/resources/sasslangbroken/scss/19-test_control_flow_if.scss b/theme-compiler/tests/resources/sasslang/scss/19-test_control_flow_if.scss
index be53e52341..be53e52341 100644
--- a/theme-compiler/tests/resources/sasslangbroken/scss/19-test_control_flow_if.scss
+++ b/theme-compiler/tests/resources/sasslang/scss/19-test_control_flow_if.scss
diff --git a/theme-compiler/tests/src/com/vaadin/sass/internal/util/StringUtilTest.java b/theme-compiler/tests/src/com/vaadin/sass/internal/util/StringUtilTest.java
index b05b0e9dcf..84d189d8ba 100644
--- a/theme-compiler/tests/src/com/vaadin/sass/internal/util/StringUtilTest.java
+++ b/theme-compiler/tests/src/com/vaadin/sass/internal/util/StringUtilTest.java
@@ -50,4 +50,62 @@ public class StringUtilTest {
Assert.assertEquals(sentence,
StringUtil.replaceVariable(sentence, word, value));
}
+
+ @Test
+ public void testContainsSubString() {
+ String sentence = "var1 var2";
+ String word = "var";
+ Assert.assertFalse(StringUtil.containsSubString(sentence, word));
+
+ word = "var1";
+ Assert.assertTrue(StringUtil.containsSubString(sentence, word));
+
+ String var2 = "var2";
+ Assert.assertTrue(StringUtil.containsSubString(sentence, var2));
+
+ Assert.assertTrue(StringUtil.containsSubString(".error.intrusion",
+ ".error"));
+
+ Assert.assertFalse(StringUtil.containsSubString(".foo", "oo"));
+ }
+
+ @Test
+ public void testContainsSubStringWithDash() {
+ String sentence = "var- var2";
+ String word = "var";
+ Assert.assertFalse(StringUtil.containsSubString(sentence, word));
+ }
+
+ @Test
+ public void testReplaceSubString() {
+ String sentence = "var1 var2";
+ String word = "var";
+ String value = "abc";
+
+ word = "var1";
+ Assert.assertEquals("abc var2",
+ StringUtil.replaceSubString(sentence, word, value));
+
+ String var2 = "var1 abc";
+ Assert.assertEquals(sentence,
+ StringUtil.replaceSubString(sentence, var2, value));
+
+ Assert.assertEquals(".foo",
+ StringUtil.replaceSubString(".foo", "oo", "aa"));
+ }
+
+ @Test
+ public void testReplaceSubStringWithDash() {
+ String sentence = "var- var2";
+ String word = "var";
+ String value = "abc";
+ Assert.assertEquals(sentence,
+ StringUtil.replaceSubString(sentence, word, value));
+ }
+
+ @Test
+ public void testRemoveDuplicatedClassSelector() {
+ Assert.assertEquals(".seriousError", StringUtil
+ .removeDuplicatedSubString(".seriousError.seriousError", "."));
+ }
}
diff --git a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AbstractDirectoryScanningSassTests.java b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AbstractDirectoryScanningSassTests.java
index f990647978..47657f805c 100644
--- a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AbstractDirectoryScanningSassTests.java
+++ b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/AbstractDirectoryScanningSassTests.java
@@ -27,15 +27,13 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import junit.framework.TestCase;
-
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import com.vaadin.sass.internal.ScssStylesheet;
import com.vaadin.sass.testcases.scss.SassTestRunner.FactoryTest;
-public abstract class AbstractDirectoryScanningSassTests extends TestCase {
+public abstract class AbstractDirectoryScanningSassTests {
public static Collection<String> getScssResourceNames(URL directoryUrl)
throws URISyntaxException {
@@ -51,7 +49,7 @@ public abstract class AbstractDirectoryScanningSassTests extends TestCase {
URL sasslangUrl = directoryUrl;
File sasslangDir = new File(sasslangUrl.toURI());
File scssDir = new File(sasslangDir, "scss");
- assertTrue(scssDir.exists());
+ Assert.assertTrue(scssDir.exists());
return scssDir.listFiles(new FilenameFilter() {
diff --git a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTests.java b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTests.java
index 7f42898fe5..4b8aada524 100644
--- a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTests.java
+++ b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTests.java
@@ -32,7 +32,12 @@ public class SassLangTests extends AbstractDirectoryScanningSassTests {
}
private static URL getResourceURLInternal(String path) {
- return SassLangTests.class.getResource("/sasslang" + path);
+ URL url = SassLangTests.class.getResource("/sasslang" + path);
+ if (url == null) {
+ throw new RuntimeException(
+ "Could not locate /sasslang using classloader");
+ }
+ return url;
}
@TestFactory
diff --git a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTestsBroken.java b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTestsBroken.java
index 897e8dc543..6b812a6940 100644
--- a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTestsBroken.java
+++ b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassLangTestsBroken.java
@@ -19,7 +19,9 @@ import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
+import org.junit.Assert;
import org.junit.runner.RunWith;
+import org.w3c.css.sac.CSSException;
import com.vaadin.sass.testcases.scss.SassTestRunner.TestFactory;
@@ -32,7 +34,13 @@ public class SassLangTestsBroken extends AbstractDirectoryScanningSassTests {
}
private static URL getResourceURLInternal(String path) {
- return SassLangTestsBroken.class.getResource("/sasslangbroken" + path);
+ URL url = SassLangTestsBroken.class.getResource("/sasslangbroken"
+ + path);
+ if (url == null) {
+ throw new RuntimeException(
+ "Could not locate /sasslangbroken using classloader");
+ }
+ return url;
}
@TestFactory
@@ -41,4 +49,22 @@ public class SassLangTestsBroken extends AbstractDirectoryScanningSassTests {
return getScssResourceNames(getResourceURLInternal(""));
}
+ @Override
+ public void compareScssWithCss(String scssResourceName) throws Exception {
+ boolean success = false;
+ try {
+ super.compareScssWithCss(scssResourceName);
+ success = true;
+ } catch (CSSException e) {
+ // this is an expected outcome
+ } catch (AssertionError e) {
+ // this is an expected outcome
+ }
+ if (success) {
+ Assert.fail("Test "
+ + scssResourceName
+ + " from sasslangbroken that was expected to fail has been fixed. Please move the corresponding CSS and SCSS files to sasslang.");
+ }
+ }
+
}
diff --git a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassTestRunner.java b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassTestRunner.java
index da5210b2da..147362e4c7 100644
--- a/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassTestRunner.java
+++ b/theme-compiler/tests/src/com/vaadin/sass/testcases/scss/SassTestRunner.java
@@ -80,7 +80,7 @@ public class SassTestRunner extends BlockJUnit4ClassRunner {
getTestClass().getJavaClass());
} catch (Throwable t) {
throw new RuntimeException("Could not run test factory method "
- + method.getName());
+ + method.getName(), t);
}
// Did the factory return an array? If so, make it a list.
diff --git a/uitest/src/com/vaadin/tests/widgetset/client/DelegateWidget.java b/uitest/src/com/vaadin/tests/widgetset/client/DelegateWidget.java
index 0543bdf073..8b71e2bdec 100644
--- a/uitest/src/com/vaadin/tests/widgetset/client/DelegateWidget.java
+++ b/uitest/src/com/vaadin/tests/widgetset/client/DelegateWidget.java
@@ -41,6 +41,7 @@ public class DelegateWidget extends HTML {
public void setValue4(double value4) {
this.value4 = value4;
+ updateText();
}
private void updateText() {