001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.acacia.client; 029 030import org.opencms.acacia.shared.CmsContentDefinition; 031import org.opencms.acacia.shared.CmsEntity; 032import org.opencms.acacia.shared.CmsValidationResult; 033import org.opencms.acacia.shared.rpc.I_CmsContentServiceAsync; 034import org.opencms.gwt.client.ui.CmsTabbedPanel; 035import org.opencms.util.CmsPair; 036 037import java.util.Collection; 038import java.util.HashSet; 039import java.util.Map.Entry; 040import java.util.Set; 041 042import com.google.gwt.event.logical.shared.HasValueChangeHandlers; 043import com.google.gwt.event.logical.shared.ValueChangeEvent; 044import com.google.gwt.event.logical.shared.ValueChangeHandler; 045import com.google.gwt.event.shared.EventHandler; 046import com.google.gwt.event.shared.GwtEvent; 047import com.google.gwt.event.shared.HandlerRegistration; 048import com.google.gwt.event.shared.SimpleEventBus; 049import com.google.gwt.user.client.Timer; 050import com.google.gwt.user.client.rpc.AsyncCallback; 051 052/** 053 * Validation handler.<p> 054 */ 055public final class CmsValidationHandler 056implements ValueChangeHandler<CmsEntity>, HasValueChangeHandlers<CmsValidationContext> { 057 058 /** 059 * The validation timer.<p> 060 */ 061 protected class ValidationTimer extends Timer { 062 063 /** The entity to validate. */ 064 private CmsEntity m_entity; 065 066 /** 067 * Constructor.<p> 068 * 069 * @param entity the entity to validate 070 */ 071 protected ValidationTimer(CmsEntity entity) { 072 073 m_entity = entity; 074 } 075 076 /** 077 * @see com.google.gwt.user.client.Timer#run() 078 */ 079 @Override 080 public void run() { 081 082 validate(m_entity); 083 m_validationTimer = null; 084 } 085 } 086 087 /** Flag indicating the a validation call is running. */ 088 boolean m_validating; 089 090 /** The current validation timer instance. */ 091 Timer m_validationTimer; 092 093 /** The content service use for validation. */ 094 private I_CmsContentServiceAsync m_contentService; 095 096 /** The event bus. */ 097 private SimpleEventBus m_eventBus; 098 099 /** The forms tabbed panel. */ 100 private CmsTabbedPanel<?> m_formTabPanel; 101 102 /** The handler registration. */ 103 private HandlerRegistration m_handlerRegistration; 104 105 /** Indicates validation is paused. */ 106 private boolean m_paused; 107 108 /** The root attribute handler. */ 109 private CmsRootHandler m_rootHandler; 110 111 /** The validation context. */ 112 private CmsValidationContext m_validationContext; 113 114 /** Paths (without indexes) for which values are synchronized over the different locales. E.g. Availability/ReleaseDate */ 115 private Set<String> m_synchronizedPaths; 116 117 /** 118 * Clears validation message for an attribute handler.<p> 119 * 120 * @param handler the handler for which to clear the validation message 121 */ 122 public static void clearValidation(I_CmsAttributeHandler handler) { 123 124 if (handler instanceof CmsAttributeHandler) { 125 ((CmsAttributeHandler)handler).removeValidationMessages(); 126 } 127 128 } 129 130 /** 131 * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler) 132 */ 133 public HandlerRegistration addValueChangeHandler(ValueChangeHandler<CmsValidationContext> handler) { 134 135 return addHandler(handler, ValueChangeEvent.getType()); 136 } 137 138 /** 139 * Destroys the current handler instance.<p> 140 */ 141 public void clear() { 142 143 if (m_handlerRegistration != null) { 144 m_handlerRegistration.removeHandler(); 145 m_handlerRegistration = null; 146 } 147 m_validationContext = null; 148 m_paused = false; 149 m_validating = false; 150 if (m_validationTimer != null) { 151 m_validationTimer.cancel(); 152 m_validationTimer = null; 153 } 154 } 155 156 /** 157 * Displays the given error messages within the form.<p> 158 * 159 * @param entityId the entity id 160 * @param validationResult the validationResult 161 */ 162 public void displayValidation(String entityId, CmsValidationResult validationResult) { 163 164 if (m_formTabPanel != null) { 165 CmsAttributeHandler.clearErrorStyles(m_formTabPanel); 166 } 167 m_rootHandler.visit(CmsValidationHandler::clearValidation); 168 if (validationResult.hasWarnings(entityId)) { 169 for (Entry<String[], CmsPair<String, String>> warning : validationResult.getWarnings(entityId).entrySet()) { 170 String[] pathElements = warning.getKey(); 171 // check if there are no errors for this attribute 172 if (!validationResult.hasErrors(entityId) 173 || !validationResult.getErrors(entityId).containsKey(pathElements)) { 174 CmsAttributeHandler handler = m_rootHandler.getHandlerByPath(pathElements); 175 if (handler != null) { 176 String attributeName = pathElements[pathElements.length - 1]; 177 handler.setWarningMessage( 178 CmsContentDefinition.extractIndex(attributeName), 179 warning.getValue().getFirst(), 180 m_formTabPanel); 181 } 182 } 183 } 184 m_validationContext.setWarningEntity(entityId, validationResult.getWarnings(entityId)); 185 } else { 186 m_validationContext.clearWarningEntity(entityId); 187 } 188 if (validationResult.hasErrors(entityId)) { 189 for (Entry<String[], CmsPair<String, String>> error : validationResult.getErrors(entityId).entrySet()) { 190 String[] pathElements = error.getKey(); 191 CmsAttributeHandler handler = m_rootHandler.getHandlerByPath(pathElements); 192 if (handler != null) { 193 String attributeName = pathElements[pathElements.length - 1]; 194 handler.setErrorMessage( 195 CmsContentDefinition.extractIndex(attributeName), 196 error.getValue().getFirst(), 197 m_formTabPanel); 198 } 199 } 200 m_validationContext.setInvalidEntity(entityId, validationResult.getErrors(entityId)); 201 } else { 202 m_validationContext.setValidEntity(entityId); 203 } 204 ValueChangeEvent.fire(this, m_validationContext); 205 m_validating = false; 206 } 207 208 /** 209 * @see com.google.gwt.event.shared.HasHandlers#fireEvent(com.google.gwt.event.shared.GwtEvent) 210 */ 211 public void fireEvent(GwtEvent<?> event) { 212 213 ensureHandlers().fireEventFromSource(event, this); 214 } 215 216 /** 217 * Returns the validation context.<p> 218 * 219 * @return the validation context 220 */ 221 public CmsValidationContext getValidationContext() { 222 223 return m_validationContext; 224 } 225 226 /** 227 * @see com.google.gwt.event.logical.shared.ValueChangeHandler#onValueChange(com.google.gwt.event.logical.shared.ValueChangeEvent) 228 */ 229 public void onValueChange(final ValueChangeEvent<CmsEntity> event) { 230 231 if (!m_paused) { 232 if (m_validationTimer != null) { 233 m_validationTimer.cancel(); 234 } 235 m_validationTimer = new ValidationTimer(event.getValue()); 236 m_validationTimer.schedule(300); 237 } 238 } 239 240 /** 241 * Registers the validation handler for the given entity.<p> 242 * 243 * @param entity the entity 244 */ 245 public void registerEntity(CmsEntity entity) { 246 247 if (m_validationContext == null) { 248 m_validationContext = new CmsValidationContext(); 249 } 250 if (m_handlerRegistration != null) { 251 m_handlerRegistration.removeHandler(); 252 } 253 m_paused = false; 254 m_handlerRegistration = entity.addValueChangeHandler(this); 255 } 256 257 /** 258 * Sets the content service used for validation.<p> 259 * 260 * @param contentService the content service 261 */ 262 public void setContentService(I_CmsContentServiceAsync contentService) { 263 264 m_contentService = contentService; 265 } 266 267 /** 268 * Sets the form tabbed panel.<p> 269 * 270 * @param tabPanel the tabbed panel 271 */ 272 public void setFormTabPanel(CmsTabbedPanel<?> tabPanel) { 273 274 m_formTabPanel = tabPanel; 275 } 276 277 /** 278 * Sets the validation to pause.<p> 279 * 280 * @param paused <code>true</code> to pause the validation 281 * @param entity the entity will be revalidated when setting paused to <code>false</code> 282 */ 283 public void setPaused(boolean paused, CmsEntity entity) { 284 285 if (paused != m_paused) { 286 m_paused = paused; 287 if (m_paused) { 288 if (m_validationTimer != null) { 289 m_validationTimer.cancel(); 290 m_validationTimer = null; 291 } 292 } else { 293 m_validationTimer = new ValidationTimer(entity); 294 m_validationTimer.schedule(300); 295 } 296 297 } 298 } 299 300 /** 301 * Sets the root attribute handler.<p> 302 * 303 * @param rootHandler the root attribute handler 304 */ 305 public void setRootHandler(CmsRootHandler rootHandler) { 306 307 m_rootHandler = rootHandler; 308 } 309 310 public void setSynchronizedValues(Collection<String> synchronizedValues) { 311 312 if (null != synchronizedValues) { 313 m_synchronizedPaths = new HashSet<>(synchronizedValues.size()); 314 for (String sval : synchronizedValues) { 315 String path = ""; 316 boolean isNested = true; 317 while (isNested) { 318 int slashIdx = sval.indexOf('/'); 319 isNested = slashIdx == 0; 320 if (isNested) { 321 sval = sval.substring(1); 322 slashIdx = sval.indexOf('/'); 323 int colonIdx = sval.indexOf(':'); 324 int idx = colonIdx < slashIdx ? colonIdx : slashIdx; 325 path += sval.substring(0, idx) + "/"; 326 sval = sval.substring(idx); 327 } else { 328 path += CmsContentDefinition.removeIndex(sval.substring(sval.lastIndexOf('/') + 1)); 329 } 330 } 331 m_synchronizedPaths.add(path); 332 } 333 } 334 } 335 336 /** 337 * Update the validation context, i.e., replace it with the one generated from the validation result. 338 * @param validationResult the result to update the context for. 339 */ 340 public void updateValidationContext(final CmsValidationResult validationResult) { 341 342 m_validationContext = new CmsValidationContext(validationResult, m_synchronizedPaths); 343 } 344 345 /** 346 * Adds this handler to the widget. 347 * 348 * @param <H> the type of handler to add 349 * @param type the event type 350 * @param handler the handler 351 * @return {@link HandlerRegistration} used to remove the handler 352 */ 353 protected <H extends EventHandler> HandlerRegistration addHandler(final H handler, GwtEvent.Type<H> type) { 354 355 return ensureHandlers().addHandlerToSource(type, this, handler); 356 } 357 358 /** 359 * Validates the given entity.<p> 360 * 361 * @param entity the entity 362 */ 363 protected void validate(final CmsEntity entity) { 364 365 if (!m_validating) { 366 m_validating = true; 367 m_contentService.validateEntity(entity, new AsyncCallback<CmsValidationResult>() { 368 369 public void onFailure(Throwable caught) { 370 371 // can be ignored 372 } 373 374 public void onSuccess(CmsValidationResult result) { 375 376 displayValidation(entity.getId(), result); 377 } 378 }); 379 } 380 } 381 382 /** 383 * Lazy initializing the handler manager.<p> 384 * 385 * @return the handler manager 386 */ 387 private SimpleEventBus ensureHandlers() { 388 389 if (m_eventBus == null) { 390 m_eventBus = new SimpleEventBus(); 391 } 392 return m_eventBus; 393 } 394}