/*! ****************************************************************************** * * Pentaho Data Integration * * Copyright (C) 2002-2016 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.trans.steps.fileinput; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.StringUtils; import org.pentaho.di.core.fileinput.FileInputList; import org.pentaho.di.core.injection.Injection; import org.pentaho.di.core.injection.InjectionDeep; import org.pentaho.di.core.variables.VariableSpace; import org.pentaho.di.i18n.BaseMessages; import org.pentaho.di.resource.ResourceEntry; import org.pentaho.di.resource.ResourceEntry.ResourceType; import org.pentaho.di.resource.ResourceReference; import org.pentaho.di.trans.TransMeta; import org.pentaho.di.trans.step.BaseStepMeta; import org.pentaho.di.trans.step.StepMeta; /** * Base meta for file-based input steps. * * @author Alexander Buloichik */ public abstract class BaseFileInputStepMeta<A extends BaseFileInputStepMeta.AdditionalOutputFields, I extends BaseFileInputStepMeta.InputFiles<? extends BaseFileInputField>> extends BaseStepMeta { private static Class<?> PKG = BaseFileInputStepMeta.class; // for i18n purposes, needed by Translator2!! public static final String[] RequiredFilesCode = new String[] { "N", "Y" }; public static final String NO = "N"; public static final String YES = "Y"; public static final String[] RequiredFilesDesc = new String[] { BaseMessages.getString( PKG, "System.Combo.No" ), BaseMessages.getString( PKG, "System.Combo.Yes" ) }; @InjectionDeep public I inputFiles; @InjectionDeep public ErrorHandling errorHandling = new ErrorHandling(); @InjectionDeep public A additionalOutputFields; /** * Input files settings. */ public static class InputFiles<F extends BaseFileInputField> implements Cloneable { /** Array of filenames */ @Injection( name = "FILENAME", group = "FILENAME_LINES" ) public String[] fileName = {}; /** Wildcard or filemask (regular expression) */ @Injection( name = "FILEMASK", group = "FILENAME_LINES" ) public String[] fileMask = {}; /** Wildcard or filemask to exclude (regular expression) */ @Injection( name = "EXCLUDE_FILEMASK", group = "FILENAME_LINES" ) public String[] excludeFileMask = {}; /** Array of boolean values as string, indicating if a file is required. */ @Injection( name = "FILE_REQUIRED", group = "FILENAME_LINES" ) public String[] fileRequired = {}; /** Array of boolean values as string, indicating if we need to fetch sub folders. */ @Injection( name = "INCLUDE_SUBFOLDERS", group = "FILENAME_LINES" ) public String[] includeSubFolders = {}; /** Are we accepting filenames in input rows? */ @Injection( name = "ACCEPT_FILE_NAMES" ) public boolean acceptingFilenames; /** The stepname to accept filenames from */ @Injection( name = "ACCEPT_FILE_STEP" ) public String acceptingStepName; /** If receiving input rows, should we pass through existing fields? */ @Injection( name = "PASS_THROUGH_FIELDS" ) public boolean passingThruFields; /** The field in which the filename is placed */ @Injection( name = "ACCEPT_FILE_FIELD" ) public String acceptingField; /** The fields to import... */ @InjectionDeep public F[] inputFields; /** The add filenames to result filenames flag */ @Injection( name = "ADD_FILES_TO_RESULT" ) public boolean isaddresult; public Object clone() { try { return super.clone(); } catch ( CloneNotSupportedException e ) { return null; } } public void setFileRequired( String[] fileRequiredin ) { for ( int i = 0; i < fileRequiredin.length; i++ ) { this.fileRequired[i] = getRequiredFilesCode( fileRequiredin[i] ); } } public void setIncludeSubFolders( String[] includeSubFoldersin ) { for ( int i = 0; i < includeSubFoldersin.length; i++ ) { this.includeSubFolders[i] = getRequiredFilesCode( includeSubFoldersin[i] ); } } } /** * Error handling settings. */ public static class ErrorHandling implements Cloneable { /** Ignore error : turn into warnings */ @Injection( name = "IGNORE_ERRORS" ) public boolean errorIgnored; /** File error field name. */ @Injection( name = "FILE_ERROR_FIELD" ) public String fileErrorField; /** File error text field name. */ @Injection( name = "FILE_ERROR_MESSAGE_FIELD" ) public String fileErrorMessageField; @Injection( name = "SKIP_BAD_FILES" ) public boolean skipBadFiles; /** The directory that will contain warning files */ @Injection( name = "WARNING_FILES_TARGET_DIR" ) public String warningFilesDestinationDirectory; /** The extension of warning files */ @Injection( name = "WARNING_FILES_EXTENTION" ) public String warningFilesExtension; /** The directory that will contain error files */ @Injection( name = "ERROR_FILES_TARGET_DIR" ) public String errorFilesDestinationDirectory; /** The extension of error files */ @Injection( name = "ERROR_FILES_EXTENTION" ) public String errorFilesExtension; /** The directory that will contain line number files */ @Injection( name = "LINE_NR_FILES_TARGET_DIR" ) public String lineNumberFilesDestinationDirectory; /** The extension of line number files */ @Injection( name = "LINE_NR_FILES_EXTENTION" ) public String lineNumberFilesExtension; public Object clone() { try { return super.clone(); } catch ( CloneNotSupportedException e ) { return null; } } } /** * Additional fields settings. */ public static class AdditionalOutputFields implements Cloneable { /** Additional fields **/ @Injection( name = "FILE_SHORT_FILE_FIELDNAME" ) public String shortFilenameField; @Injection( name = "FILE_EXTENSION_FIELDNAME" ) public String extensionField; @Injection( name = "FILE_PATH_FIELDNAME" ) public String pathField; @Injection( name = "FILE_SIZE_FIELDNAME" ) public String sizeField; @Injection( name = "FILE_HIDDEN_FIELDNAME" ) public String hiddenField; @Injection( name = "FILE_LAST_MODIFICATION_FIELDNAME" ) public String lastModificationField; @Injection( name = "FILE_URI_FIELDNAME" ) public String uriField; @Injection( name = "FILE_ROOT_URI_FIELDNAME" ) public String rootUriField; public Object clone() { try { return super.clone(); } catch ( CloneNotSupportedException e ) { return null; } } /** * Set null for all empty field values to be able to fast check during step processing. Need to be executed once * before processing. */ public void normalize() { if ( StringUtils.isBlank( shortFilenameField ) ) { shortFilenameField = null; } if ( StringUtils.isBlank( extensionField ) ) { extensionField = null; } if ( StringUtils.isBlank( pathField ) ) { pathField = null; } if ( StringUtils.isBlank( sizeField ) ) { sizeField = null; } if ( StringUtils.isBlank( hiddenField ) ) { hiddenField = null; } if ( StringUtils.isBlank( lastModificationField ) ) { lastModificationField = null; } if ( StringUtils.isBlank( uriField ) ) { uriField = null; } if ( StringUtils.isBlank( rootUriField ) ) { rootUriField = null; } } } public Object clone() { BaseFileInputStepMeta retval = (BaseFileInputStepMeta) super.clone(); retval.inputFiles = (InputFiles) inputFiles.clone(); retval.errorHandling = (ErrorHandling) errorHandling.clone(); retval.additionalOutputFields = (AdditionalOutputFields) additionalOutputFields.clone(); return retval; } /** * @param fileRequired * The fileRequired to set. */ public void inputFiles_fileRequired( String[] fileRequiredin ) { for ( int i = 0; i < fileRequiredin.length; i++ ) { inputFiles.fileRequired[i] = getRequiredFilesCode( fileRequiredin[i] ); } } public String[] inputFiles_includeSubFolders() { return inputFiles.includeSubFolders; } public void inputFiles_includeSubFolders( String[] includeSubFoldersin ) { for ( int i = 0; i < includeSubFoldersin.length; i++ ) { inputFiles.includeSubFolders[i] = getRequiredFilesCode( includeSubFoldersin[i] ); } } public static String getRequiredFilesCode( String tt ) { if ( tt == null ) { return RequiredFilesCode[0]; } if ( tt.equals( RequiredFilesDesc[1] ) ) { return RequiredFilesCode[1]; } else { return RequiredFilesCode[0]; } } public FileInputList getFileInputList( VariableSpace space ) { inputFiles.fileMask = normalizeAllocation( inputFiles.fileMask, inputFiles.fileName.length ); inputFiles.excludeFileMask = normalizeAllocation( inputFiles.excludeFileMask, inputFiles.fileName.length ); inputFiles.fileRequired = normalizeAllocation( inputFiles.fileRequired, inputFiles.fileName.length ); inputFiles.includeSubFolders = normalizeAllocation( inputFiles.includeSubFolders, inputFiles.fileName.length ); return FileInputList.createFileList( space, inputFiles.fileName, inputFiles.fileMask, inputFiles.excludeFileMask, inputFiles.fileRequired, includeSubFolderBoolean() ); } public boolean[] includeSubFolderBoolean() { int len = inputFiles.fileName.length; boolean[] includeSubFolderBoolean = new boolean[len]; for ( int i = 0; i < len; i++ ) { includeSubFolderBoolean[i] = YES.equalsIgnoreCase( inputFiles.includeSubFolders[i] ); } return includeSubFolderBoolean; } private String[] normalizeAllocation( String[] oldAllocation, int length ) { String[] newAllocation = null; if ( oldAllocation.length < length ) { newAllocation = new String[length]; for ( int i = 0; i < oldAllocation.length; i++ ) { newAllocation[i] = oldAllocation[i]; } } else { newAllocation = oldAllocation; } return newAllocation; } @Override public List<ResourceReference> getResourceDependencies( TransMeta transMeta, StepMeta stepInfo ) { List<ResourceReference> references = new ArrayList<ResourceReference>( 5 ); ResourceReference reference = new ResourceReference( stepInfo ); references.add( reference ); String[] textFiles = FileInputList.createFilePathList( transMeta, inputFiles.fileName, inputFiles.fileMask, inputFiles.excludeFileMask, inputFiles.fileRequired, includeSubFolderBoolean() ); if ( textFiles != null ) { for ( int i = 0; i < textFiles.length; i++ ) { reference.getEntries().add( new ResourceEntry( textFiles[i], ResourceType.FILE ) ); } } return references; } public abstract String getEncoding(); }