/*******************************************************************************
* Copyright (c) 2014 Salesforce.com, inc..
* All rights reserved. This program and the accompanying materials
* are 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:
* Salesforce.com, inc. - initial API and implementation
******************************************************************************/
package com.salesforce.ide.schemabrowser.ui;
import java.lang.reflect.InvocationTargetException;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Stack;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import com.salesforce.ide.core.internal.context.ContainerDelegate;
import com.salesforce.ide.core.internal.utils.ForceExceptionUtils;
import com.salesforce.ide.core.internal.utils.Utils;
import com.salesforce.ide.core.project.ForceProjectException;
import com.salesforce.ide.core.remote.Connection;
import com.salesforce.ide.core.remote.ForceConnectionException;
import com.salesforce.ide.core.remote.ForceRemoteException;
import com.salesforce.ide.schemabrowser.ui.tableviewer.QueryTableViewer;
import com.salesforce.ide.ui.internal.ForceImages;
import com.salesforce.ide.ui.internal.editor.BaseMultiPageEditorPart;
import com.salesforce.ide.ui.internal.utils.UIUtils;
import com.sforce.soap.partner.fault.wsc.InvalidSObjectFault;
import com.sforce.soap.partner.fault.wsc.UnexpectedErrorFault;
import com.sforce.soap.partner.wsc.ChildRelationship;
import com.sforce.soap.partner.wsc.DescribeSObjectResult;
import com.sforce.soap.partner.wsc.Field;
import com.sforce.soap.partner.wsc.PicklistEntry;
import com.sforce.soap.partner.wsc.QueryResult;
import com.sforce.ws.ConnectionException;
/**
* TODO: Can this be made into a single page editor?
*
* Legacy class
*
* @author dcarroll
*/
public class SchemaBrowser extends BaseMultiPageEditorPart {
private static final Logger logger = Logger.getLogger(SchemaBrowser.class);
private static final String MESSAGE_GETTING_CHILD_FIELDS = "Getting child fields and relationships...";
private static final String MESSAGE_ERROR_LOADING_SCHEMA = "Error Loading Schema";
private static final String LABEL_SCHEMA_EXPLORER = "Schema Explorer";
private static final String MESSAGE_SEE_DETAILS = "See details for more info.";
private static final String MESSAGE_COULD_NOT_FETCH_META_DATA = "Could not fetch meta-data";
private static final String FQN = "fqn";
private static final String RESTRICTED = "restricted";
private static final String NILLABLE = "nillable";
private static final String NAME_FIELD = "name field";
private static final String FILTERABLE = "filterable";
private static final String DEFAULTED_ON_CREATE = "defaulted on create";
private static final String FORMULA = "formula";
private static final String AUTO_NUMBER = "auto number";
private static final String UPDATEABLE = "updateable";
private static final String UNDELETEABLE = "undeleteable";
private static final String SEARCHABLE = "searchable";
private static final String RETRIEVEABLE = "retrieveable";
private static final String REPLICATABLE = "replicatable";
private static final String QUERYABLE = "queryable";
private static final String LAYOUTABLE = "layoutable";
private static final String DELETEABLE = "deleteable";
private static final String CUSTOM = "custom";
private static final String CREATEABLE = "createable";
private static final String ACTIVATABLE = "activatable";
private static final String NODE_LABEL_CHILD_RELATIONSHIPS = "Child Relationships";
private static final String MESSAGE_GETTING_LOOKUP = "Getting Lookup Relationship object definition...";
private static final String MESSAGE_GETTING_CHILD = "Getting Child Relationship object definition...";
private static final String IS_TOP_LEVEL = "isTopLevel";
private static final String LOADED = "loaded";
private static final String HAS_CHECKABLE_CHILDREN = "hasCheckableChildren";
private static final String IMAGE_TYPE = "imageType";
private static final int IMAGE_TYPE_CHECKED = 0;
private static final String TYPE = "type";
private static final int TYPE_MINUSONE = -1;
static final Integer PRIMARY_ROOT_NODE = 0;
static final Integer PRIMARY_ROOT_FIELD = 1;
// The tree node
static final Integer PRIMARY_OBJECT_FIELDS_NODE = 2;
// named Fields on the root object
static final Integer LOOKUP_RELATIONSHIP_NODE = 3;
static final Integer LOOKUP_RELATIONSHIP_FIELD = 4;
static final Integer LOOKUP_RELATIONSHIP_FIELDS_NODE = 5;
static final Integer CHILD_RELATIONSHIP_FIELD = 6;
static final Integer CHILD_FIELDS_NODE = 7;
static final Integer CHILD_RELATIONSHIP_NODE = 8;
static final Integer DATA_TYPE_NODE = 9;
static final Integer REFERENCE_TO_NODE = 10;
protected DescribeSObjectResult dr = null;
protected SchemaTreeLabelProvider provider = null;
protected TreeItem selectedItem = null;
protected boolean wasExpanded = false;
protected Image imageNotChecked = null;
protected Image imageChecked = null;
protected Image imageArrowUp = null;
protected Image imageArrowDown = null;
protected Image imageBlank = null;
protected IFile file = null;
protected SashForm sashForm = null;
protected StyledText textSOQL = null;
protected SchemaEditorComposite schemaEditorComposite = null;
protected QueryTableViewer queryTableViewer = null;
// REVIEWME: why stored when we store in describe registry?
private final Hashtable<String, DescribeSObjectResult> describeCache =
new Hashtable<String, DescribeSObjectResult>();
// C O N S T R U C T O R S
public SchemaBrowser() throws ForceProjectException {
super();
queryTableViewer = new QueryTableViewer();
}
// M E T H O D S
@Override
protected String getEditorName() {
return "Schema Browser";
}
public QueryTableViewer getQueryTableViewer() {
return queryTableViewer;
}
@Override
protected IEditorSite createSite(IEditorPart editor) {
IEditorSite site = null;
site = super.createSite(editor);
return site;
}
/**
* Creates the pages of the multi-page editor.
*/
@Override
protected void createPages() {
setPartName(file.getProject().getName());
int index;
try {
index = addPage(createPage(new Composite(getContainer(), SWT.NONE)));
setPageText(index, LABEL_SCHEMA_EXPLORER);
} catch (Exception e) {
logger.error("Unable to open Schema Browser", e);
Utils.openError(new InvocationTargetException(e), true, "Unable to open Schema Browser.");
index = addPage(new Composite(getContainer(), SWT.NONE));
setPageText(index, MESSAGE_ERROR_LOADING_SCHEMA);
}
if (logger.isDebugEnabled()) {
logger.debug("Opening Schema Browser");
}
}
private Composite createPage(Composite composite) throws ForceConnectionException, ForceProjectException,
ForceRemoteException {
schemaEditorComposite =
new SchemaEditorComposite(getContainer(), SWT.NONE, file.getProject(), queryTableViewer);
wireUpComposite();
UIUtils.setHelpContext(schemaEditorComposite, this.getClass().getSimpleName());
return schemaEditorComposite;
}
private void wireUpComposite() {
schemaEditorComposite.getButtonRun().addMouseListener(new org.eclipse.swt.events.MouseListener() {
public void mouseUp(org.eclipse.swt.events.MouseEvent e) {
try {
PlatformUI.getWorkbench().getProgressService().run(false, true, runQuery);
} catch (InvocationTargetException e1) {
logger.error("Unable to open Schema Browser", ForceExceptionUtils.getRootCause(e1));
Utils.openError(ForceExceptionUtils.getRootCause(e1), false, "Unable to open Schema Browser.");
} catch (InterruptedException e1) {
;
}
}
public void mouseDoubleClick(org.eclipse.swt.events.MouseEvent e) {}
public void mouseDown(org.eclipse.swt.events.MouseEvent e) {}
});
schemaEditorComposite.getButtonRefresh().addMouseListener(new org.eclipse.swt.events.MouseListener() {
public void mouseUp(org.eclipse.swt.events.MouseEvent e) {
initialize();
}
public void mouseDoubleClick(org.eclipse.swt.events.MouseEvent e) {}
public void mouseDown(org.eclipse.swt.events.MouseEvent e) {}
});
createTree(schemaEditorComposite);
}
IRunnableWithProgress runQuery = new IRunnableWithProgress() {
public void run(IProgressMonitor monitor) throws InvocationTargetException {
try {
IProject project = file.getProject();
Connection connection = getConnectionFactory().getConnection(project);
// get describe object w/o client id
QueryResult qr = connection.query(schemaEditorComposite.getTextSOQL().getText(), false);
if (schemaEditorComposite.getTextSOQL().getText().contains("count()")) {
if (logger.isDebugEnabled()) {
logger.debug("count() executed");
}
Utils.openInfo("Record Count", (qr != null ? qr.getSize() : 0) + " records");
}
fillTable(qr, monitor, true);
} catch (ConnectionException e) {
logger.error(e);
Utils.openError(e, false, "Failed to execute query");
} catch (Exception e) {
logger.error(e);
throw new InvocationTargetException(e);
} finally {
if (monitor != null) {
monitor.done();
}
;
}
}
};
@Override
public String getTitle() {
String title = super.getTitle();
if ((title == null) && (getEditorInput() != null)) {
title = getEditorInput().getName();
}
return title;
}
@Override
public void doSaveAs() {
// Nothing to save for now
}
/**
* The <code>MultiPageEditorExample</code> implementation of this method checks that the input is an instance of
* <code>IFileEditorInput</code>.
*/
@Override
public void init(IEditorSite site, IEditorInput editorInput) throws PartInitException {
super.init(site, editorInput);
IFileEditorInput fei = (IFileEditorInput) editorInput;
file = fei.getFile();
setPartName("Schema Browser");
}
/*
* (non-Javadoc) Method declared on IEditorPart.
*/
@Override
public boolean isSaveAsAllowed() {
return false;
}
/**
* Calculates the contents of page 2 when the it is activated.
*/
@Override
protected void pageChange(int newPageIndex) {
getEditorSite().getActionBars().getStatusLineManager().setErrorMessage(null);
super.pageChange(newPageIndex);
}
@Override
public void setInitializationData(IConfigurationElement config, String propertyName, Object data) {
super.setInitializationData(config, propertyName, data);
}
@Override
public void setInput(IEditorInput input) {
super.setInput(input);
setPartName(input.getName());
}
@Override
public void doSave(IProgressMonitor monitor) {
// nothing to do, this is only a view on some remote content
}
private void createTree(Composite composite) {
imageNotChecked = ForceImages.get(ForceImages.IMAGE_NOT_CHECKED);
imageChecked = ForceImages.get(ForceImages.IMAGE_CHECKED);
imageArrowDown = ForceImages.get(ForceImages.IMAGE_ARROW_DOWN);
imageArrowUp = ForceImages.get(ForceImages.IMAGE_ARROW_UP);
imageBlank = ForceImages.get(ForceImages.IMAGE_BLANK);
final Composite thisComposite = composite;
provider = new SchemaTreeLabelProvider();
Tree tree = this.schemaEditorComposite.getTree();
tree.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
if (event.item instanceof TreeItem) {
selectedItem = (TreeItem) event.item;
} else {
selectedItem = null;
}
}
});
tree.addTreeListener(new org.eclipse.swt.events.TreeListener() {
public void treeExpanded(org.eclipse.swt.events.TreeEvent e) {
final TreeItem selectedItem = (TreeItem) e.item;
Boolean isTopLevel = (Boolean) selectedItem.getData(IS_TOP_LEVEL);
if ((isTopLevel != null) && isTopLevel.booleanValue()) {
if (selectedItem.getItemCount() == 1) {
Runnable lt = new Runnable() {
public void run() {
ProgressMonitorDialog mon = new ProgressMonitorDialog(getShell());
mon.getProgressMonitor();
mon.setBlockOnOpen(false);
mon.open();
loadTreeData(selectedItem, thisComposite, mon.getProgressMonitor());
setHasCheckableChildren(selectedItem, Boolean.TRUE);
setIsTopLevel(selectedItem, Boolean.TRUE);
setItemNotChecked(selectedItem);
selectedItem.setImage(provider.getImage(0, selectedItem.getText(), selectedItem
.getParent()));
mon.close();
}
};
Runnable lb = new Runnable() {
public void run() {
ProgressMonitorDialog mon = new ProgressMonitorDialog(getShell());
mon.getProgressMonitor();
mon.setBlockOnOpen(false);
mon.open();
mon.getProgressMonitor().beginTask("Get object definition...", 2);
loadObject(selectedItem.getText(), mon.getProgressMonitor());
mon.close();
}
};
getSite().getShell().getDisplay().asyncExec(lb);
getSite().getShell().getDisplay().asyncExec(lt);
}
} else {
Integer type = (Integer) selectedItem.getData(TYPE);
if (type != null) {
if (type.equals(SchemaBrowser.CHILD_RELATIONSHIP_NODE)
&& selectedItem.getData(LOADED).equals(Boolean.FALSE)) {
Runnable getThisChildSchema = new Runnable() {
public void run() {
ProgressMonitorDialog mon = new ProgressMonitorDialog(getShell());
mon.getProgressMonitor();
mon.setBlockOnOpen(false);
mon.open();
mon.getProgressMonitor().beginTask(MESSAGE_GETTING_CHILD, 2);
loadOneChildRelationship(selectedItem, mon.getProgressMonitor());
mon.close();
}
};
getSite().getShell().getDisplay().asyncExec(getThisChildSchema);
} else if (SchemaBrowser.LOOKUP_RELATIONSHIP_NODE.equals(type)
&& Boolean.FALSE.equals(selectedItem.getData(LOADED))) {
Runnable getThisChildSchema = new Runnable() {
public void run() {
ProgressMonitorDialog mon = new ProgressMonitorDialog(getShell());
mon.getProgressMonitor();
mon.setBlockOnOpen(false);
mon.open();
mon.getProgressMonitor().beginTask(MESSAGE_GETTING_LOOKUP, 2);
loadOneChildRelationship(selectedItem, mon.getProgressMonitor());
mon.close();
}
};
getSite().getShell().getDisplay().asyncExec(getThisChildSchema);
}
}
}
wasExpanded = true;
}
public void treeCollapsed(org.eclipse.swt.events.TreeEvent e) {
wasExpanded = true;
}
});
tree.addMouseListener(new org.eclipse.swt.events.MouseListener() {
public void mouseUp(org.eclipse.swt.events.MouseEvent e) {
if (!wasExpanded) {
if (selectedItem != null) {
if (selectedItem.getImage() != null) {
Rectangle rect = selectedItem.getBounds();
Image img = selectedItem.getImage();
Rectangle imgRect = img.getBounds();
int leftMost = rect.x - imgRect.width - 3;
int rightMost = rect.x - 3;
if ((e.x >= leftMost) && (e.x <= rightMost)) {
Integer imageType = (Integer) selectedItem.getData(IMAGE_TYPE);
if (imageType != null) {
if (imageType.intValue() == IMAGE_TYPE_CHECKED) {
setItemChecked(selectedItem);
} else {
setItemNotChecked(selectedItem);
}
Integer type = (Integer) selectedItem.getData(TYPE);
if (SchemaBrowser.CHILD_RELATIONSHIP_NODE.equals(type)
&& Boolean.FALSE.equals(selectedItem.getData(LOADED))) {
if ((selectedItem.getData(LOADED) != null)
&& Boolean.FALSE.equals(selectedItem.getData(LOADED))) {
Runnable getThisChildSchema = new Runnable() {
public void run() {
ProgressMonitorDialog mon = new ProgressMonitorDialog(getShell());
mon.getProgressMonitor();
mon.setBlockOnOpen(false);
mon.open();
mon.getProgressMonitor().beginTask(MESSAGE_GETTING_CHILD, 2);
loadOneChildRelationship(selectedItem, mon.getProgressMonitor());
mon.close();
}
};
getSite().getShell().getDisplay().syncExec(getThisChildSchema);
}
} else if (SchemaBrowser.LOOKUP_RELATIONSHIP_NODE.equals(type)
&& Boolean.FALSE.equals(selectedItem.getData(LOADED))) {
Runnable getThisChildSchema = new Runnable() {
public void run() {
ProgressMonitorDialog mon = new ProgressMonitorDialog(getShell());
mon.getProgressMonitor();
mon.setBlockOnOpen(false);
mon.open();
mon.getProgressMonitor().beginTask(MESSAGE_GETTING_LOOKUP, 2);
loadOneChildRelationship(selectedItem, mon.getProgressMonitor());
mon.close();
}
};
getSite().getShell().getDisplay().asyncExec(getThisChildSchema);
}
setChildren(selectedItem, ((Integer) selectedItem.getData(IMAGE_TYPE)).intValue());
while (selectedItem.getParentItem() != null) {
TreeItem parent = selectedItem.getParentItem();
boolean setParent = true;
for (int i = 0; i < parent.getItemCount(); i++) {
if (!parent.getItem(i).equals(selectedItem)
&& parent.getItem(i).getImage().equals(imageChecked)) {
setParent = false;
break;
}
}
if (!setParent) {
break;
}
if (imageType.intValue() == 0) {
setItemChecked(parent);
} else {
setItemNotChecked(parent);
}
selectedItem = parent;
}
fireSelectionChanged(selectedItem);
}
}
}
}
}
wasExpanded = false;
}
public void mouseDoubleClick(org.eclipse.swt.events.MouseEvent e) {}
public void mouseDown(org.eclipse.swt.events.MouseEvent e) {}
});
initialize();
}
Shell getShell() {
return getSite().getShell();
}
void setChildren(TreeItem treeItem, int imageType) {
TreeItem[] children = treeItem.getItems();
for (TreeItem element : children) {
Integer iType = (Integer) element.getData(IMAGE_TYPE);
if (iType != null) {
if (imageType == 1) {
setItemChecked(element);
} else {
setItemNotChecked(element);
}
}
if (((Boolean) element.getData(HAS_CHECKABLE_CHILDREN)).booleanValue()) {
setChildren(element, imageType);
}
}
}
void fireSelectionChanged(TreeItem treeItem) {
SelectListChangedArguments slca = generateSOQL(treeItem);
if (slca == null) {
schemaEditorComposite.getTextSOQL().setText("");
} else {
String soql = "Select ";
ArrayList<String> fieldList = slca.getSelectedFields();
for (int i = 0; i < fieldList.size(); i++) {
String fieldName = fieldList.get(i);
soql += fieldName;
if (i < fieldList.size() - 1) {
soql += ", ";
}
}
soql += " From " + slca.getTableName();
schemaEditorComposite.getTextSOQL().setText(soql);
}
}
private TreeItem getPrimaryFieldsNode(TreeItem customObjectNode) {
TreeItem fieldsNode = null;
for (int i = 0; i < customObjectNode.getItemCount(); i++) {
TreeItem nodeChild = customObjectNode.getItem(i);
Integer nodeType = (Integer) nodeChild.getData(TYPE);
if (nodeType != null) {
if (SchemaBrowser.PRIMARY_OBJECT_FIELDS_NODE.equals(nodeType)
|| SchemaBrowser.CHILD_FIELDS_NODE.equals(nodeType)) {
fieldsNode = customObjectNode.getItem(i);
break;
}
}
}
return fieldsNode;
}
private TreeItem getChildRelationshipsNode(TreeItem customObjectNode) {
TreeItem childRNode = null;
for (int i = 0; i < customObjectNode.getItemCount(); i++) {
String nodeLabel = customObjectNode.getItem(i).getText();
if (NODE_LABEL_CHILD_RELATIONSHIPS.equals(nodeLabel)) {
childRNode = customObjectNode.getItem(i);
break;
}
}
return childRNode;
}
private TreeItem getReferenceToNode(TreeItem primaryField) {
TreeItem referenceToNode = null;
TreeItem returnNode = null;
for (int i = 0; i < primaryField.getItemCount(); i++) {
if (SchemaBrowser.DATA_TYPE_NODE.equals(primaryField.getItem(i).getData(TYPE))) {
referenceToNode = primaryField.getItem(i);
break;
}
}
if (referenceToNode != null) {
for (int i = 0; i < referenceToNode.getItemCount(); i++) {
if (SchemaBrowser.REFERENCE_TO_NODE.equals(referenceToNode.getItem(i).getData(TYPE))) {
returnNode = referenceToNode.getItem(i);
break;
}
}
}
return returnNode;
}
private TreeItem getReferenceToObjectFieldsNode(TreeItem referenceToObjectNode) {
TreeItem lookupFieldsNode = null;
for (int k = 0; k < referenceToObjectNode.getItemCount(); k++) {
TreeItem objectChildNode = referenceToObjectNode.getItem(k);
if (SchemaBrowser.LOOKUP_RELATIONSHIP_FIELDS_NODE.equals(referenceToObjectNode.getItem(k).getData(TYPE))) {
lookupFieldsNode = objectChildNode;
break;
}
}
return lookupFieldsNode;
}
private void checkLookupRelations(TreeItem primaryField, Stack<String> primaryFieldStack, String parentAlias) {
// We need to find the reference to node
TreeItem referenceToNode = getReferenceToNode(primaryField);
if (referenceToNode != null) {
for (int j = 0; j < referenceToNode.getItemCount(); j++) {
TreeItem cobject = referenceToNode.getItem(j);
if (cobject.getImage().equals(imageChecked)) {
// We have the lookup object, need to get to the fields node
TreeItem lookupFieldsNode = getReferenceToObjectFieldsNode(cobject);
if (lookupFieldsNode != null) {
for (int k = 0; k < lookupFieldsNode.getItemCount(); k++) {
TreeItem objectChildNode = lookupFieldsNode.getItem(k);
if (objectChildNode.getImage().equals(imageChecked)) {
primaryFieldStack.push(parentAlias + "." + objectChildNode.getData(FQN));
}
}
}
}
}
}
}
private void checkChildRelationships(TreeItem childRelationShipsNode, Stack<String> childRelationshipStack) {
if (childRelationShipsNode != null) {
for (int i = 0; i < childRelationShipsNode.getItemCount(); i++) {
TreeItem item = childRelationShipsNode.getItem(i);
if (item.getImage().equals(imageChecked)) {
TreeItem fields = null;
String from = ((ChildRelationship) item.getData("relationship")).getRelationshipName();
String subQuery = "Select ";
for (int j = 0; j < item.getItemCount(); j++) {
if ("Fields".equalsIgnoreCase(item.getItem(j).getText())) {
fields = item.getItem(j);
break;
}
}
if (fields != null) {
for (int j = 0; j < fields.getItemCount(); j++) {
if (fields.getItem(j).getImage().equals(imageChecked)) {
if ("Select ".equalsIgnoreCase(subQuery)) {
subQuery += ((Field) fields.getItem(j).getData("field")).getName();
} else {
subQuery += ", " + ((Field) fields.getItem(j).getData("field")).getName();
}
}
}
}
subQuery += " From " + from;
childRelationshipStack.push("(" + subQuery + ")");
}
}
}
}
/*
* How this works:
*
* 1. First, we need to get the top most node and that is the customObjectNode.
*
* 2. If the top most node is not checked then nothing else is and we can clear the SOQL
*
* 3. If the top most node is not check, then we need look for checked items
*
* 4. Fields can come from 3 places, the primary fields, child relationships and related records from reference to
* fields
*
* 5. Get the node that contains all the primary fields (PRIMARY_OBJECT_FIELDS_NODE)
*
* 6. Check to see if that is checked,
*/
private SelectListChangedArguments generateSOQL(TreeItem selectedItem) {
// 1
TreeItem customObjectNode = getTopOfBranch(selectedItem);
// 2
if (customObjectNode.getImage().equals(imageNotChecked)) {
return null;
}
// now we go to the fields node, which is a child of the root
TreeItem primaryFieldsNode = getPrimaryFieldsNode(customObjectNode);
// Get the childRelationships node in case we are doing a join
TreeItem childRelationShipsNode = getChildRelationshipsNode(customObjectNode);
// Initialize a stack to hold subqueries
Stack<String> childRelationshipStack = new Stack<String>();
Stack<String> primaryFieldsStack = new Stack<String>();
SelectListChangedArguments slca = null;
String parentAlias = customObjectNode.getText().toLowerCase().substring(0, 1);
// Loop through the primary fields and the referenceTo fields
for (int i = 0; i < primaryFieldsNode.getItemCount(); i++) {
// For each field node, we see if it is checked
TreeItem primaryField = primaryFieldsNode.getItem(i);
if (primaryField.getImage().equals(imageChecked)) {
// Since the field is checked, we will add it to the field list
primaryFieldsStack.push(parentAlias + "." + ((Field) primaryField.getData("field")).getName());
// Now we need to check for lookup relation fields
if (((Field) primaryField.getData("field")).getRelationshipName() != null) {
checkLookupRelations(primaryField, primaryFieldsStack, parentAlias);
}
}
}
// Look for subqueries based on child relationships
checkChildRelationships(childRelationShipsNode, childRelationshipStack);
if (!primaryFieldsStack.isEmpty() || !childRelationshipStack.isEmpty()) {
slca = new SelectListChangedArguments();
slca.setTableName(customObjectNode.getText() + " " + parentAlias);
while (!primaryFieldsStack.isEmpty()) {
slca.addField(primaryFieldsStack.pop());
}
while (!childRelationshipStack.isEmpty()) {
slca.addField(childRelationshipStack.pop());
}
}
return slca;
}
private TreeItem getTopOfBranch(TreeItem childItem) {
Boolean isTopLevel = (Boolean) childItem.getData(IS_TOP_LEVEL);
TreeItem parent;
if (isTopLevel.booleanValue()) {
return childItem;
}
parent = childItem;
while ((isTopLevel == null) || !isTopLevel.booleanValue()) {
parent = parent.getParentItem();
isTopLevel = (Boolean) parent.getData(IS_TOP_LEVEL);
}
return parent;
}
private Image getImage(int imageId, String label) {
return provider.getImage(imageId, label, schemaEditorComposite.getTree());
}
private TreeItem createItem(TreeItem item, String label, boolean check, int imageIndex) {
item.setText(label);
if (check) {
setItemNotChecked(item);
} else {
Image image = getImage(imageIndex, item.getText());
item.setImage(image);
}
return item;
}
private TreeItem createTreeItemChild(TreeItem parent, String label, boolean check, int imageIndex,
boolean hasCheckableChildren, Integer type) {
TreeItem item = createTreeItemChild(parent, label, check, imageIndex, hasCheckableChildren);
item.setData(TYPE, type);
item.setData(LOADED, Boolean.FALSE);
return item;
}
private TreeItem createTreeItemChild(TreeItem parent, String label, boolean check, int imageIndex,
boolean hasCheckableChildren) {
TreeItem treeItem = createItem(new TreeItem(parent, SWT.NONE), label, check, imageIndex);
treeItem.setData(IS_TOP_LEVEL, Boolean.FALSE);
treeItem.setData(HAS_CHECKABLE_CHILDREN, Boolean.valueOf(hasCheckableChildren));
treeItem.setData(TYPE, Integer.valueOf(TYPE_MINUSONE));
return treeItem;
}
private TreeItem createTreeChild(Tree parent, String label, boolean check, int imageIndex, Integer type) {
TreeItem ret = createTreeChild(parent, label, check, imageIndex);
ret.setData(TYPE, type);
return ret;
}
private TreeItem createTreeChild(Tree parent, String label, boolean check, int imageIndex) {
TreeItem treeItem = createItem(new TreeItem(parent, SWT.NONE), label, check, imageIndex);
treeItem.setData(IS_TOP_LEVEL, Boolean.TRUE);
treeItem.setData(HAS_CHECKABLE_CHILDREN, Boolean.TRUE);
treeItem.setData(TYPE, Integer.valueOf(TYPE_MINUSONE));
return treeItem;
}
void setItemChecked(TreeItem item) {
item.setImage(imageChecked);
item.setData(IMAGE_TYPE, Integer.valueOf(1));
}
void setItemNotChecked(TreeItem item) {
item.setImage(imageNotChecked);
item.setData(IMAGE_TYPE, Integer.valueOf(0));
}
void setIsTopLevel(TreeItem treeItem, Boolean isTopLevel) {
treeItem.setData(IS_TOP_LEVEL, isTopLevel);
}
void setHasCheckableChildren(TreeItem treeItem, Boolean hasCheckableChildren) {
treeItem.setData(HAS_CHECKABLE_CHILDREN, hasCheckableChildren);
}
private void loadObjectAccessData(TreeItem accessRoot, DescribeSObjectResult dr) {
if (dr.isActivateable()) {
createTreeItemChild(accessRoot, ACTIVATABLE, false, 0, false);
}
if (dr.isCreateable()) {
createTreeItemChild(accessRoot, CREATEABLE, false, 0, false);
}
if (dr.isCustom()) {
createTreeItemChild(accessRoot, CUSTOM, false, 0, false);
}
if (dr.isDeletable()) {
createTreeItemChild(accessRoot, DELETEABLE, false, 0, false);
}
if (dr.isLayoutable()) {
createTreeItemChild(accessRoot, LAYOUTABLE, false, 0, false);
}
if (dr.isQueryable()) {
createTreeItemChild(accessRoot, QUERYABLE, false, 0, false);
}
if (dr.isReplicateable()) {
createTreeItemChild(accessRoot, REPLICATABLE, false, 0, false);
}
if (dr.isRetrieveable()) {
createTreeItemChild(accessRoot, RETRIEVEABLE, false, 0, false);
}
if (dr.isSearchable()) {
createTreeItemChild(accessRoot, SEARCHABLE, false, 0, false);
}
if (dr.isUndeletable()) {
createTreeItemChild(accessRoot, UNDELETEABLE, false, 0, false);
}
if (dr.isUpdateable()) {
createTreeItemChild(accessRoot, UPDATEABLE, false, 0, false);
}
}
private void loadFieldAccessData(TreeItem fieldAccessRoot, Field field) {
if (field.isAutoNumber()) {
createTreeItemChild(fieldAccessRoot, AUTO_NUMBER, false, 0, false);
}
if (field.isCalculated()) {
createTreeItemChild(fieldAccessRoot, FORMULA, false, 0, false);
}
if (field.isCreateable()) {
createTreeItemChild(fieldAccessRoot, CREATEABLE, false, 0, false);
}
if (field.isCustom()) {
createTreeItemChild(fieldAccessRoot, CUSTOM, false, 0, false);
}
if (field.isDefaultedOnCreate()) {
createTreeItemChild(fieldAccessRoot, DEFAULTED_ON_CREATE, false, 0, false);
}
if (field.isFilterable()) {
createTreeItemChild(fieldAccessRoot, FILTERABLE, false, 0, false);
}
if (field.isNameField()) {
createTreeItemChild(fieldAccessRoot, NAME_FIELD, false, 0, false);
}
if (field.isNillable()) {
createTreeItemChild(fieldAccessRoot, NILLABLE, false, 0, false);
}
if (field.isRestrictedPicklist()) {
createTreeItemChild(fieldAccessRoot, RESTRICTED, false, 0, false);
}
if (field.isUpdateable()) {
createTreeItemChild(fieldAccessRoot, UPDATEABLE, false, 0, false);
}
}
private void loadPickListValues(TreeItem picklistRoot, PicklistEntry[] values) {
for (PicklistEntry entry : values) {
String label = entry.getLabel();
if (label == null) {
label = entry.getValue();
}
if (entry.isDefaultValue()) {
label += " (default)";
}
TreeItem entryRoot = createTreeItemChild(picklistRoot, label, false, 0, false);
if (entry.getLabel() != null) {
createTreeItemChild(entryRoot, "Label: " + entry.getLabel(), false, 0, false);
}
createTreeItemChild(entryRoot, "Value: " + entry.getValue(), false, 0, false);
createTreeItemChild(entryRoot, "Active: " + Boolean.valueOf(entry.isActive()).toString(), false, 0, false);
createTreeItemChild(entryRoot, "Default Value: " + Boolean.valueOf(entry.isDefaultValue()).toString(),
false, 0, false);
}
}
private void loadReferenceTo(TreeItem referenceToRoot, String[] refTo) {
for (String element : refTo) {
TreeItem referenceTo =
createTreeItemChild(referenceToRoot, element, true, 0, true, SchemaBrowser.LOOKUP_RELATIONSHIP_NODE);
createTreeItemChild(referenceTo, " ", false, 0, false);
}
}
private void loadFieldTypeData(TreeItem fieldTypeRoot, Field field) {
String fieldType = field.getType().toString();
String ext = "";
if (field.getExternalId()) {
ext = " (External Id)";
}
fieldTypeRoot.setText(fieldTypeRoot.getText() + " - " + fieldType + ext);
createTreeItemChild(fieldTypeRoot, "Soap Type: " + field.getSoapType(), false, 0, false);
if ("int".equalsIgnoreCase(fieldType)) {
createTreeItemChild(fieldTypeRoot, "digits: " + Integer.valueOf(field.getDigits()).toString(), false, 0,
false);
}
if ("double".equalsIgnoreCase(fieldType) || "currency".equalsIgnoreCase(fieldType)) {
createTreeItemChild(fieldTypeRoot, "precision: " + Integer.valueOf(field.getPrecision()).toString(), false,
0, false);
createTreeItemChild(fieldTypeRoot, "scale: " + Integer.valueOf(field.getScale()).toString(), false, 0,
false);
}
if ("percent".equalsIgnoreCase(fieldType)) {
createTreeItemChild(fieldTypeRoot, "precision: " + Integer.valueOf(field.getPrecision()).toString(), false,
0, false);
}
if ("string".equalsIgnoreCase(fieldType) || "textarea".equalsIgnoreCase(fieldType)
|| "phone".equalsIgnoreCase(fieldType) || "id".equalsIgnoreCase(fieldType)
|| "url".equalsIgnoreCase(fieldType) || "email".equalsIgnoreCase(fieldType)) {
createTreeItemChild(fieldTypeRoot, "length: " + Integer.valueOf(field.getLength()).toString(), false, 0,
false);
createTreeItemChild(fieldTypeRoot, "byte length: " + Integer.valueOf(field.getByteLength()).toString(),
false, 0, false);
}
if ("picklist".equalsIgnoreCase(fieldType) || "mutlipicklist".equalsIgnoreCase(fieldType)
|| "combobox".equalsIgnoreCase(fieldType)) {
createTreeItemChild(fieldTypeRoot, "Length: " + Integer.valueOf(field.getLength()).toString(), false, 0,
false);
createTreeItemChild(fieldTypeRoot, "Byte Length: " + Integer.valueOf(field.getByteLength()).toString(),
false, 0, false);
if (field.getControllerName() != null) {
createTreeItemChild(fieldTypeRoot, "Controller: " + field.getControllerName(), false, 0, false);
}
TreeItem picklistRoot = createTreeItemChild(fieldTypeRoot, "Picklist Values", false, 12, false);
if (field.getDependentPicklist()) {
createTreeItemChild(picklistRoot, "Dependent Picklist", false, 0, false);
}
if (field.getPicklistValues() != null) {
loadPickListValues(picklistRoot, field.getPicklistValues());
}
}
if ("reference".equalsIgnoreCase(fieldType)) {
setHasCheckableChildren(fieldTypeRoot, Boolean.TRUE);
createTreeItemChild(fieldTypeRoot, "Length: " + Integer.valueOf(field.getLength()).toString(), false, 0,
false);
createTreeItemChild(fieldTypeRoot, "Byte Length: " + Integer.valueOf(field.getByteLength()).toString(),
false, 0, false);
TreeItem referenceToRoot =
createTreeItemChild(fieldTypeRoot, "Reference To", false, 0, true, SchemaBrowser.REFERENCE_TO_NODE);
loadReferenceTo(referenceToRoot, field.getReferenceTo());
}
}
private void loadFieldsData(TreeItem fieldsRoot, DescribeSObjectResult dr, IProgressMonitor monitor) {
Field[] fields = dr.getFields();
TreeItem fieldRoot;
Arrays.sort(fields, new FieldComparator());
for (Field element : fields) {
if (monitor != null) {
monitor.worked(1);
}
Field field = element;
String fldName = field.getName();
fldName += " - " + field.getType();
if (field.isCustom()) {
fldName += " (custom)";
}
fieldRoot = createTreeItemChild(fieldsRoot, fldName, true, 0, false, SchemaBrowser.PRIMARY_ROOT_FIELD);
fieldRoot.setData("field", element);
if (field.getHtmlFormatted()) {
createTreeItemChild(fieldRoot, "Field is HTML Formatted", false, 0, false);
}
if (field.getRelationshipName() != null) {
createTreeItemChild(fieldRoot, "Foreign Key: " + field.getRelationshipName(), false, 0, false);
}
TreeItem fieldAccessRoot = createTreeItemChild(fieldRoot, "Access", false, 0, false);
loadFieldAccessData(fieldAccessRoot, field);
if (field.getLabel() != null) {
TreeItem labelRoot = createTreeItemChild(fieldRoot, "Label", false, 0, false);
createTreeItemChild(labelRoot, field.getLabel(), false, 0, false);
}
TreeItem fieldTypeRoot =
createTreeItemChild(fieldRoot, "Type Data", false, 0, false, SchemaBrowser.DATA_TYPE_NODE);
loadFieldTypeData(fieldTypeRoot, field);
}
}
void loadOneChildRelationship(TreeItem crRoot, IProgressMonitor monitor) {
try {
Integer childFieldType = SchemaBrowser.LOOKUP_RELATIONSHIP_FIELD;
Integer childFieldsNode = SchemaBrowser.LOOKUP_RELATIONSHIP_FIELDS_NODE;
String fieldPrefix = "";
if (SchemaBrowser.PRIMARY_ROOT_FIELD.equals(crRoot.getData(TYPE))) {
crRoot = getReferenceToNode(crRoot);
}
if (SchemaBrowser.LOOKUP_RELATIONSHIP_NODE.equals(crRoot.getData(TYPE))
|| SchemaBrowser.PRIMARY_ROOT_FIELD.equals(crRoot.getData(TYPE))) {
crRoot.removeAll();
childFieldType = SchemaBrowser.LOOKUP_RELATIONSHIP_FIELD;
if (SchemaBrowser.PRIMARY_ROOT_FIELD.equals(crRoot.getData(TYPE))) {
fieldPrefix = ((Field) crRoot.getData("field")).getRelationshipName();
} else {
fieldPrefix =
((Field) crRoot.getParentItem().getParentItem().getParentItem().getData("field"))
.getRelationshipName();
}
} else {
childFieldType = SchemaBrowser.CHILD_RELATIONSHIP_FIELD;
childFieldsNode = SchemaBrowser.CHILD_FIELDS_NODE;
fieldPrefix = ((ChildRelationship) crRoot.getData("relationship")).getRelationshipName();
}
DescribeSObjectResult dr = null;
IProject project = file.getProject();
Connection connection = getConnectionFactory().getConnection(project);
// get describe object w/o client id
dr = connection.describeSObject(crRoot.getText(), false);
Field[] fields = dr.getFields();
TreeItem childFields = createTreeItemChild(crRoot, "Fields", true, 0, true, childFieldsNode);
SubProgressMonitor spm = new SubProgressMonitor(monitor, 1);
spm.beginTask("Getting relationship definitions...", fields.length);
for (Field element : fields) {
if (spm != null) {
spm.worked(1);
}
spm.subTask("Getting " + element.getLabel() + " definition...");
TreeItem thisChild =
createTreeItemChild(childFields, element.getLabel(), true, 0, false, childFieldType);
thisChild.setData(FQN, fieldPrefix + "." + element.getName());
thisChild.setData("field", element);
}
crRoot.setData(LOADED, Boolean.TRUE);
spm.done();
} catch (Exception e) {
Utils.openError(e, MESSAGE_COULD_NOT_FETCH_META_DATA, MESSAGE_SEE_DETAILS);
}
}
private void loadChildRelationships(TreeItem crRoot, ChildRelationship[] relationships, IProgressMonitor monitor) {
for (ChildRelationship cr : relationships) {
if (monitor != null) {
monitor.worked(1);
}
TreeItem child;
if (cr.getRelationshipName() != null) {
child =
createTreeItemChild(crRoot, cr.getChildSObject(), true, 0, true,
SchemaBrowser.CHILD_RELATIONSHIP_NODE);
child.setData("relationship", cr);
} else {
child = createTreeItemChild(crRoot, cr.getChildSObject(), false, 0, false);
}
if (cr.isCascadeDelete()) {
createTreeItemChild(child, "Cascade Delete", false, 0, false);
}
if (cr.getRelationshipName() != null) {
monitor.subTask(cr.getRelationshipName());
createTreeItemChild(child, "Relationship Name: " + cr.getRelationshipName(), false, 0, false);
createTreeItemChild(child, "Related Field: " + cr.getField(), false, 0, false);
}
}
}
private void loadFrontDoorUrls(TreeItem objectRoot, DescribeSObjectResult dr) {
if ((dr.getUrlDetail() != null) || (dr.getUrlEdit() != null) || (dr.getUrlNew() != null)) {
TreeItem frontDoor = createTreeItemChild(objectRoot, "Frontdoor URLS", false, 0, false);
if (dr.getUrlDetail() != null) {
TreeItem urlD = createTreeItemChild(frontDoor, "URL - Detail", false, 0, false);
createTreeItemChild(urlD, dr.getUrlDetail(), false, 0, false);
}
if (dr.getUrlEdit() != null) {
TreeItem urlD = createTreeItemChild(frontDoor, "URL - Edit", false, 0, false);
createTreeItemChild(urlD, dr.getUrlEdit(), false, 0, false);
}
if (dr.getUrlNew() != null) {
TreeItem urlD = createTreeItemChild(frontDoor, "URL - New", false, 0, false);
createTreeItemChild(urlD, dr.getUrlNew(), false, 0, false);
}
}
}
void loadObject(String type, IProgressMonitor monitor) {
try {
dr = getCachedDescribe(type);
} catch (Exception e) {
Utils.openError(e, MESSAGE_COULD_NOT_FETCH_META_DATA, MESSAGE_SEE_DETAILS);
}
}
void loadTreeData(TreeItem objectRoot, Composite composite, IProgressMonitor monitor) {
Cursor wait_cursor = new Cursor(composite.getDisplay(), SWT.CURSOR_WAIT);
composite.setCursor(wait_cursor);
objectRoot.removeAll();
try {
if (dr.getKeyPrefix() != null) {
createTreeItemChild(objectRoot, "ID Prefix: " + dr.getKeyPrefix(), false, 0, false);
}
loadFrontDoorUrls(objectRoot, dr);
TreeItem labelsRoot = createTreeItemChild(objectRoot, "Labels", false, 0, false);
createTreeItemChild(labelsRoot, "Singular: " + dr.getLabel(), false, 0, false);
createTreeItemChild(labelsRoot, "Plural: " + dr.getLabelPlural(), false, 0, false);
TreeItem accessRoot = createTreeItemChild(objectRoot, "Access", false, 0, false);
loadObjectAccessData(accessRoot, dr);
TreeItem fieldsRoot =
createTreeItemChild(objectRoot, "Fields - " + dr.getFields().length, true, 0, true,
SchemaBrowser.PRIMARY_OBJECT_FIELDS_NODE);
int monitorTotal = dr.getFields().length;
if (dr.getChildRelationships() != null) {
monitorTotal += dr.getChildRelationships().length;
}
monitor.beginTask(MESSAGE_GETTING_CHILD_FIELDS, monitorTotal);
loadFieldsData(fieldsRoot, dr, monitor);
if (dr.getChildRelationships() != null) {
TreeItem childRelsRoot =
createTreeItemChild(objectRoot, NODE_LABEL_CHILD_RELATIONSHIPS, false, 0, false);
loadChildRelationships(childRelsRoot, dr.getChildRelationships(), monitor);
}
} catch (Exception e) {
Utils.openError(e, MESSAGE_COULD_NOT_FETCH_META_DATA, MESSAGE_SEE_DETAILS);
}
if (monitor != null) {
monitor.done();
}
wait_cursor.dispose();
composite.setCursor(null);
}
private DescribeSObjectResult getCachedDescribe(String componentType) throws ForceConnectionException,
ForceProjectException, ForceRemoteException {
if (!describeCache.containsKey(componentType.toLowerCase())) {
IProject project = file.getProject();
Connection connection = getConnectionFactory().getConnection(project);
// get describe object w/o client id
DescribeSObjectResult describeSObject = connection.describeSObject(componentType, false);
describeCache.put(componentType.toLowerCase(), describeSObject);
}
return describeCache.get(componentType.toLowerCase());
}
void initialize() {
Cursor wait_cursor = new Cursor(schemaEditorComposite.getDisplay(), SWT.CURSOR_WAIT);
schemaEditorComposite.setCursor(wait_cursor);
IProject project = file.getProject();
try {
Connection connection = getConnectionFactory().getConnection(project);
// get types w/o client id
String[] types = connection.retrieveTypes(false);
if (Utils.isNotEmpty(types) && schemaEditorComposite != null && schemaEditorComposite.getTree() != null
&& schemaEditorComposite.getTree().getItemCount() > 0) {
schemaEditorComposite.getTree().removeAll();
}
clearDescribeCache(project);
for (int i = 0; i < types.length; i++) {
// temp remove
if ("ApexPage".equals(types[i])) {
continue;
}
createTreeItemChild(createTreeChild(schemaEditorComposite.getTree(), types[i], false, 0,
SchemaBrowser.PRIMARY_ROOT_NODE), "", false, 0, false);
}
} catch (Exception e) {
Utils.openError(e, MESSAGE_COULD_NOT_FETCH_META_DATA, MESSAGE_SEE_DETAILS);
}
wait_cursor.dispose();
schemaEditorComposite.setCursor(null);
}
private void clearDescribeCache(IProject project) throws ForceConnectionException, ForceRemoteException, ForceProjectException {
// Refresh the DescribeSObject cache and also the DescribeObjectRegistry
describeCache.clear();
ContainerDelegate.getInstance().getServiceLocator().getProjectService().getDescribeObjectRegistry().refresh(project);
}
void fillTable(QueryResult qr, IProgressMonitor monitor, boolean clearTable) throws InvalidSObjectFault,
UnexpectedErrorFault, RemoteException {
schemaEditorComposite.loadTable(qr);
monitor.done();
}
public SchemaEditorComposite getSchemaEditorComposite() {
return schemaEditorComposite;
}
}