/*******************************************************************************
* Copyright (c) 2007 Exadel, Inc. and Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Exadel, Inc. and Red Hat, Inc. - initial API and implementation
******************************************************************************/
package org.jboss.tools.common.model.impl;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IActionFilter;
import org.eclipse.ui.views.properties.IPropertySource;
import org.jboss.tools.common.meta.XAttribute;
import org.jboss.tools.common.meta.XMapping;
import org.jboss.tools.common.meta.XModelEntity;
import org.jboss.tools.common.model.XModel;
import org.jboss.tools.common.model.XModelException;
import org.jboss.tools.common.model.XModelObjectConstants;
import org.jboss.tools.common.model.XModelObject;
import org.jboss.tools.common.model.adapter.IModelObjectAdapter;
import org.jboss.tools.common.model.adapter.ModelObjectAdapterExtensionPoint;
import org.jboss.tools.common.model.icons.impl.XModelObjectIcon;
import org.jboss.tools.common.model.plugin.ModelPlugin;
import org.jboss.tools.common.model.util.EclipseResourceUtil;
import org.jboss.tools.common.util.FileUtil;
public class XModelObjectImpl implements XModelObject, Serializable, Cloneable {
private static final long serialVersionUID = 3860648580262144825L;
// protected static final String ENTITY = XModelConstants.XMODEL_ENTITY_ATTR;
public static final String DUPLICATE = "__duplicate";
private XModel model = null;
private XModelEntity entity = null;
private XModelObjectImpl parent = null;
protected XModelObjectImpl.SP properties = new SProperties();
protected boolean modified = false;
protected long timeStamp = System.currentTimeMillis();
protected long lastModificationTimeStamp = 0;
public XModelObjectImpl() {}
public XModel getModel() {
return model;
}
void setModel(XModel model) {
this.model = model;
}
public XModelEntity getModelEntity() {
return entity;
}
/**
* Destroys object.
*/
public void destroy() {
//TODO provide safe work at removing model, entity etc.
// model = null;
parent = null;
// entity = null;
//
}
public void changeEntity(String name) {
if(entity.getName().equals(name)) return;
XModelEntity newEntity = entity.getMetaModel().getEntity(name);
if(newEntity == null) throw new IllegalArgumentException("Entity " + name + " does not exist."); //$NON-NLS-1$ //$NON-NLS-2$
if(entity.getImplementingClass() != newEntity.getImplementingClass()) {
throw new IllegalArgumentException("Cannot convert entity " + entity.getName() + " to " + name + " because they have different implementations."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
Properties p = new Properties();
XAttribute[] as = entity.getAttributes();
for (int i = 0; i < as.length; i++) {
String n = as[i].getName();
String v = getAttributeValue(n);
if(v != null) p.setProperty(n, v);
}
entity = newEntity;
as = newEntity.getAttributes();
for (int i = 0; i < as.length; i++) {
String n = as[i].getName();
String v = p.getProperty(n);
if(v == null) v = as[i].getDefaultValue();
if(v == null) v = ""; //$NON-NLS-1$
setAttributeValue(n, v);
}
}
void setEntityName_0(String entityname) {
entity = getModel().getMetaData().getEntity(entityname);
((SProperties)properties).init(entity);
onSetEntity(entityname);
}
protected void onSetEntity(String entity) {}
public XModelObject getParent() {
return parent;
}
public void setParent_0(XModelObjectImpl parent) {
this.parent = parent;
if(parent != null) {
if(model != parent.getModel()) setModel(parent.getModel());
} else {
modified = false;
}
}
public boolean isActive() {
return (getParent() != null && getParent().isActive());
}
public boolean isObjectEditable() {
return getParent() == null || getParent().isObjectEditable();
}
public boolean isAttributeEditable(String name) {
return isObjectEditable();
}
public int getFileType() {
return NONE;
}
// modification
public String get(String name) {
return properties.get(name);
}
public void set(String name, String value) {
if(value != null && value.length() < 100) value = value.intern();
properties.put(name.intern(), value);
}
private Boolean hasIdAttr = null;
public boolean hasIdAttr() {
if(hasIdAttr != null) return hasIdAttr.booleanValue();
hasIdAttr = Boolean.FALSE;
for (int i = 0; i < getModelEntity().getAttributes().length; i++) {
if("true".equals(getModelEntity().getAttributes()[i].getProperty("id"))) {
hasIdAttr = Boolean.TRUE;
break;
}
}
return hasIdAttr.booleanValue();
}
protected String get_0(String name) {
XAttribute a = getModelEntity().getAttribute(name);
return (a == null || a.getAdapter() == null) ? null : a.getAdapter().getProperty(this);
}
protected void set_0(String name, String value) {
XAttribute a = getModelEntity().getAttribute(name);
if(a != null && a.getAdapter() != null) a.getAdapter().setProperty(this, value);
}
Map<String,Object> context = null;
public Object getObject(String name) {
return (context == null) ? null : context.get(name);
}
public void setObject(String name, Object o) {
if(context == null) {
if(o == null) return;
context = new HashMap<String,Object>();
}
if(o == null) context.remove(name); else context.put(name, o);
}
public String getAttributeValue(String name) {
return get_0(name);
}
public String setAttributeValue(String name, String value) {
if(getModelEntity().getAttribute(name) == null) return ""; //$NON-NLS-1$
String ov = getAttributeValue(name);
if(value != null && value.equals(ov)) return ov;
String path = getPath();
set_0(name, value);
String nv = getAttributeValue(name);
if(nv != null && !nv.equals(ov)) {
changeTimeStamp();
if(path != null) ((XModelImpl)getModel()).fireNodeChanged(this, path);
}
return nv;
}
protected void onAttributeValueEdit(String name, String oldValue, String newValue) throws XModelException {
}
public boolean isModified() {
return modified;
}
public void setModified(boolean value) {
if(parent != null && value) {
parent.setModified(value);
}
modified = value;
if(modified) ++lastModificationTimeStamp;
}
public long getTimeStamp() {
return timeStamp;
}
public long getLastModificationTimeStamp() {
return lastModificationTimeStamp;
}
protected void changeTimeStamp() {
++timeStamp;
if(parent != null) parent.changeTimeStamp();
}
protected void safeChangeTimeStamp() {
++timeStamp;
if(parent != null) parent.safeChangeTimeStamp();
}
// children
public XModelObject[] getChildren() {
return new XModelObject[0];
}
public XModelObject[] getChildren(String name) {
XModelObject[] mes = getChildren();
ArrayList<XModelObject> v = new ArrayList<XModelObject>();
for (int i = 0; i < mes.length; i++) {
if(name.equals(mes[i].getModelEntity().getName()))
v.add(mes[i]);
}
return v.toArray(new XModelObject[v.size()]);
}
/*
* Non-interface method that are called only by the default
* saver (XModelObjectLoaderUtil).
* Should be overrided by subclass that overrides method
* loadChildren() of RegularObjectImpl
*/
public XModelObject[] getChildrenForSave() {
return getChildren();
}
public XModelObject[] getLoadedChildren() {
return getChildren();
}
public XModelObject getChildAt(int i) {
XModelObject[] children = getChildren();
return (i < 0 || i >= children.length) ? null : children[i];
}
public boolean hasChildren() {
return getChildren().length > 0;
}
public boolean addChild(XModelObject object) {
boolean b = addChild_0(object);
if(b) fireStructureChanged(org.jboss.tools.common.model.event.XModelTreeEvent.CHILD_ADDED, object);
return b;
}
public boolean addChild_0(XModelObject o) {
return false;
}
public void removeChild(XModelObject child) {
if(child.getParent() != this) return;
String path = child.getPath();
removeChild_0(child);
fireStructureChanged(org.jboss.tools.common.model.event.XModelTreeEvent.CHILD_REMOVED, path);
}
public void removeChild_0(XModelObject o) {}
public void removeFromParent() {
if(getParent() != null) getParent().removeChild(this);
}
// paths
public String getLongPath() {
if(getParent() == null) return null;
String p = parent.getLongPath();
return (p == null) ? null : (p.length() == 0)
? getPathPart() : p + XModelObjectConstants.SEPARATOR + getPathPart();
}
public String getPath() {
String lp = getLongPath();
if(lp == null) return null;
XMapping m = getModel().getMetaData().getMapping("Roots"); //$NON-NLS-1$
String h = "" + lp; //$NON-NLS-1$
do {
int ib = h.lastIndexOf('/');
String q = h.substring(ib + 1), r = m.getValue(q);
if(r != null && h.equals(r)) return "%" + q + "%" + lp.substring(r.length()); //$NON-NLS-1$ //$NON-NLS-2$
if(ib >= 0) h = h.substring(0, ib); else h = null;
} while(h != null);
return lp;
}
public String getPathPart() {
String p = name();
p = ((p == null || p.indexOf('/') < 0) ? p : p.replace('/', '#'));
return applyDuplicate(p);
}
protected final String applyDuplicate(String pathpart) {
String duplicate = get(DUPLICATE);
return (duplicate == null || duplicate.length() == 0)
? pathpart
: pathpart + DUPLICATE + duplicate;
}
public XModelObject getChildByPath(String path) {
int i = path.indexOf(XModelObjectConstants.SEPARATOR);
String n = (i < 0) ? path : path.substring(0, i);
String f = (i < 0) ? "" : path.substring(i + 1); //$NON-NLS-1$
XModelObject me = getChildByPathPart(n);
if(me == null) {
if(n.equals("..")) me = getParent(); //$NON-NLS-1$
else if(n.equals(".")) me = this; //$NON-NLS-1$
}
return (me == null || i < 0) ? me : me.getChildByPath(f);
}
public XModelObject getChildByPathPart(String pathpart) {
XModelObject[] cs = getChildren();
for (int i = 0; i < cs.length; i++)
if(cs[i].getPathPart().equals(pathpart)) return cs[i];
return null;
}
// copy
public XModelObject copy(boolean transform, int level) {
XModelObject copy = shallow_copy(transform);
if(copy != null) copy.setModified(true);
if(level == 0 || copy == null) return copy;
XModelObject[] cs = getChildrenForSave();
for (int i = 0; i < cs.length; i++) {
XModelObject ccopy = cs[i].copy(transform, level - 1);
if(ccopy == null) continue;
copy.addChild(ccopy);
}
return copy;
}
public XModelObject copy(boolean transform) {
if(getModel() == null) return null;
XModelObject copy = shallow_copy(transform);
if(copy != null) copy_children(copy, transform);
return copy;
}
public XModelObject copy(int level) {
return copy(false, level);
}
public XModelObject shallow_copy(boolean transform) {
XAttribute[] as = getModelEntity().getAttributes();
Properties p = new Properties();
for (int i = 0; i < as.length; i++) {
if(!as[i].isCopyable()) continue;
String n = as[i].getName();
String v = getAttributeValue(n);
if(v != null) p.setProperty(n, getAttributeValue(n));
}
String entity = (transform) ? getEntityForCopy() : getModelEntity().getName();
XModelObject c = getModel().createModelObject(entity, p);
if(c != null) c.setModified(true);
String d = get(DUPLICATE);
if(d != null && d.length() > 0) {
c.set(DUPLICATE, d);
}
return c;
}
protected void copy_children(XModelObject copy, boolean transform) {
XModelObject[] cs = getChildrenForSave();
for (int i = 0; i < cs.length; i++) {
XModelObjectImpl c = (XModelObjectImpl)cs[i];
XModelObject ccopy = c.shallow_copy(transform);
if(ccopy == null) continue;
c.copy_children(ccopy, transform);
if(copy.addChild(ccopy)) continue;
//child was created during setting attributes
XModelObject cc = copy.getChildByPath(ccopy.getPathPart());
if(cc == null) continue;
copy.removeChild(cc);
copy.addChild(ccopy);
}
}
public XModelObject copy() {
return copy(true);
}
protected String getEntityForCopy() {
return getModelEntity().getName();
}
// presentation
public String getPresentationString() {
return name();
}
protected String name() {
XAttribute a = getModelEntity().getAttribute(XModelObjectConstants.ATTR_NAME);
return (a != null) ? getAttributeValue(XModelObjectConstants.ATTR_NAME) : getModelEntity().getName();
}
public String getMainIconName() {
try {
return getModelEntity().getRenderer().getIconInfo("main"); //$NON-NLS-1$
} catch (NullPointerException e) {
return "main.closedbox"; //$NON-NLS-1$
}
}
public Image getImage() {
return new XModelObjectIcon(this).getEclipseImage();
}
public Image getImage(String[] types) {
return new XModelObjectIcon(this).getEclipseImage0(types);
}
protected void fireStructureChanged(int kind, Object info) {
changeTimeStamp();
XModelImpl m = (XModelImpl)getModel();
m.fireStructureChanged(this, kind, info);
}
public boolean isEqual(XModelObject o) {
if(o == null) return false;
if(!getModelEntity().getName().equals(o.getModelEntity().getName())) return false;
XAttribute[] as = getModelEntity().getAttributes();
for (int i = 0; i < as.length; i++) {
if(!as[i].isCopyable()) continue;
String n = as[i].getName();
String va = getAttributeValue(n);
String vb = o.getAttributeValue(n);
if((va == null && vb != null) || !va.equals(vb)) return false;
}
XModelObject[] ca = getChildrenForSave(), cb = o.getChildrenForSave();
if(ca.length != cb.length) return false;
for (int i = 0; i < ca.length; i++)
if(!ca[i].isEqual(cb[i])) return false;
return true;
}
public interface SP {
public String get(String name);
public void put(String name, String value);
public void remove(String name);
}
static ModelObjectAdapterExtensionPoint ep = ModelObjectAdapterExtensionPoint.getInstance();
private static IModelObjectAdapter propertySource = ep.getAdapter("IPropertySource"); //$NON-NLS-1$
public Object getAdapter(Class adapter) {
if(XModelObject.class == adapter) return this;
else if(adapter == IResource.class) {
//implementation moved to XModelObjectToResourceAdapter
return null;
} else if(adapter == IProject.class) {
return EclipseResourceUtil.getProject(this);
} else if(adapter == IFile.class) {
XModelObject f = getResourceAncestor();
Object o = (f == null) ? null : f.getAdapter(IResource.class);
return (o instanceof IFile) ? o : null;
} else if(adapter == IJavaElement.class) {
return EclipseResourceUtil.findJavaElement(this);
} else if(adapter == IActionFilter.class) {
IModelObjectAdapter af = ep.getAdapter("IActionFilter"); //$NON-NLS-1$
if(af != null) af.setModelObject(this);
return af;
} else if (adapter == IPropertySource.class) {
if(propertySource != null) propertySource.setModelObject(this);
return propertySource;
}
return null;
}
public XModelObject getResourceAncestor() {
XModelObject p = this;
while(p != null && p.getFileType() == NONE) p = p.getParent();
return p;
}
public void fireObjectChanged(Object details) {
((XModelImpl)getModel()).fireNodeChanged(this, getPath(), details);
}
protected int errorState = 0;
protected int errorChildCount = 0;
protected int warningChildCount = 0;
protected HashSet<String> errorAttributes;
protected HashSet<String> errorAttributesDirty;
public int getErrorState() {
return errorState;
}
public int getErrorChildCount() {
return errorChildCount;
}
public int getWarningChildCount() {
return warningChildCount;
}
public boolean getAttributeErrorState(String attributeName) {
return errorAttributes != null && errorAttributes.contains(attributeName);
}
public void setErrorState(int b) {
if(b != 0) {
commitErrorAttributes();
}
if(errorState == b) return;
int oldErrorState = errorState;
errorState = b;
if(b == 0) {
errorAttributes = null;
errorAttributesDirty = null;
}
if(errorChildCount > 0 && b == 2 && oldErrorState == 0) return;
if(warningChildCount > 0 && b == 1 && oldErrorState == 0) return;
fireErrorStateChanged();
if(parent != null) {
if(oldErrorState == 2 && errorChildCount == 0) {
parent.unregisterErrorChild();
} else if(oldErrorState == 1 && warningChildCount == 0) {
parent.unregisterWarningChild();
}
if(errorState == 2 && errorChildCount == 0) {
parent.registerErrorChild();
} else if(errorState == 1 && warningChildCount == 0) {
parent.registerWarningChild();
}
}
}
public void registerErrorChild() {
errorChildCount++;
if(errorChildCount == 1 && errorState != 2) {
fireErrorStateChanged();
if(parent != null) parent.registerErrorChild();
}
}
public void unregisterErrorChild() {
errorChildCount--;
if(errorChildCount == 0 && errorState != 2) {
fireErrorStateChanged();
if(parent != null) parent.unregisterErrorChild();
}
}
public void registerWarningChild() {
warningChildCount++;
if(warningChildCount == 1 && errorState != 1) {
fireErrorStateChanged();
if(parent != null) parent.registerWarningChild();
}
}
public void unregisterWarningChild() {
warningChildCount--;
if(warningChildCount == 0 && errorState != 1) {
fireErrorStateChanged();
if(parent != null) parent.unregisterWarningChild();
}
}
private void fireErrorStateChanged() {
safeChangeTimeStamp();
String path = getPath();
if(path != null) {
fireObjectChanged(null);
}
}
public void addErrorAttributeDirty(String attributeName) {
if(errorAttributesDirty == null) {
errorAttributesDirty = new HashSet<String>();
}
errorAttributesDirty.add(attributeName);
}
public void commitErrorAttributes() {
if(errorAttributes == null) {
if(errorAttributesDirty == null) return;
errorAttributes = errorAttributesDirty;
errorAttributesDirty = null;
fireErrorStateChanged();
} else if(errorAttributesDirty == null) {
errorAttributes = null;
fireErrorStateChanged();
} else {
boolean b = false;
Iterator<String> it = errorAttributes.iterator();
while(it.hasNext()) {
Object o = it.next();
if(!errorAttributesDirty.contains(o)) {
b = true;
it.remove();
}
}
it = errorAttributesDirty.iterator();
while(it.hasNext()) {
String o = it.next();
if(!errorAttributes.contains(o)) {
b = true;
errorAttributes.add(o);
}
}
if(errorAttributes.isEmpty()) errorAttributes = null;
errorAttributesDirty = null;
if(b) fireErrorStateChanged();
}
}
}
class SProperties implements XModelObjectImpl.SP {
static final String SUFFIX = ".sp.txt";
static final String MOD_SUFFIX = ".mod.sp.txt";
static int modification = 0;
String[] list = null;
XModelEntity entity;
void init(XModelEntity entity) {
this.entity = entity;
list = new String[entity.getAttributes().length];
}
public String get(String name) {
int i = entity.getPropertyIndex(name, false);
String value = (i < 0 || i >= list.length) ? null : list[i];
if(value != null && value.endsWith(SUFFIX)) {
synchronized(this) {
File f = new File(value);
if(f.exists()) {
value = FileUtil.readFile(f);
} else {
//Report illegal state
String message = "Cannot read " + value + " for object\n entity:" + entity.getName() + "\n";
XAttribute[] as = entity.getAttributes();
for (XAttribute a: as) {
int j = entity.getPropertyIndex(a.getName(), false);
String v = (j < 0 || j >= list.length) ? null : list[j];
if(v != null && v.length() > 200) {
v = v.substring(0, 200);
}
if(v != null) {
message += a.getName() + "=" + v + "\n";
}
}
ModelPlugin.getDefault().logError(new IllegalStateException(message));
return "";
}
}
}
return value;
}
public void put(String name, String value) {
int i = entity.getPropertyIndex(name, true);
ensureCapacity(i);
if(value != null && value.length() > 10000 && ModelPlugin.getDefault().getStateTempFolder() != null) {
synchronized(this) {
long l = value.hashCode();
l = Math.abs((l << 32) + value.substring(100, value.length() - 100).hashCode());
File f = new File(ModelPlugin.getDefault().getStateTempFolder(), ModelPlugin.TEMP_FILE_PREFIX + l + SUFFIX);
String fileName = f.getAbsolutePath();
if(f.exists() && fileName.equals(list[i])) {
return;
}
if(!f.exists() || (list[i] != null && list[i].endsWith(MOD_SUFFIX))) {
if(list[i] != null && list[i].endsWith(SUFFIX)) {
if(!list[i].endsWith(MOD_SUFFIX)) {
fileName = list[i].substring(0, list[i].length() - SUFFIX.length()) + "." + (modification++) + MOD_SUFFIX;
} else {
fileName = list[i];
}
f = new File(fileName);
}
FileUtil.writeFile(f, value);
f.deleteOnExit();
}
list[i] = fileName;
}
} else {
list[i] = value;
}
}
public void remove(String name) {
int i = entity.getPropertyIndex(name, false);
if (i >= 0 && i < list.length) list[i] = null;
}
private void ensureCapacity(int i) {
if(i < list.length) return;
String[] _list = new String[i + 1];
System.arraycopy(list, 0, _list, 0, list.length);
list = _list;
}
}
class SProperty {}