/* * Copyright (C) 2014 Divide.io * * 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 io.divide.shared.transitory; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import io.divide.shared.logging.Logger; import io.divide.shared.transitory.query.Query; import java.util.*; import static io.divide.shared.util.ObjectUtils.isArray; import static io.divide.shared.util.ObjectUtils.v2c; /* * User entered data will */ public class TransientObject { private static final Logger logger = Logger.getLogger(TransientObject.class); public static final String USER_DATA = "user_data"; public static final String META_DATA = "meta_data"; public static final MetaKey OBJECT_TYPE_KEY = new MetaKey("object_type"); public static final MetaKey OWNER_ID_KEY = new MetaKey("owner_key"); public static final MetaKey OBJECT_KEY = new MetaKey("object_key"); public static final MetaKey CREATE_DATE_KEY = new MetaKey("create_date_key"); public static final MetaKey MODIFIED_DATE_KEY = new MetaKey("modified_date_key"); private static final MetaKey PERMISSIONS_KEY = new MetaKey("permissions_key"); private static Gson gson = new GsonBuilder().serializeNulls().create(); protected Map<String,Object> user_data = new LinkedHashMap<String, Object>(); protected Map<String,String> meta_data = new LinkedHashMap<String, String>(); private transient boolean isNewObject = false; @SuppressWarnings("unused") protected TransientObject(){ isNewObject = true; meta_put(PERMISSIONS_KEY, new FilePermissions()); setObjectType(Query.safeTable(getClass())); setObjectKey(UUID.randomUUID().toString()); updateCreateDate(); } protected <T extends TransientObject> TransientObject(Class<T> objectType){ isNewObject = true; meta_put(PERMISSIONS_KEY, new FilePermissions()); setObjectType(Query.safeTable(objectType)); setObjectKey(UUID.randomUUID().toString()); updateCreateDate(); } protected Credentials getLoggedInUser(){ return null; } /** * @return whether this object is readable for the currently logged in user. */ public boolean isReadable(){ if(isNewObject)return true; FilePermissions fp = getFilePermissions(); Credentials user = getLoggedInUser(); if(user == null) return false; else if(user.isSystemUser()) return true; else if(fp.isReadable(FilePermissions.Level.WORLD))return true; else if(fp.isReadable(FilePermissions.Level.GROUP) && fp.getGroups().contains(user.getUserGroup())) return true; else if(fp.isReadable(FilePermissions.Level.OWNER) && getOwnerId().equals(user.getOwnerId())) return true; else return false; } /** * @return whether this object is writable for the currently logged in user. */ public boolean isWritable(){ if(isNewObject)return true; FilePermissions fp = getFilePermissions(); Credentials user = getLoggedInUser(); if(user == null) return false; else if(user.isSystemUser()) return true; else if(fp.isWritable(FilePermissions.Level.WORLD))return true; else if(fp.isWritable(FilePermissions.Level.GROUP) && fp.getGroups().contains(user.getUserGroup())) return true; else if(fp.isWritable(FilePermissions.Level.OWNER) && getOwnerId().equals(user.getOwnerId())) return true; else return false; } private void canRead(){ if(!isReadable()) { throw new IllegalAccessError(meta_data.get(OBJECT_KEY.KEY) + " is not readable for " + getLoggedInUser() +". " + getFilePermissions()); } } private void canWrite(){ if(!isWritable()) { throw new IllegalAccessError(meta_data.get(OBJECT_KEY.KEY) + " is not writable for " + getLoggedInUser() +". " + getFilePermissions()); } } /** * Sets new file permissions for this object. * @param filePermissions file permissions to be used for this object. */ public void setFilePermissions(FilePermissions filePermissions){ if(isWritable()){ meta_data.put(PERMISSIONS_KEY.KEY,gson.toJson(filePermissions,FilePermissions.class)); } } /** * Retrieves the file permissions for this object. This not a direct reference. If changes are made they must be stored * with setFilePermissions(). * @return */ public FilePermissions getFilePermissions(){ return gson.fromJson(meta_data.get(PERMISSIONS_KEY.KEY), FilePermissions.class); } protected final void meta_put(MetaKey key, Object object) { // canWrite(); TODO disabled for now. updateModifiedDate(); if(object instanceof String) meta_data.put(key.KEY, (String) object); else meta_data.put(key.KEY,gson.toJson(object)); } protected final <O> O meta_get(Class<O> clazz, MetaKey key){ // canRead(); TODO disabled for now if(clazz.equals(String.class)) return clazz.cast(meta_data.get(key.KEY)); else return gson.fromJson(meta_data.get(key.KEY),clazz); } protected final boolean meta_remove(MetaKey key){ return meta_data.remove(key.KEY) != null; } /** * Add a specific key value pair to this object. * @param key key to identify this element by. * @param object object to be added. * @param serialize whether to manually serialize this object and store it as a json blob. */ public void put(String key, Object object, boolean serialize){ if(serialize) put(key,gson.toJson(object)); else put(key,object); } /** * Add a specific key value pair to this object. * @param key key to identify this element by. * @param object object to be added. */ public void put(String key, Object object) { // canWrite() TODO disabled for now; updateModifiedDate(); user_data.put(key,object); // if(isCollection(object)){ // Object[] array = ((Collection) object).toArray(new Object[0]); // user_data.put(key, array); // } // else if(isArray(object)){ // Object[] array = (Object[]) object; // user_data.put(key, array); // } else { // user_data.put(key,object); // } } /** * Add a map to this object. Must be of type <String,?> * @param map */ public void putAll(Map<? extends String ,?> map) { // canWrite(); TODO disabled for now updateModifiedDate(); user_data.putAll(map); } /** * Retrieve a specific element from this object. * @param clazz type of object to be retrieved. If type given does not match a classcast exception will be thrown. * @param key key of object to be retrieved. * @param deserialize specify of whether or not this object needs to be manually deserialized(stored as a json string). * @return element of type specified corrosponding to the given key. */ public <O> O get(Class<O> clazz, String key, boolean deserialize){ if(deserialize) return gson.fromJson(get(String.class,key),clazz); else return get(clazz,key); } /** * Retrieve a specific element from this object. * @param clazz type of object to be retrieved. If type given does not match a classcast exception will be thrown. * @param key key of object to be retrieved. * @return element of type specified corrosponding to the given key. */ public <O> O get(Class<O> clazz, String key){ // canWrite(); TODO disabled for now Object o = null; try{ o = user_data.get(key); return clazz.cast( o ); } catch (Exception e){ e.printStackTrace(); System.out.println("Get(" + clazz.getSimpleName() + "," + key + ")"); System.out.println(o); return null; } } /** * Remove a specific user data element corrosponding to the given key. * @param key key of object to be removed. * @return */ public final boolean remove(String key){ // canWrite(); TODO disabled for now updateModifiedDate(); return user_data.remove(key) != null; } /** * Removes all user data. This can not be undone. */ public final void removeAll(){ // canWrite(); TODO disabled for now updateModifiedDate(); user_data.clear(); } private long updateCreateDate(){ long createDate = System.currentTimeMillis(); setCreateDate(createDate); setModifiedDate(createDate); return createDate; } private long updateModifiedDate(){ long createDate = System.currentTimeMillis(); setModifiedDate(createDate); return createDate; } /** * @return map representing this objects total user data. This is a new map, not a direct reference. */ public Map<String, Object> getUserData(){ // canRead(); TODO disabled for now return new HashMap<String, Object>(user_data); // return Collections.unmodifiableMap(user_data); } /** * @return map representing this objects total metadata. This is a new map, not a direct reference. */ public Map<String,String> getMetaData(){ // canRead(); TODO disabled for now return new HashMap<String, String>(meta_data); // return Collections.unmodifiableMap(meta_data); } private void setObjectKey(String key){ meta_put(OBJECT_KEY, key); } /** * @return UUID key for this object. */ public String getObjectKey(){ return meta_get(String.class, OBJECT_KEY); } private void setCreateDate(long date){ meta_data.put(CREATE_DATE_KEY.KEY, String.valueOf(date)); } /** * @return this objects creation date. */ public Long getCreateDate(){ return meta_get(Long.class, CREATE_DATE_KEY); } private void setModifiedDate(Long modifiedDate){ meta_data.put(MODIFIED_DATE_KEY.KEY, String.valueOf(modifiedDate)); } /** * @return last modified date for this object. */ public Long getModifiedDate(){ return meta_get(Long.class, MODIFIED_DATE_KEY); } private void setObjectType(String type){ if(stringIsEmpty(type))throw new NullPointerException("Type can not be null"); meta_data.put(OBJECT_TYPE_KEY.KEY,type); // dont allow null } /** * @return this objects type, package + class name. */ public String getObjectType(){ return meta_get(String.class, OBJECT_TYPE_KEY); } // accessable to subclasses and this class, stupid java. protected void setOwnerId(Integer id){ if(getOwnerId() != null){ logger.warn("Attempting to set owner id for an object where it as previously been set. Ignoring new id"); return; } meta_put(OWNER_ID_KEY, id); // dont allow null } /** * Owner Id representing the owner id of ther user who created this object * @return Owner Id of user who created this object. */ public Integer getOwnerId(){ return meta_get(Integer.class, OWNER_ID_KEY); } @Override public String toString() { return getClass().getSimpleName()+"{" + "\n" + "user_data=" + user_data + "\n" + ", meta_data=" + meta_data + "\n" + '}'; } @Override public boolean equals(Object o) { if (null == o) return false; if (this == o) return true; if (!(TransientObject.class.isAssignableFrom(o.getClass()))) return false; TransientObject that = (TransientObject) o; if (isNewObject != that.isNewObject) return false; Set<Map.Entry<String, Object>> entries = user_data.entrySet(); for(Map.Entry<String, Object> entry : entries){ if(entry == null || entry.getValue() == null || entry.getKey() == null) return false; Class c = entry.getValue().getClass(); String key = entry.getKey(); Object one = this.get(c,key); Object two = that.get(c,key); if(one == null || two == null) return false; if(isArray(one)) one = v2c((Object[])one); if(isArray(two)) two = v2c((Object[])two); if(!one.equals(two))return false; } if (meta_data != null ? !meta_data.equals(that.meta_data) : that.meta_data != null) return false; if (user_data != null ? !user_data.equals(that.user_data) : that.user_data != null) return false; return true; } @Override public int hashCode() { int result = user_data != null ? user_data.hashCode() : 0; result = 31 * result + (meta_data != null ? meta_data.hashCode() : 0); result = 31 * result + (isNewObject ? 1 : 0); return result; } public static class MetaKey { public String KEY = ""; public MetaKey(String key){ this.KEY = key; } @Override public String toString(){ return KEY; } } private static boolean stringIsEmpty(String string){ if(string == null) return true; string = string.trim(); return ( string.length()==0 ); } }