/*
* Copyright (C) 2012 The Android Open Source Project
*
* 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.motorolamobility.studio.android.db.core.ui;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ILazyTreeContentProvider;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.Bundle;
import com.motorola.studio.android.common.log.StudioLogger;
import com.motorolamobility.studio.android.db.core.DbCoreActivator;
import com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent;
import com.motorolamobility.studio.android.db.core.event.DatabaseModelEvent.EVENT_TYPE;
import com.motorolamobility.studio.android.db.core.event.DatabaseModelEventManager;
import com.motorolamobility.studio.android.db.core.i18n.DbCoreNLS;
import com.motorolamobility.studio.android.db.core.ui.view.SaveStateManager;
/**
* Node abstraction to be contributed/implemented through extension point com.motorolamobility.studio.android.db.core.dbRootNode.
*/
public abstract class AbstractTreeNode implements ITreeNode
{
/**
* Property value used to check if the node has an error status.
*/
public static final String PROP_VALUE_NODE_STATUS_ERROR =
"com.motorolamobility.studio.android.db.core.nodeStatusError"; //$NON-NLS-1$
/**
* Property name used to test the status of the node.
*/
public static final String PROP_NAME_NODE_STATUS =
"com.motorolamobility.studio.android.db.core.nodeStatus"; //$NON-NLS-1$
private static final String DEFAULT_ICON_PATH = "icons/obj16/plate16.png"; //$NON-NLS-1$
/*
* id, name, and icon will come from extension point com.motorolamobility.studio.android.db.core.dbRootNode
*/
private String id;
private String name;
private ITreeNode parent;
private ImageDescriptor icon = DbCoreActivator.imageDescriptorFromPlugin(
DbCoreActivator.PLUGIN_ID, DEFAULT_ICON_PATH);
private IStatus nodeStatus = Status.OK_STATUS;
/**
* We need to guarantee the order of the inserted items to use {@link ILazyTreeContentProvider}
* We need to guarantee the change operations are thread-safe by using synchronized list
*/
private final List<ITreeNode> children = new CopyOnWriteArrayList<ITreeNode>();
private volatile boolean isLoading = false;
private String tooltip;
/**
* Default constructor
*
* Warning: If the node comes is declared through extension point com.motorolamobility.studio.android.db.core.dbRootNode
* this constructor is mandatory to exist and be the unique to be used.
*/
public AbstractTreeNode()
{
this(null, null, null);
}
public AbstractTreeNode(ITreeNode parent)
{
this(null, null, parent);
}
public AbstractTreeNode(String id, String name, ITreeNode parent)
{
this(id, name, parent, null);
}
public AbstractTreeNode(String id, String name, ITreeNode parent, ImageDescriptor icon)
{
this.id = id;
this.name = name;
this.parent = parent;
this.icon = icon;
if (this instanceof ISaveStateTreeNode)
{
ISaveStateTreeNode saveStateTreeNode = (ISaveStateTreeNode) this;
SaveStateManager saveStateManager = SaveStateManager.getInstance();
saveStateManager.registerSaveStateNode(saveStateTreeNode);
IEclipsePreferences prefNode = saveStateManager.getPrefNode();
if (prefNode != null)
{
saveStateTreeNode.restoreState(prefNode);
}
}
}
public final void refreshAsync()
{
refreshAsync(true);
}
public final void refreshAsync(final boolean canRefreshYesResponse)
{
if (!isLoading())
{
AbstractLoadingNodeJob loadingJob =
new AbstractLoadingNodeJob(NLS.bind(
DbCoreNLS.AbstractTreeNode_Loading_Job_Name, getName()), this)
{
@Override
protected IStatus run(IProgressMonitor monitor)
{
refresh(canRefreshYesResponse);
return Status.OK_STATUS;
}
};
loadingJob.schedule();
}
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#refresh()
*/
public abstract void refresh();
/**
* @param canRefreshYesResponse
*/
public void refresh(boolean canRefreshYesResponse)
{
refresh();
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getParent()
*/
public ITreeNode getParent()
{
return parent;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setParent(com.motorolamobility.studio.android.db.core.ui.ITreeNode)
*/
public void setParent(ITreeNode parent)
{
this.parent = parent;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getChildren()
*/
public List<ITreeNode> getChildren()
{
return children;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#clear()
*/
public void clear()
{
DatabaseModelEventManager.getInstance().fireEvent(this, EVENT_TYPE.CLEAR);
for (ITreeNode child : getChildren())
{
child.cleanUp();
child.clear();
}
children.clear();
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getChild(int)
*/
public ITreeNode getChild(int index)
{
return children.size() > index ? children.get(index) : null;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getChildById(String)
*/
public ITreeNode getChildById(String id)
{
ITreeNode foundChild = null;
for (ITreeNode node : children)
{
if ((node != null) && (node.getId() != null) && node.getId().equals(id))
{
foundChild = node;
break;
}
}
return foundChild;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getFilteredChildren(java.lang.String)
*/
public List<ITreeNode> getFilteredChildren(String regex)
{
List<ITreeNode> filteredChildren = new ArrayList<ITreeNode>();
if (regex == null)
{
filteredChildren = getChildren();
}
else
{
for (ITreeNode child : children)
{
if ((regex != null) && child.getId().matches(regex))
{
filteredChildren.add(child);
}
}
}
return filteredChildren;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#putChild(java.lang.String, com.motorolamobility.studio.android.db.core.ui.AbstractTreeNode)
*/
public void putChild(ITreeNode treeNode)
{
treeNode.setParent(this);
//node does not exist yet as a child => add it
children.add(treeNode);
DatabaseModelEventManager.getInstance().fireEvent(treeNode,
DatabaseModelEvent.EVENT_TYPE.ADD);
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#putChildren(java.util.List)
*/
public void putChildren(List<ITreeNode> childrenList)
{
children.addAll(childrenList);
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#removeChild(ITreeNode)
*/
public void removeChild(ITreeNode node)
{
node.cleanUp();
children.remove(node);
DatabaseModelEventManager.getInstance().fireEvent(node,
DatabaseModelEvent.EVENT_TYPE.REMOVE);
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#isLoading()
*/
public boolean isLoading()
{
return isLoading;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setLoading(boolean)
*/
public void setLoading(boolean isLoading)
{
this.isLoading = isLoading;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getId()
*/
public String getId()
{
return id;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setId(java.lang.String)
*/
public void setId(String id)
{
this.id = id;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getName()
*/
public String getName()
{
return name;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setName(java.lang.String)
*/
public void setName(String name)
{
this.name = name;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getIcon()
*/
public ImageDescriptor getIcon()
{
return icon;
}
/**
* Retrieves an icon image descriptor from other Eclipse bundles (JDT, Datatools, ADT, etc).
*
* @param bundleId The id of the bundle where the icon should be retrieved from
* @param iconPath The path of the icon inside the bundle
*
* @return The image descriptor for the desired icon, or the default icon from the db code plugin
* in case the process of retrieving the desired icon fails
*/
protected final ImageDescriptor getSpecificIcon(String bundleId, String iconPath)
{
ImageDescriptor imgDesc = null;
try
{
Bundle bundle = Platform.getBundle(bundleId);
URL path = bundle.getEntry("/"); //$NON-NLS-1$
URL fullPathString = new URL(path, iconPath);
imgDesc = ImageDescriptor.createFromURL(fullPathString);
}
catch (Exception e)
{
StudioLogger.error(getClass(), "Error retrieving icon for tree node", e); //$NON-NLS-1$
}
if (imgDesc == null)
{
imgDesc = getIcon();
}
return imgDesc;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setIcon(org.eclipse.jface.resource.ImageDescriptor)
*/
public void setIcon(ImageDescriptor icon)
{
this.icon = icon;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#canRefresh()
*/
public IStatus canRefresh()
{
return Status.OK_STATUS;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#isLeaf()
*/
public abstract boolean isLeaf();
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return "AbstractTreeNode [id=" + id + ", name=" + name + ", parent=" + parent + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#clean()
*/
public void cleanUp()
{
for (ITreeNode node : children)
{
node.cleanUp();
}
if (this instanceof ISaveStateTreeNode)
{
SaveStateManager.getInstance().unregisterSaveStateNode((ISaveStateTreeNode) this);
}
}
public void setNodeStatus(IStatus status)
{
nodeStatus = status;
//update node label/icon/decoration given that its status has changed
DatabaseModelEventManager.getInstance().fireEvent(this, EVENT_TYPE.UPDATE);
}
public IStatus getNodeStatus()
{
return nodeStatus;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IActionFilter#testAttribute(java.lang.Object, java.lang.String, java.lang.String)
*/
public boolean testAttribute(Object target, String name, String value)
{
boolean result = false;
if (name.equals(PROP_NAME_NODE_STATUS))
{
if (value.equals(PROP_VALUE_NODE_STATUS_ERROR))
{
result = !getNodeStatus().isOK();
}
}
return result;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#setTooltip(java.lang.String)
*/
public void setTooltip(String tooltip)
{
this.tooltip = tooltip;
}
/* (non-Javadoc)
* @see com.motorolamobility.studio.android.db.core.ui.ITreeNode#getTooltip()
*/
public String getTooltip()
{
return tooltip;
}
}