/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.vdb;
import static org.teiid.designer.vdb.Vdb.Event.MODEL_TRANSLATOR;
import static org.teiid.designer.vdb.Vdb.Event.MODEL_VISIBLE;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.ZipOutputStream;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.teiid.core.designer.util.CoreArgCheck;
import org.teiid.core.designer.util.CoreStringUtil;
import org.teiid.core.designer.util.StringUtilities;
import org.teiid.designer.core.ModelerCore;
import org.teiid.designer.core.resource.EmfResource;
import org.teiid.designer.core.translators.TranslatorOverrideProperty;
import org.teiid.designer.core.translators.TranslatorPropertyDefinition;
import org.teiid.designer.core.util.VdbHelper;
import org.teiid.designer.core.workspace.ModelResource;
import org.teiid.designer.core.workspace.ModelUtil;
import org.teiid.designer.core.workspace.ModelWorkspaceException;
import org.teiid.designer.datatools.connection.ConnectionInfoHelper;
import org.teiid.designer.extension.ExtensionPlugin;
import org.teiid.designer.extension.definition.ModelObjectExtensionAssistant;
import org.teiid.designer.metamodels.function.FunctionPlugin;
import org.teiid.designer.metamodels.function.ScalarFunction;
import org.teiid.designer.metamodels.function.extension.FunctionModelExtensionConstants;
import org.teiid.designer.metamodels.relational.Procedure;
import org.teiid.designer.metamodels.relational.RelationalPlugin;
import org.teiid.designer.metamodels.relational.extension.RelationalModelExtensionConstants;
import org.teiid.designer.vdb.manifest.ModelElement;
import org.teiid.designer.vdb.manifest.ProblemElement;
import org.teiid.designer.vdb.manifest.PropertyElement;
import org.teiid.designer.vdb.manifest.SourceElement;
import net.jcip.annotations.ThreadSafe;
/**
*
*
* @since 8.0
*/
@ThreadSafe
public class VdbModelEntry extends VdbIndexedEntry {
/**
* @param path the model path (may not be <code>null</code>)
* @return the default name to use as the source name (never <code>null</code>)
*/
public static String createDefaultSourceName(IPath path) {
return path.removeFileExtension().lastSegment();
}
private String modelUuid;
private final AtomicBoolean visible = new AtomicBoolean(true);
final CopyOnWriteArraySet<VdbEntry> imports = new CopyOnWriteArraySet<VdbEntry>();
final CopyOnWriteArraySet<VdbModelEntry> importedBy = new CopyOnWriteArraySet<VdbModelEntry>();
final CopyOnWriteArraySet<String> importVdbNames = new CopyOnWriteArraySet<String>();
final CopyOnWriteArraySet<VdbFileEntry> udfJars = new CopyOnWriteArraySet<VdbFileEntry>();
private final String modelClass;
private final boolean builtIn;
private final String type;
final VdbSourceInfo sourceInfo;
private transient ModelElement element;
private final String schemaText;
private final String metadataType;
/**
* Constructs a model entry and adds it to the specified VDB. <strong>Callers of this method should call
* {@link #synchronizeModelEntry()} immediately after constructing the model entry.</strong>
*
* @param vdb the VDB where the resource is be added to (may not be <code>null</code>)
* @param path the resource path (may not be <code>null</code>)
* @throws Exception
*/
public VdbModelEntry( final XmiVdb vdb, final IPath path) throws Exception {
super(vdb, path);
final Resource model = findModel();
builtIn = getFinder().isBuiltInResource(model);
modelClass = findModelClass(model);
sourceInfo = new VdbSourceInfo(vdb);
if (ModelUtil.isXmiFile(model)) {
final ModelResource mr = ModelerCore.getModelEditor().findModelResource(model);
final EmfResource emfModel = (EmfResource)model;
type = emfModel.getModelType().getName();
modelUuid = ModelUtil.getUuidString(mr);
// TODO: Backing out the auto-set visibility to FALSE for physical models (Preview won't work)
// visible.set(false);
// TODO: re-visit in 7.1
// For now, we're removing the assumption that the user will want to seed the VDB model entry with the model's
// Description. From a UI standpoint, if the description contains multiple lines, then the row height
// is way too high. User can always copy/paste from model to VDB AND the description for a model is always
// available in the model itself.
if (emfModel.getDescription() != null) setDescription(emfModel.getDescription());
if (ModelUtil.isPhysical(model)) {
final String defaultName = createDefaultSourceName(path);
final ConnectionInfoHelper helper = new ConnectionInfoHelper();
String translator = helper.getTranslatorName(mr);
if( translator == null ) translator = EMPTY_STRING;
// Jndi defaults to source name, unless the property is found in the model.
String jndiName = defaultName;
String jndiProp = helper.getJndiProperty(mr);
if(!CoreStringUtil.isEmpty(jndiProp)) {
jndiName = jndiProp;
}
sourceInfo.add(defaultName, jndiName, translator);
Properties translatorProps = helper.getTranslatorOverrideProperties(mr);
if( !translatorProps.isEmpty() ) {
updateTranslatorOverrides(translatorProps);
}
}
// TODO: Backing out the auto-set visibility to FALSE for physical models (Preview won't work)
// if( ModelUtil.isVirtual(emfModel) ) {
visible.set(true);
// }
} else {
type = VdbUtil.OTHER;
}
schemaText = null;
metadataType = null;
}
private void updateUdfJars(Resource model) throws Exception {
if(model==null) return;
final ModelResource mdlResrc = ModelerCore.getModelEditor().findModelResource(model);
if (mdlResrc == null) return;
udfJars.clear();
// Find available udf jar resources in the project
IProject project = mdlResrc.getModelProject().getProject();
List<IResource> jarResources = VdbHelper.getUdfJarResources(project);
// Get all scalar functions in the Model, then update the jarPaths
List<EObject> children = model.getContents();
for(EObject eObj: children) {
// Look for ScalarFunctions and Procedure in View Model with function=true
String udfJarPath = null;
if(eObj instanceof ScalarFunction) {
udfJarPath = getUdfJarPath((ScalarFunction)eObj);
} else if(eObj instanceof Procedure && ((Procedure)eObj).isFunction()) {
udfJarPath = getUdfJarPath((Procedure)eObj);
}
if(udfJarPath!=null && udfJarPath.trim().length()!=0) {
// Go thru available jar resources
for(IResource jarResource: jarResources) {
if(jarResource instanceof IFile) {
IPath path = ((IFile)jarResource).getProjectRelativePath();
if(path.toString().equals(udfJarPath)) {
udfJars.add(new VdbFileEntry(getVdb(), jarResource.getFullPath(), VdbFileEntry.FileEntryType.UDFJar));
}
}
}
}
}
}
/**
* Get the Udf jarPath property from the supplied ScalarFunction
* @param scalarFunc the supplied ScalarFunction
* @return the Udf jarPath property value
*/
public static String getUdfJarPath(final ScalarFunction scalarFunc) {
String udfJarPath = null;
ModelObjectExtensionAssistant assistant = (ModelObjectExtensionAssistant)ExtensionPlugin.getInstance().getRegistry().getModelExtensionAssistant(FunctionModelExtensionConstants.NAMESPACE_PROVIDER.getNamespacePrefix());
if(assistant!=null) {
try {
udfJarPath = assistant.getPropertyValue(scalarFunc, FunctionModelExtensionConstants.PropertyIds.UDF_JAR_PATH);
} catch (Exception ex) {
ModelerCore.Util.log(IStatus.ERROR,ex,FunctionPlugin.Util.getString("FunctionUtil.ErrorGettingJarPath", scalarFunc.getName())); //$NON-NLS-1$
}
}
return udfJarPath;
}
/**
* Get the Udf jarPath property from the supplied ScalarFunction
* @param proc the supplied Procedure
* @return the Udf jarPath property value
*/
public static String getUdfJarPath(final Procedure proc) {
String udfJarPath = null;
ModelObjectExtensionAssistant assistant = (ModelObjectExtensionAssistant)ExtensionPlugin.getInstance().getRegistry().getModelExtensionAssistant(RelationalModelExtensionConstants.NAMESPACE_PROVIDER.getNamespacePrefix());
if(assistant!=null) {
try {
udfJarPath = assistant.getPropertyValue(proc, RelationalModelExtensionConstants.PropertyIds.UDF_JAR_PATH);
} catch (Exception ex) {
String msg = RelationalPlugin.Util.getString("ProcedureVirtualFunctionRule.errorGettingJarPath", proc.getName()); //$NON-NLS-1$
RelationalPlugin.Util.log(IStatus.ERROR,ex,msg);
}
}
return udfJarPath;
}
/**
* @param vdb
* @param element
* @throws Exception
*/
public VdbModelEntry( final XmiVdb vdb,
final ModelElement element ) throws Exception {
super(vdb, element);
this.element = element;
type = element.getType();
visible.set(element.isVisible());
sourceInfo = new VdbSourceInfo(vdb);
if( element.getMetadata() != null && !element.getMetadata().isEmpty()) {
schemaText = element.getMetadata().get(0).getSchemaText();
metadataType = element.getMetadata().get(0).getType();
} else {
schemaText = null;
metadataType = null;
}
if (element.getSources() != null && !element.getSources().isEmpty()) {
for (final SourceElement source : element.getSources()) {
sourceInfo.add(source.getName(), source.getJndiName(),
source.getTranslatorName() == null ? EMPTY_STRING : source.getTranslatorName());
}
}
if(VdbUtil.FUNCTION.equals(type) || VdbUtil.VIRTUAL.equals(type) || VdbUtil.PHYSICAL.equals(type)) {
updateUdfJars(findModel());
}
for (final ProblemElement problem : element.getProblems())
addProblem(new Problem(problem));
boolean builtIn = false;
String modelClass = null;
for (final PropertyElement property : element.getProperties()) {
final String name = property.getName();
if (ModelElement.BUILT_IN.equals(name)) builtIn = Boolean.parseBoolean(property.getValue());
else if (ModelElement.MODEL_CLASS.equals(name)) modelClass = property.getValue();
else if (ModelElement.MODEL_UUID.equals(name)) modelUuid = property.getValue();
else if (ModelElement.IMPORT_VDB_REFERENCE.equals(name)) {
importVdbNames.add(property.getValue());
} else if( ModelElement.SUPPORTS_MULTI_SOURCE.equals(name) || ModelElement.MULTI_SOURCE.equals(name) ) {
sourceInfo.setIsMultiSource(Boolean.parseBoolean(property.getValue()));
} else if( ModelElement.MULTI_SOURCE_ADD_COLUMN.equals(name)) {
sourceInfo.setAddColumn(Boolean.parseBoolean(property.getValue()));
} else if( ModelElement.MULTI_SOURCE_COLUMN_ALIAS.equals(name)) {
sourceInfo.setColumnAlias(property.getValue());
} else {
setProperty(name, property.getValue());
}
}
this.builtIn = builtIn;
this.modelClass = modelClass;
getVdb().synchronizeUdfJars(udfJars);
}
@Override
protected void clean() {
super.clean();
// Clear set of imports and inverse relationships
for (final VdbEntry entry : imports) {
if (entry instanceof VdbModelEntry) {
VdbModelEntry vdbModelEntry = (VdbModelEntry) entry;
vdbModelEntry.importedBy.remove(this);
if (vdbModelEntry.isBuiltIn())
entry.dispose();
}
}
imports.clear();
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.vdb.VdbEntry#dispose()
*/
@Override
public final void dispose() {
super.dispose();
// remove the imported by models
Collection<VdbModelEntry> importedByModels = new ArrayList<VdbModelEntry>(importedBy);
for (final VdbModelEntry entry : importedByModels) {
importedBy.remove(entry);
getVdb().removeEntry(entry);
}
clean();
}
private String findModelClass(Resource resource) throws Exception {
return ModelUtil.getModelClass(resource);
}
/**
* Determine if the resource for this entry contains any User-Defined functions. Currently this includes:
* 1) FunctionModel with ScalarFunctions and 2) Relational Procedures where function=true
* @return 'true' if the Model resource contains a User-Defined function, 'false' if not.
* @throws Exception
*/
public final boolean containsUdf() throws Exception {
boolean hasUdf = false;
// If its a FunctionModel it has ScalarFunctions/Udfs in it
if(VdbUtil.FUNCTION.equals(getType())) {
hasUdf = true;
// If its a relational View Model, see if it has any procedures with function=true
} else {
Resource modelResc = findModel();
boolean isRelational = false;
isRelational = ModelUtil.getModelClass(modelResc).equals(ModelUtil.MODEL_CLASS_RELATIONAL);
if(isRelational) {
try {
final ModelResource mr = ModelerCore.getModelEditor().findModelResource(modelResc);
@SuppressWarnings("unchecked")
List<EObject> eObjs = mr.getEObjects();
for(EObject eObj: eObjs) {
if(eObj instanceof Procedure && ((Procedure)eObj).isFunction()) {
hasUdf = true;
break;
}
}
} catch (ModelWorkspaceException ex) {
// Nothing to do
}
}
}
return hasUdf;
}
/**
* @return the immutable set of model entries that import this model entry
*/
public final Set<VdbModelEntry> getImportedBy() {
return Collections.unmodifiableSet(importedBy);
}
/**
* @return the immutable set of model entries imported by this model entry
*/
public final Set<? extends VdbEntry> getImports() {
return Collections.unmodifiableSet(imports);
}
/**
* @return the immutable set of VDB name strings referenced by this model entry
*/
public final Set<String> getImportVdbNames() {
return Collections.unmodifiableSet(importVdbNames);
}
/**
* @return the immutable set of VDB Udf File entries used by this model entry
*/
public final Set<VdbFileEntry> getUdfJars() {
return Collections.unmodifiableSet(udfJars);
}
/**
* @return the <code>VdbSourceInfo</code> object
*/
public VdbSourceInfo getSourceInfo() {
return this.sourceInfo;
}
/**
* @return type
*/
public String getType() {
return type;
}
/**
*
*/
public void initializeImports() {
for (final PropertyElement property : element.getProperties()) {
if (ModelElement.IMPORTS.equals(property.getName())) {
for (final VdbEntry entry : getVdb().getModelEntries()) {
if (property.getValue().equals(entry.getPath().toOSString())) {
((VdbModelEntry)entry).importedBy.add(this);
imports.add(entry);
break;
}
}
}
}
element = null;
}
/**
* @return model uuid
*/
public final String getModelUuid() {
return modelUuid;
}
/**
* @return model class
*/
public final String getModelClass() {
return modelClass;
}
/**
* @return <code>true</code> if the associated model is a hidden built-in model.
*/
public final boolean isBuiltIn() {
return builtIn;
}
/**
* @return <code>true</code> if the associated model will be directly accessible to users.
*/
public final boolean isVisible() {
return visible.get();
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.vdb.VdbEntry#save(java.util.zip.ZipOutputStream)
*/
@Override
public final void save( final ZipOutputStream out) throws Exception {
super.save(out);
}
/**
* @param index
* @param name
*/
public void setJndiName( int index, String name ) {
CoreArgCheck.isTrue(index < sourceInfo.getSourceCount(), "index out of range"); //$NON-NLS-1$
if (StringUtilities.isEmpty(name)) name = null;
String oldName = sourceInfo.getSource(index).getJndiName();
if (StringUtilities.equals(name, oldName)) return;
sourceInfo.getSource(index).setJndiName(name);
}
/**
* @param index
* @param name
*/
public final void setSourceName( int index, String name ) {
CoreArgCheck.isTrue(index < sourceInfo.getSourceCount(), "index out of range"); //$NON-NLS-1$
if (StringUtilities.isEmpty(name)) name = null;
String oldName = sourceInfo.getSource(index).getName();
if (StringUtilities.equals(name, oldName)) return;
sourceInfo.getSource(index).setName(name);
}
/**
* @param index
* @param name
*/
public final void setTranslatorName( int index, String name ) {
CoreArgCheck.isTrue(index < sourceInfo.getSourceCount(), "index out of range"); //$NON-NLS-1$
if (StringUtilities.isEmpty(name)) name = null;
String oldName = sourceInfo.getSource(index).getTranslatorName();
if (StringUtilities.equals(name, oldName)) return;
sourceInfo.getSource(index).setTranslatorName(name);
}
/**
* @return schema text
*/
public String getSchemaText() {
return schemaText;
}
/**
* @return metadata type
*/
public final String getMetadataType() {
return metadataType;
}
/**
* Returns the current <code>TranslatorOverride</code> for this model
* @return translator override. May be null.
*/
public final TranslatorOverride getTranslatorOverride() {
if( !this.sourceInfo.isEmpty() ) {
Collection<TranslatorOverride> overrides = getVdb().getTranslators();
for( TranslatorOverride to : overrides) {
for( VdbSource source : this.sourceInfo.getSources() ) {
String translatorName = source.getTranslatorName();
if( translatorName != null && translatorName.toString().equalsIgnoreCase(to.getType()) ) {
return to;
}
}
}
}
return null;
}
/*
* Only called by synchronize method or vdb creation. Intent is to update the TO for a given model based on
* injected translator properties.
*
* 1) if matching property found, set the new value
* 2) if no matching property found, add a new one
* 3) No way to tell if an OLD property needs to get removed though
*/
void updateTranslatorOverrides(Properties props) {
// TODO: Update for Multi-Sources bindings
if( this.sourceInfo.isMultiSource() ) {
return;
}
// If only ONE property and it's "name", then ignore
if( props.size() == 1 && ((String)props.keySet().toArray()[0]).equalsIgnoreCase(VdbConstants.TRANSLATOR_NAME_KEY) ) {
return;
}
TranslatorOverride to = getTranslatorOverride();
String oldTranslator = this.sourceInfo.getSource(0).getTranslatorName();
String newTranslator = null;
if( to == null ) {
String toName = null;
if( !oldTranslator.startsWith(this.sourceInfo.getSource(0).getName()) ) {
toName = this.sourceInfo.getSource(0).getName() + '_' + oldTranslator;
}
to = new TranslatorOverride(getVdb(), toName, oldTranslator, null);
newTranslator = toName;
this.sourceInfo.getSource(0).setTranslatorName(toName);
getVdb().addTranslator(to);
}
TranslatorOverrideProperty[] toProps = to.getOverrideProperties();
Set<Object> keys = props.keySet();
for (Object nextKey : keys) {
boolean existing = "name".equals(nextKey); //$NON-NLS-1$
// Look through current TO props to see if already defined
for( TranslatorOverrideProperty toProp : toProps ) {
if( toProp.getDefinition().getId().equals(nextKey) ) {
// This is an override case
toProp.setValue(props.getProperty((String)nextKey));
existing = true;
break;
}
}
if( !existing ) {
to.addProperty(new TranslatorOverrideProperty(new TranslatorPropertyDefinition((String) nextKey, "dummy"), props.getProperty((String)nextKey))); //$NON-NLS-1$
}
}
getVdb().setModified(this, MODEL_TRANSLATOR, oldTranslator, newTranslator);
}
/**
* @param visible <code>true</code> if the associated model will be directly accessible to users.
*/
public final void setVisible( final boolean visible ) {
final boolean oldVisible = isVisible();
if (oldVisible == visible) return;
this.visible.set(visible);
getVdb().setModified(this, MODEL_VISIBLE, oldVisible, visible);
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.vdb.VdbEntry#synchronize()
*/
@Override
public void synchronize() throws Exception {
if (getSynchronization() != Synchronization.NotSynchronized)
return;
synchronizeModelEntry();
super.synchronize();
}
/**
* @throws Exception
*/
public void synchronizeModelEntry() throws Exception {
final IFile workspaceFile = findFileInWorkspace();
if (workspaceFile == null)
return;
clean();
final Resource model = findModel();
if (ModelUtil.isPhysical(model)) {
if (!this.getSourceInfo().isMultiSource()) {
final ModelResource mr = ModelerCore.getModelEditor().findModelResource(model);
final ConnectionInfoHelper helper = new ConnectionInfoHelper();
final String translatorName = this.sourceInfo.getSource(0).getTranslatorName();
final String resourceTranslatorName = helper.getTranslatorName(mr);
if (!CoreStringUtil.isEmpty(resourceTranslatorName)
&& !CoreStringUtil.equals(translatorName, resourceTranslatorName)) {
this.sourceInfo.getSource(0).setTranslatorName(resourceTranslatorName == null ? EMPTY_STRING : resourceTranslatorName);
}
Properties translatorProps = helper.getTranslatorProperties(mr);
if (!translatorProps.isEmpty()) {
updateTranslatorOverrides(translatorProps);
}
final String jndiName = this.sourceInfo.getSource(0).getJndiName();
final String resourceJndiName = helper.getJndiProperty(mr);
if (!CoreStringUtil.isEmpty(resourceJndiName) && !CoreStringUtil.equals(jndiName, resourceJndiName)) {
this.sourceInfo.getSource(0).setJndiName(resourceJndiName == null ? EMPTY_STRING : resourceJndiName);
}
}
}
if (containsUdf()) {
updateUdfJars(model);
}
synchronizeIndex();
// Also add imported models if not a preview
if (!getVdb().isPreview()) {
importVdbNames.clear();
Resource[] refs = getFinder().findReferencesFrom(model, true, false);
Collection<VdbImportInfo> vdbImports = new ArrayList<VdbImportInfo>();
if (refs != null) {
// Need to look in each imported model.. if it's a "view" model, then we can ignore it
// If it's a "source" model, then we need to look for the properties in the model
for (final Resource importedModel : refs) {
java.net.URI uri = java.net.URI.create(importedModel.getURI().toString());
IFile[] modelFiles = ModelerCore.getWorkspace().getRoot().findFilesForLocationURI(uri);
final IPath name = modelFiles[0].getFullPath();
// Check Model File to see if it contains a vdb name property
final String importVdbName = ModelUtil.getModelAnnotationPropertyValue(modelFiles[0],
VdbConstants.VDB_NAME_KEY);
if (importVdbName != null) {
String versionStr = ModelUtil.getModelAnnotationPropertyValue(modelFiles[0],
VdbConstants.VDB_VERSION_KEY);
String vdbVersion = "1";
if( ! StringUtilities.isEmpty(versionStr) ) {
try {
String versionValue = versionStr;
if (StringUtilities.isNotEmpty(versionValue)) {
vdbVersion = versionValue;
}
} catch (NumberFormatException ex) {
VdbPlugin.UTIL.log(ex);
}
}
importVdbNames.add(importVdbName);
vdbImports.add(new VdbImportInfo(importVdbName, vdbVersion));
} else {
VdbEntry importedEntry = null;
for (final VdbEntry entry : getVdb().getModelEntries()) {
if (name.equals(entry.getPath())) {
importedEntry = entry;
break;
}
}
if (importedEntry == null)
importedEntry = getVdb().addEntry(name);
imports.add(importedEntry);
if (importedEntry instanceof VdbModelEntry) {
((VdbModelEntry)importedEntry).importedBy.add(this);
}
}
}
}
final EmfResource emfModel = (EmfResource)model;
String description = getDescription();
String emfDescription = emfModel.getDescription();
if( StringUtilities.areDifferent(description, emfDescription) ) {
setDescription(emfDescription);
}
// Process for any import VDBs
// if list is empty, then there may be import VDB's that need to get removed from the VDB
getVdb().registerImportVdbs(vdbImports, this.getPath().toString());
getVdb().synchronizeUdfJars(udfJars);
}
}
/**
* Replaces the given old entry with the new entry in this entry's
* imports collection
*
* @param oldEntry
* @param newEntry
*/
void replaceImport(VdbEntry oldEntry, VdbEntry newEntry) {
imports.remove(oldEntry);
imports.add(newEntry);
}
/**
* {@inheritDoc}
*
* @see org.teiid.designer.vdb.VdbEntry#toString(java.lang.StringBuilder)
*/
@Override
protected void toString( final StringBuilder builder ) {
builder.append(", type="); //$NON-NLS-1$
builder.append(type);
builder.append(", visible?="); //$NON-NLS-1$
builder.append(visible);
builder.append(", built-in?="); //$NON-NLS-1$
builder.append(builtIn);
for( VdbSource source : sourceInfo.getSources() ) {
builder.append(", source="); //$NON-NLS-1$
builder.append(source);
}
builder.append(", index="); //$NON-NLS-1$
builder.append(getIndexName());
builder.append(", problems?="); //$NON-NLS-1$
builder.append(!getProblems().isEmpty());
builder.append(", imports=["); //$NON-NLS-1$
for (final Iterator<VdbEntry> iter = imports.iterator(); iter.hasNext();) {
builder.append(iter.next().getName());
if (iter.hasNext()) builder.append(", "); //$NON-NLS-1$
}
builder.append(']');
}
@Override
public VdbModelEntry clone() {
try {
VdbModelEntry clone = new VdbModelEntry(getVdb(), getPath());
cloneVdbObject(clone);
return clone;
} catch (Exception ex) {
VdbPlugin.UTIL.log(ex);
return null;
}
}
}