/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2017 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* 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 org.pentaho.di.ui.repository.repositoryexplorer.model;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.repository.ObjectId;
import org.pentaho.di.repository.Repository;
import org.pentaho.di.repository.RepositoryDirectory;
import org.pentaho.di.repository.RepositoryDirectoryInterface;
import org.pentaho.di.repository.RepositoryElementMetaInterface;
import org.pentaho.di.repository.RepositoryObjectType;
public class UIRepositoryDirectory extends UIRepositoryObject {
private static final long serialVersionUID = -2003651575793768451L;
private RepositoryDirectoryInterface rd;
private UIRepositoryDirectory uiParent = null;
private UIRepositoryDirectories kidDirectoryCache = null;
private UIRepositoryObjects kidElementCache = null;
private boolean expanded = false;
public UIRepositoryDirectory() {
super();
kidDirectoryCache = null;
kidElementCache = null;
}
public UIRepositoryDirectory( RepositoryDirectoryInterface rd, UIRepositoryDirectory uiParent, Repository rep ) {
super( rd, rep );
this.uiParent = uiParent;
this.rd = rd;
kidDirectoryCache = null;
kidElementCache = null;
}
public UIRepositoryDirectories getChildren() {
// We've been here before.. use the cache
if ( kidDirectoryCache != null ) {
return kidDirectoryCache;
}
if ( kidDirectoryCache == null ) {
kidDirectoryCache = new UIRepositoryDirectories();
}
if ( rd.getChildren() == null ) {
return kidDirectoryCache;
}
for ( RepositoryDirectoryInterface child : rd.getChildren() ) {
try {
kidDirectoryCache.add( UIObjectRegistry.getInstance().constructUIRepositoryDirectory( child, this, rep ) );
} catch ( UIObjectCreationException e ) {
kidDirectoryCache.add( new UIRepositoryDirectory( child, this, rep ) );
}
}
return kidDirectoryCache;
}
public void setChildren( UIRepositoryDirectories children ) {
kidDirectoryCache = children;
}
// TODO: Abstract working model; should throw RepositoryException
// TODO: We will need a way to reset this cache when a directory or element changes
public UIRepositoryObjects getRepositoryObjects() throws KettleException {
// We've been here before.. use the cache
if ( kidElementCache != null ) {
return kidElementCache;
}
if ( kidElementCache == null ) {
kidElementCache = new UIRepositoryObjects() {
private static final long serialVersionUID = 6901479331535375165L;
public void onRemove( UIRepositoryObject child ) {
List<? extends RepositoryElementMetaInterface> dirRepoObjects = getDirectory().getRepositoryObjects();
if ( dirRepoObjects != null ) {
Iterator<? extends RepositoryElementMetaInterface> iter = dirRepoObjects.iterator();
while ( iter.hasNext() ) {
RepositoryElementMetaInterface e = iter.next();
if ( child.getObjectId().equals( e.getObjectId() ) ) {
iter.remove();
return;
}
}
}
}
};
}
for ( UIRepositoryObject child : getChildren() ) {
kidElementCache.add( child );
}
List<RepositoryElementMetaInterface> jobsAndTransformations = getDirectory().getRepositoryObjects();
if ( jobsAndTransformations == null ) {
RepositoryDirectoryInterface dir = getDirectory();
jobsAndTransformations = rep.getJobAndTransformationObjects( dir.getObjectId(), false );
dir.setRepositoryObjects( jobsAndTransformations );
}
for ( RepositoryElementMetaInterface child : jobsAndTransformations ) {
if ( child.getObjectType().equals( RepositoryObjectType.TRANSFORMATION ) ) {
try {
kidElementCache.add( UIObjectRegistry.getInstance().constructUITransformation( child, this, rep ) );
} catch ( UIObjectCreationException e ) {
kidElementCache.add( new UITransformation( child, this, rep ) );
}
} else if ( child.getObjectType().equals( RepositoryObjectType.JOB ) ) {
try {
kidElementCache.add( UIObjectRegistry.getInstance().constructUIJob( child, this, rep ) );
} catch ( UIObjectCreationException e ) {
kidElementCache.add( new UIJob( child, this, rep ) );
}
}
}
return kidElementCache;
}
public String toString() {
return getName();
}
public void setName( String name ) throws Exception {
if ( getDirectory().getName().equalsIgnoreCase( name ) ) {
return;
}
rep.renameRepositoryDirectory( getDirectory().getObjectId(), null, name );
// Update the object reference so the new name is displayed
obj = rep.findDirectory( getObjectId() );
refresh();
}
public String getDescription() {
return null;
}
public String getLockMessage() {
return null;
}
public Date getModifiedDate() {
return null;
}
public String getModifiedUser() {
return null;
}
public RepositoryObjectType getRepositoryElementType() {
return null;
}
@Override
public boolean isDeleted() {
return super.isDeleted();
}
@Override
public String getType() {
return null;
}
@Override
public String getFormatModifiedDate() {
return null;
}
public RepositoryDirectory getDirectory() {
return (RepositoryDirectory) rd;
}
@Override
public String getImage() {
return "ui/images/folder.svg";
}
public void delete() throws Exception {
if ( uiParent.checkDirNameExistsInRepo( getName() ) != null ) {
rep.deleteRepositoryDirectory( getDirectory() );
}
uiParent.getChildren().remove( this );
if ( uiParent.getRepositoryObjects().contains( this ) ) {
uiParent.getRepositoryObjects().remove( this );
}
uiParent.refresh();
}
/**
* Check if a subdirectory already exists in the repository.
* This is to help fix PDI-5202
* Since the ui directories are case insensitive, we look for a repo directory with the same name ignoring case.
* If we find an existing directory, we return the name so we can use that to get hold of the directory
* as it is known in the repository.
* If we don't find such a directory, we return null
* @param name - the name of a subdirectory
* @return null if the subdirectory does not exist, or the name of the subdirectory as it is known inside the repo.
* @throws KettleException
*/
public String checkDirNameExistsInRepo( String name ) throws KettleException {
String[] dirNames = rep.getDirectoryNames( getObjectId() );
for ( String dirName : dirNames ) {
if ( dirName.equalsIgnoreCase( name ) ) {
return dirName;
}
}
return null;
}
public UIRepositoryDirectory createFolder( String name ) throws Exception {
RepositoryDirectoryInterface thisDir = getDirectory();
RepositoryDirectoryInterface dir;
//PDI-5202: the directory might exist already. If so, don't create a new one.
String dirName = checkDirNameExistsInRepo( name );
if ( dirName == null ) {
dir = rep.createRepositoryDirectory( thisDir, name );
} else {
dir = rep.findDirectory( thisDir.getPath() + "/" + dirName );
}
UIRepositoryDirectory newDir = null;
try {
newDir = UIObjectRegistry.getInstance().constructUIRepositoryDirectory( dir, this, rep );
} catch ( UIObjectCreationException uoe ) {
newDir = new UIRepositoryDirectory( dir, this, rep );
}
UIRepositoryDirectories directories = getChildren();
if ( !contains( directories, newDir ) ) {
directories.add( newDir );
} else {
throw new KettleException( "Unable to create folder with the same name [" + name + "]" );
}
kidElementCache = null; // rebuild the element cache for correct positioning.
return newDir;
}
public void fireCollectionChanged() {
firePropertyChange( "children", null, getChildren() );
getChildren(); // prime cache before firing event (already primed from above getChildren call but to be consistent)
kidDirectoryCache.fireCollectionChanged();
try {
getRepositoryObjects(); // prime cache before firing event
kidElementCache.fireCollectionChanged();
} catch ( KettleException ignored ) {
// Ignore errors
}
}
@Override
public void move( UIRepositoryDirectory newParentDir ) throws Exception {
if ( newParentDir != null ) {
rep.renameRepositoryDirectory( obj.getObjectId(), newParentDir.getDirectory(), null );
// Try to make sure the directories are updated properly
if ( !newParentDir.equals( getParent() ) ) {
getParent().getChildren().remove( this );
newParentDir.getChildren().add( this );
getParent().refresh();
newParentDir.refresh();
}
}
}
protected UIRepositoryDirectory getParentDirectory() {
return uiParent;
}
protected UIRepositoryDirectory getRootDirectory() {
UIRepositoryDirectory parent = uiParent, result = this;
while ( parent != null ) {
result = parent;
parent = parent.getParentDirectory();
}
return result;
}
/**
* Synchronize this folder with the back-end
*
*
*/
public void refresh() {
try {
kidElementCache = null;
kidDirectoryCache = null;
if ( this == getRootDirectory() ) {
RepositoryDirectoryInterface localRoot = rep.findDirectory( rd.getObjectId() );
rd = localRoot;
// Rebuild caches
fireCollectionChanged();
} else {
getRootDirectory().refresh();
}
} catch ( Exception e ) {
// TODO: Better error handling
e.printStackTrace();
}
}
@Override
public int getCategory() {
return 10;
}
public boolean isExpanded() {
return expanded;
}
public void setExpanded( boolean expand ) {
this.expanded = expand;
}
public void toggleExpanded() {
setExpanded( !isExpanded() );
firePropertyChange( "expanded", null, this.expanded );
}
public UIRepositoryDirectory getParent() {
return uiParent;
}
public String getPath() {
return ( (RepositoryDirectory) rd ).getPath();
}
public boolean isVisible() {
return rd.isVisible();
}
// begin PDI-3326 hack
@Override
public int size() {
return getChildren().size();
}
@Override
public UIRepositoryObject get( int index ) {
return getChildren().get( index );
}
@Override
public Iterator<UIRepositoryObject> iterator() {
return getChildren().iterator();
}
public boolean contains( String dirName ) {
UIRepositoryDirectories directories = getChildren();
UIRepositoryObject dir;
for ( int i = 0; i < directories.size(); i++ ) {
dir = directories.get( i );
if ( !( dir instanceof UIRepositoryDirectory ) ) {
continue;
} else if ( dir.getName() == null && dirName == null ) {
return true;
} else if ( dir.getName().equalsIgnoreCase( dirName ) ) {
return true;
}
}
return false;
}
private boolean contains( UIRepositoryDirectories directories, UIRepositoryDirectory searchDir ) {
for ( int i = 0; i < directories.size(); i++ ) {
UIRepositoryObject dir = directories.get( i );
if ( dir instanceof UIRepositoryDirectory ) {
if ( dir.getName() != null && dir.getName().equals( searchDir.getName() ) ) {
return true;
}
}
}
return false;
}
// end PDI-3326 hack
// Must implement equals/hashcode to compare object ids since the cache of directories may be refreshed
// and therefore would not be the same instances
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
ObjectId id = getObjectId();
result = prime * result + ( ( id == null ) ? 0 : id.hashCode() );
return result;
}
@Override
public boolean equals( Object obj ) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
UIRepositoryDirectory other = (UIRepositoryDirectory) obj;
ObjectId id = getObjectId();
ObjectId otherId = other.getObjectId();
if ( id == null ) {
if ( otherId != null ) {
return false;
}
} else if ( !id.equals( otherId ) ) {
return false;
}
return true;
}
}