/*
* Copyright 2012 Shared Learning Collaborative, LLC
*
* 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 org.slc.sli.dashboard.entity;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.validation.Valid;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.apache.commons.lang3.ArrayUtils;
import org.slc.sli.dashboard.util.DashboardException;
import org.slc.sli.dashboard.web.util.NoBadChars;
/**
* Config unit for all dashboard components including panels and layouts
* Immutable
*
* @author agrebneva
*
*/
public class Config implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
/**
* Type of components
* set true: it will look up outside config file.
* set false: it does not have outside config.
*
* @author agrebneva
*
*/
public enum Type {
LAYOUT(true), PANEL(true), GRID(true), TREE(true), TAB(false), WIDGET(true), FIELD(false), EXPAND(false), REPEAT_HEADER_GRID(
true);
private boolean hasOwnConfig;
private Type(boolean hasOwnConfig) {
this.hasOwnConfig = hasOwnConfig;
}
public boolean hasOwnConfig() {
return hasOwnConfig;
}
public boolean isLayoutItem() {
return this == LAYOUT;
}
}
/**
* Subcomponent of the config
*
* @author agrebneva
*
*/
public static class Item extends Config {
private static final long serialVersionUID = 1L;
@Pattern(regexp = "[a-zA-Z0-9 \\-/\\+()\"':]{0,150}")
protected String description;
// Field is a json hierarchy with nodes delimited by period and can use optional single
// quote
@Pattern(regexp = "[a-zA-Z0-9 \\.\\-_\\\\]{0,100}")
protected String field;
@Pattern(regexp = "[a-zA-Z0-9 -/\\+()\"':]{0,150}")
protected String value;
@Pattern(regexp = "[a-zA-Z0-9]{0,20}")
protected String width;
@Pattern(regexp = "[a-zA-Z0-9]{0,20}")
protected String datatype;
@Pattern(regexp = "[a-zA-Z0-9]{0,20}")
protected String color;
@Pattern(regexp = "[a-zA-Z0-9.-]{0,40}")
protected String style;
@Pattern(regexp = "[a-zA-Z0-9 \\.\\-]{0,30}")
protected String formatter;
@Pattern(regexp = "[a-zA-Z0-9 \\.\\-]{0,30}")
protected String sorter;
@Pattern(regexp = "[a-zA-Z0-9 \\.\\-]")
protected String align;
public String getDescription() {
return description;
}
public String getField() {
return field;
}
public String getValue() {
return value;
}
public String getWidth() {
return width;
}
public String getColor() {
return color;
}
public String getStyle() {
return style;
}
public String getFormatter() {
return formatter;
}
public String getSorter() {
return sorter;
}
public String getDatatype() {
return datatype;
}
public String getAlign() {
return align;
}
public void setAlign(String align) {
this.align = align;
}
@Override
public Config cloneWithItems(Config.Item[] items) {
Item item;
try {
item = (Item) this.clone();
item.items = ArrayUtils.clone( items );
} catch (CloneNotSupportedException e) {
throw new DashboardException("Unable to clone items", e);
}
return item;
}
public Item cloneWithName(String name) {
Item item;
try {
item = (Item) this.clone();
} catch (CloneNotSupportedException e) {
throw new DashboardException("Unable to clone items", e);
}
item.name = name;
return item;
}
public Item cloneWithParams(String name, String field) {
Item item;
try {
item = (Item) this.clone();
} catch (CloneNotSupportedException e) {
throw new DashboardException("Unable to clone items", e);
}
item.name = name;
item.field = field;
return item;
}
@Override
public String toString() {
return "ViewItem [width=" + width + ", type=" + datatype + ", color=" + color + ", style=" + style
+ ", formatter=" + formatter + ", params=" + params + "]";
}
}
/**
* Data component of the config
*
* @author agrebneva
*
*/
public static class Data implements Serializable {
private static final long serialVersionUID = 1L;
@Pattern(regexp = "[a-zA-Z0-9]{0,50}")
protected String entity;
@Pattern(regexp = "[a-zA-Z0-9 ]{0,50}")
protected String cacheKey;
@Size(max = 30)
@NoBadChars
protected Map<String, Object> params;
protected boolean lazy;
public Data() {
}
public Data(String entity, String cacheKey, boolean lazy, Map<String, Object> params) {
this.entity = entity;
this.cacheKey = cacheKey;
this.params = params;
this.lazy = lazy;
}
public String getEntityRef() {
return entity;
}
public String getCacheKey() {
return cacheKey;
}
public Map<String, Object> getParams() {
return params;
}
public boolean isLazy() {
return lazy;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((cacheKey == null) ? 0 : cacheKey.hashCode());
result = prime * result + ((entity == null) ? 0 : entity.hashCode());
result = prime * result + (lazy ? 1231 : 1237);
if (params != null) {
for (Object value : params.values()) {
result = prime * result + ((value == null) ? 0 : value.hashCode());
}
}
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Data other = (Data) obj;
if (cacheKey == null) {
if (other.cacheKey != null) {
return false;
}
} else if (!cacheKey.equals(other.cacheKey)) {
return false;
}
if (entity == null) {
if (other.entity != null) {
return false;
}
} else if (!entity.equals(other.entity)) {
return false;
}
if (lazy != other.lazy) {
return false;
}
if (params == null) {
if (other.params != null) {
return false;
}
} else if (!params.equals(other.params)) {
return false;
}
return true;
}
}
/**
* Data-related condition on the item
*
* @author agrebneva
*
*/
public static class Condition implements Serializable {
private static final long serialVersionUID = 1L;
@Pattern(regexp = "[a-zA-Z0-9 \\.\\-]{0,30}")
protected String field;
protected Object[] value;
public String getField() {
return field;
}
public Object[] getValue() {
return value;
}
@Override
public String toString() {
return "Condition [field=" + field + ", value=" + Arrays.toString(value) + "]";
}
}
@Pattern(regexp = "[a-zA-Z0-9]{1,30}")
protected String id;
/**
* if id of the parent is different from the id - in case when many similar panels share the
* driver
*/
@Pattern(regexp = "[a-zA-Z0-9]{0,30}")
protected String parentId;
@Pattern(regexp = "[a-zA-Z0-9 \\-/\\+()\"':\\.%]{0,150}")
protected String name;
protected Type type = Type.FIELD;
@Valid
protected Condition condition;
@Valid
protected Data data;
@Valid
protected Item[] items;
@Pattern(regexp = "[a-zA-Z0-9 \\.\\-]{0,30}")
protected String root;
@NoBadChars
@Size(max = 30)
protected Map<String, Object> params;
public Config(String id, String parentId, String name, Type type, Condition condition, Data data, Item[] items,
String root, Map<String, Object> params) {
super();
this.id = id;
this.parentId = parentId;
this.name = name;
this.type = type;
this.condition = condition;
this.data = data;
this.items = ArrayUtils.clone( items );
this.root = root;
this.params = params;
}
public Config() {
}
public String getId() {
return id;
}
public String getParentId() {
return parentId == null ? id : parentId;
}
public String getName() {
return name;
}
public String getRoot() {
return root;
}
public Type getType() {
return type;
}
public Condition getCondition() {
return condition;
}
public Data getData() {
return data;
}
public Item[] getItems() {
return items;
}
public Map<String, Object> getParams() {
return params;
}
public Config cloneWithItems(Item[] items) {
return new Config(id, parentId, name, type, condition, data, items, root, params);
}
/**
* use this method if Config object is required to have a duplicate copy. Config object should
* be immutable in order to avoid confusions.
* It creates a cloned (deep copy) Config object except Config.Data.entity and Config.Data.param
* (these values are overwritten by Config object an input param)
*
* @param customConfig
* Config.Data.entity and Config.Data.param are used to overwrite to a cloned Config
* object
* @return cloned Config object merged with customConfig
*/
public Config overWrite(Config customConfig) {
// parent id for overwrite should be the same as id of the driver
Config config = new Config(this.id, this.id, customConfig.name, this.type, this.condition, new Data(
this.data.entity, customConfig.data.cacheKey, this.data.lazy, customConfig.data.params == null ? null
: Collections.unmodifiableMap(new HashMap<String, Object>(customConfig.data.params))),
(customConfig.items == null) ? this.items : customConfig.items, this.root, this.params);
return config;
}
}