/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, version 2 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
*
* Copyright 2006 - 2013 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.platform.repository2.unified.fileio;
import org.apache.commons.lang.StringUtils;
import org.pentaho.platform.api.repository2.unified.ISourcesStreamEvents;
import org.pentaho.platform.api.repository2.unified.IStreamListener;
import org.pentaho.platform.api.repository2.unified.IUnifiedRepository;
import org.pentaho.platform.api.repository2.unified.RepositoryFile;
import org.pentaho.platform.api.repository2.unified.data.simple.SimpleRepositoryFileData;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.repository.RepositoryFilenameUtils;
import org.pentaho.platform.util.web.MimeHelper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class RepositoryFileOutputStream extends ByteArrayOutputStream implements ISourcesStreamEvents {
protected boolean hidden = false;
protected String path = null;
protected IUnifiedRepository repository;
protected String charsetName = null;
protected boolean autoCreateUniqueFileName = false;
protected boolean autoCreateDirStructure = false;
protected boolean closed = false;
protected boolean flushed = false;
protected ArrayList<IStreamListener> listeners = new ArrayList<IStreamListener>();
public RepositoryFileOutputStream( final String path, final boolean autoCreateUniqueFileName,
final boolean autoCreateDirStructure, final IUnifiedRepository repository, final boolean hidden ) {
setRepository( repository );
this.path = path;
this.hidden = hidden;
this.autoCreateDirStructure = autoCreateDirStructure;
this.autoCreateUniqueFileName = autoCreateUniqueFileName;
}
public RepositoryFileOutputStream( final Serializable id, final boolean autoCreateUniqueFileName,
final boolean autoCreateDirStructure, final IUnifiedRepository repository, final boolean hidden )
throws FileNotFoundException {
setRepository( repository );
RepositoryFile file = this.repository.getFileById( id );
if ( file == null ) {
throw new FileNotFoundException( MessageFormat.format(
"Repository file with id {0} not readable or does not exist", id ) );
}
this.hidden = hidden;
this.path = file.getPath();
this.autoCreateDirStructure = autoCreateDirStructure;
this.autoCreateUniqueFileName = autoCreateUniqueFileName;
}
public RepositoryFileOutputStream( final RepositoryFile file, final boolean autoCreateUniqueFileName,
final boolean autoCreateDirStructure, final IUnifiedRepository repository ) {
this( file.getPath(), autoCreateUniqueFileName, autoCreateDirStructure, repository, false );
}
public RepositoryFileOutputStream( final String path, final boolean autoCreateUniqueFileName,
final boolean autoCreateDirStructure ) {
this( path, autoCreateUniqueFileName, autoCreateDirStructure, null, false );
}
public RepositoryFileOutputStream( final Serializable id, final boolean autoCreateUniqueFileName,
final boolean autoCreateDirStructure ) throws FileNotFoundException {
this( id, autoCreateUniqueFileName, autoCreateDirStructure, null, false );
}
public RepositoryFileOutputStream( final RepositoryFile file, final boolean autoCreateUniqueFileName,
final boolean autoCreateDirStructure ) {
this( file, autoCreateUniqueFileName, autoCreateDirStructure, null );
}
public RepositoryFileOutputStream( final String path ) {
this( path, false, false, null, false );
}
public RepositoryFileOutputStream( final String path, final boolean hidden ) {
this( path, false, false, null, hidden );
}
public RepositoryFileOutputStream( final RepositoryFile file ) {
this( file.getPath(), false, false, null, file.isHidden() );
}
public RepositoryFileOutputStream( final Serializable id ) throws FileNotFoundException {
this( id, false, false, null, false );
}
// //
// charsetName is required as metadata so the JCR can be provided the correct encoding, provided
// we are storing text. These are package private methosd only and should only be called by
// RepositoryFileWriter, because if you are wanting to write characters to a repository file,
// you need to use RepositoryFileWriter, not this class.
//
protected RepositoryFileOutputStream( String path, String charsetName ) throws FileNotFoundException {
this( path );
this.charsetName = charsetName;
}
protected RepositoryFileOutputStream( RepositoryFile file, String charsetName ) throws FileNotFoundException {
this( file );
this.charsetName = charsetName;
}
protected RepositoryFileOutputStream( Serializable id, String charsetName ) throws FileNotFoundException {
this( id );
this.charsetName = charsetName;
}
//
// //
protected RepositoryFile getParent( String path ) {
String newFilePath = StringUtils.removeEnd( path, "/" ); //$NON-NLS-1$
String parentPath = StringUtils.substringBeforeLast( newFilePath, "/" ); //$NON-NLS-1$
if ( parentPath.isEmpty() ) {
parentPath = "/";
}
return repository.getFile( parentPath );
}
@Override
public void close() throws IOException {
if ( !closed ) {
flush();
closed = true;
reset();
}
}
@Override
public void flush() throws IOException {
if ( closed ) {
return;
}
super.flush();
ByteArrayInputStream bis = new ByteArrayInputStream( toByteArray() );
// make an effort to determine the correct mime type, default to application/octet-stream
String ext = RepositoryFilenameUtils.getExtension( path );
String mimeType = "application/octet-stream"; //$NON-NLS-1$
if ( ext != null ) {
String tempMimeType = MimeHelper.getMimeTypeFromExtension( "." + ext ); //$NON-NLS-1$
if ( tempMimeType != null ) {
mimeType = tempMimeType;
}
}
if ( charsetName == null && mimeType != null ) {
charsetName = MimeHelper.getDefaultCharset( mimeType );
}
// FIXME: not a good idea that we assume we are dealing with text. Best if this is somehow moved to the
// RepositoryFileWriter
// but I couldn't figure out a clean way to do that. For now, charsetName is passed in here and we use it if
// available.
final SimpleRepositoryFileData payload = new SimpleRepositoryFileData( bis, charsetName, mimeType );
if ( !flushed ) {
RepositoryFile file = repository.getFile( path );
RepositoryFile parentFolder = getParent( path );
String baseFileName = RepositoryFilenameUtils.getBaseName( path );
String extension = RepositoryFilenameUtils.getExtension( path );
if ( file == null ) {
if ( autoCreateDirStructure ) {
ArrayList<String> foldersToCreate = new ArrayList<String>();
String parentPath = RepositoryFilenameUtils.getFullPathNoEndSeparator( path );
// Make sure the parent path isn't the root
while ( ( parentPath != null ) && ( parentPath.length() > 0 && !path.equals( parentPath ) )
&& ( repository.getFile( parentPath ) == null ) ) {
foldersToCreate.add( RepositoryFilenameUtils.getName( parentPath ) );
parentPath = RepositoryFilenameUtils.getFullPathNoEndSeparator( parentPath );
}
Collections.reverse( foldersToCreate );
parentFolder =
( ( parentPath != null ) && ( parentPath.length() > 0 ) ) ? repository.getFile( parentPath ) : repository
.getFile( "/" );
if ( !parentFolder.isFolder() ) {
throw new FileNotFoundException();
}
for ( String folderName : foldersToCreate ) {
parentFolder =
repository.createFolder( parentFolder.getId(), new RepositoryFile.Builder( folderName ).folder( true )
.build(), null );
}
} else {
if ( parentFolder == null ) {
throw new FileNotFoundException();
}
}
file =
new RepositoryFile.Builder( RepositoryFilenameUtils.getName( path ) ).hidden( hidden ).versioned( true )
.build(); // Default
// versioned to true so that we're keeping history
file =
repository.createFile( parentFolder.getId(), file, payload,
"commit from " + RepositoryFileOutputStream.class.getName() ); //$NON-NLS-1$
for ( IStreamListener listener : listeners ) {
listener.fileCreated( path );
}
} else if ( file.isFolder() ) {
throw new FileNotFoundException( MessageFormat.format( "Repository file {0} is a directory", file.getPath() ) );
} else {
if ( autoCreateUniqueFileName ) {
int nameCount = 1;
String newFileName = null;
List<RepositoryFile> children = repository.getChildren( parentFolder.getId() );
boolean hasFile = true;
while ( hasFile ) {
hasFile = false;
nameCount++;
if ( ( extension != null ) && ( extension.length() > 0 ) ) {
newFileName = baseFileName + "(" + nameCount + ")." + extension; //$NON-NLS-1$ //$NON-NLS-2$
} else {
newFileName = baseFileName + "(" + nameCount + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
for ( RepositoryFile child : children ) {
if ( child.getPath().equals( parentFolder.getPath() + "/" + newFileName ) ) {
hasFile = true;
break;
}
}
}
file = new RepositoryFile.Builder( newFileName ).versioned( true ).build(); // Default versioned to true
// so
// that we're keeping history
file = repository.createFile( parentFolder.getId(), file, payload, "New File" ); //$NON-NLS-1$
path = file.getPath();
for ( IStreamListener listener : listeners ) {
listener.fileCreated( path );
}
} else {
repository.updateFile( file, payload, "New File" ); //$NON-NLS-1$
path = file.getPath();
for ( IStreamListener listener : listeners ) {
listener.fileCreated( path );
}
}
}
} else {
RepositoryFile file = repository.getFile( path );
repository.updateFile( file, payload, "New File" ); //$NON-NLS-1$
}
flushed = true;
}
public String getFilePath() {
return path;
}
public void setFilePath( String path ) {
if ( !path.equals( this.path ) ) {
this.path = path;
reset();
flushed = false;
closed = false;
}
}
public boolean getAutoCreateUniqueFileName() {
return autoCreateUniqueFileName;
}
public boolean getAutoCreateDirStructure() {
return autoCreateDirStructure;
}
public void setAutoCreateDirStructure( boolean autoCreateDirStructure ) {
this.autoCreateDirStructure = autoCreateDirStructure;
}
public void addListener( IStreamListener listener ) {
listeners.add( listener );
}
public void setRepository( final IUnifiedRepository repository ) {
this.repository = ( repository != null ? repository : PentahoSystem.get( IUnifiedRepository.class ) );
}
public IUnifiedRepository getRepository() {
return this.repository;
}
public String getCharsetName() {
return charsetName;
}
public void setCharsetName( final String charsetName ) {
this.charsetName = charsetName;
}
public boolean isFlushed() {
return flushed;
}
}