/**
  * 
  *
  * @param string $ps_source
  * @param string $ps_mapping
  * @param array $pa_options
  *		user_id = user to execute import for
  *		description = Text describing purpose of import to be logged.
  *		showCLIProgressBar = Show command-line progress bar. Default is false.
  *		format = Format of data being imported. MANDATORY
  *		logDirectory = path to directory where logs should be written
  *		logLevel = KLogger constant for minimum log level to record. Default is KLogger::INFO. Constants are, in descending order of shrillness:
  *			KLogger::EMERG = Emergency messages (system is unusable)
  *			KLogger::ALERT = Alert messages (action must be taken immediately)
  *			KLogger::CRIT = Critical conditions
  *			KLogger::ERR = Error conditions
  *			KLogger::WARN = Warnings
  *			KLogger::NOTICE = Notices (normal but significant conditions)
  *			KLogger::INFO = Informational messages
  *			KLogger::DEBUG = Debugging messages
  *		dryRun = do import but don't actually save data
  *		environment = an array of environment values to provide to the import process. The keys manifest themselves as mappable tags.
  *		forceImportForPrimaryKeys = list of primary key ids to force mapped source data into. The number of keys passed should equal or exceed the number of rows in the source data. [Default is empty] 
  *		transaction = transaction to perform import within. Will not be used if noTransaction option is set. [Default is to create a new transaction]
  *		noTransaction = don't wrap the import in a transaction. [Default is false]
  *		importAllDatasets = for data formats (such as Excel/XLSX) that support multiple data sets in a single file (worksheets in Excel), indicated that all data sets should be imported; otherwise only the default data set is imported [Default=false]
  */
 public static function importDataFromSource($ps_source, $ps_mapping, $pa_options = null)
 {
     ca_data_importers::$s_num_import_errors = 0;
     ca_data_importers::$s_num_records_processed = 0;
     ca_data_importers::$s_num_records_skipped = 0;
     ca_data_importers::$s_import_error_list = array();
     $opa_app_plugin_manager = new ApplicationPluginManager();
     $va_notices = $va_errors = array();
     $pb_no_transaction = caGetOption('noTransaction', $pa_options, false, array('castTo' => 'bool'));
     $pa_force_import_for_primary_keys = caGetOption('forceImportForPrimaryKeys', $pa_options, null);
     if (!($t_mapping = ca_data_importers::mappingExists($ps_mapping))) {
         return null;
     }
     $o_event = ca_data_import_events::newEvent(isset($pa_options['user_id']) ? $pa_options['user_id'] : null, $pa_options['format'], $ps_source, isset($pa_options['description']) ? $pa_options['description'] : '');
     $o_trans = null;
     if (!$pb_no_transaction) {
         if (!($o_trans = caGetOption('transaction', $pa_options, null))) {
             $o_trans = new Transaction();
         }
         $t_mapping->setTransaction($o_trans);
     }
     $po_request = caGetOption('request', $pa_options, null);
     $pb_dry_run = caGetOption('dryRun', $pa_options, false);
     $pn_file_number = caGetOption('fileNumber', $pa_options, 0);
     $pn_number_of_files = caGetOption('numberOfFiles', $pa_options, 1);
     $o_config = Configuration::load();
     if (!is_array($pa_options) || !isset($pa_options['logLevel']) || !$pa_options['logLevel']) {
         $pa_options['logLevel'] = KLogger::INFO;
     }
     if (!is_array($pa_options) || !isset($pa_options['logDirectory']) || !$pa_options['logDirectory'] || !file_exists($pa_options['logDirectory'])) {
         if (!($pa_options['logDirectory'] = $o_config->get('batch_metadata_import_log_directory'))) {
             $pa_options['logDirectory'] = ".";
         }
     }
     if (!is_writeable($pa_options['logDirectory'])) {
         $pa_options['logDirectory'] = caGetTempDirPath();
     }
     $o_log = new KLogger($pa_options['logDirectory'], $pa_options['logLevel']);
     $vb_show_cli_progress_bar = isset($pa_options['showCLIProgressBar']) && $pa_options['showCLIProgressBar'] ? true : false;
     $o_progress = caGetOption('progressBar', $pa_options, new ProgressBar('WebUI'));
     if ($vb_show_cli_progress_bar) {
         $o_progress->setMode('CLI');
         $o_progress->set('outputToTerminal', true);
     }
     $o_log->logInfo(_t('Started import of %1 using mapping %2', $ps_source, $t_mapping->get("importer_code")));
     $t = new Timer();
     $vn_start_time = time();
     $va_log_import_error_opts = array('startTime' => $vn_start_time, 'window' => $r_errors, 'log' => $o_log, 'request' => $po_request, 'progressCallback' => isset($pa_options['progressCallback']) && ($ps_callback = $pa_options['progressCallback']) ? $ps_callback : null, 'reportCallback' => isset($pa_options['reportCallback']) && ($ps_callback = $pa_options['reportCallback']) ? $ps_callback : null);
     global $g_ui_locale_id;
     // constant locale set by index.php for web requests
     $vn_locale_id = isset($pa_options['locale_id']) && (int) $pa_options['locale_id'] ? (int) $pa_options['locale_id'] : $g_ui_locale_id;
     $o_dm = $t_mapping->getAppDatamodel();
     $vb_import_all_datasets = caGetOption('importAllDatasets', $pa_options, false);
     $o_progress->start(_t('Reading %1', $ps_source), array('window' => $r_progress));
     if ($po_request && isset($pa_options['progressCallback']) && ($ps_callback = $pa_options['progressCallback'])) {
         $ps_callback($po_request, $pn_file_number, $pn_number_of_files, $ps_source, 0, 100, _t('Reading %1', $ps_source), time() - $vn_start_time, memory_get_usage(true), 0, ca_data_importers::$s_num_import_errors);
     }
     // Open file
     $ps_format = isset($pa_options['format']) && $pa_options['format'] ? $pa_options['format'] : null;
     if (!($o_reader = $t_mapping->getDataReader($ps_source, $ps_format))) {
         ca_data_importers::logImportError(_t("Could not open source %1 (format=%2)", $ps_source, $ps_format), $va_log_import_error_opts);
         if ($o_trans) {
             $o_trans->rollback();
         }
         return false;
     }
     $va_reader_opts = array('basePath' => $t_mapping->getSetting('basePath'));
     if (!$o_reader->read($ps_source, $va_reader_opts)) {
         ca_data_importers::logImportError(_t("Could not read source %1 (format=%2)", $ps_source, $ps_format), $va_log_import_error_opts);
         if ($o_trans) {
             $o_trans->rollback();
         }
         return false;
     }
     $o_log->logDebug(_t('Finished reading input source at %1 seconds', $t->getTime(4)));
     $vn_dataset_count = $vb_import_all_datasets ? (int) $o_reader->getDatasetCount() : 1;
     for ($vn_dataset = 0; $vn_dataset < $vn_dataset_count; $vn_dataset++) {
         if (!$o_reader->setCurrentDataset($vn_dataset)) {
             continue;
         }
         $vn_num_items = $o_reader->numRows();
         $o_log->logDebug(_t('Found %1 rows in input source', $vn_num_items));
         $o_progress->setTotal($vn_num_items);
         $o_progress->start(_t('Importing from %1', $ps_source), array('window' => $r_progress));
         if ($po_request && isset($pa_options['progressCallback']) && ($ps_callback = $pa_options['progressCallback'])) {
             $ps_callback($po_request, $pn_file_number, $pn_number_of_files, $ps_source, 0, $vn_num_items, _t('Importing from %1', $ps_source), time() - $vn_start_time, memory_get_usage(true), 0, ca_data_importers::$s_num_import_errors);
         }
         // What are we importing?
         $vn_table_num = $t_mapping->get('table_num');
         if (!($t_subject = $o_dm->getInstanceByTableNum($vn_table_num))) {
             // invalid table
             $o_log->logError(_t('Mapping uses invalid table %1 as target', $vn_table_num));
             if ($o_trans) {
                 $o_trans->rollback();
             }
             return false;
         }
         $t_subject->setTransaction($o_trans);
         $vs_subject_table_name = $t_subject->tableName();
         $t_label = $t_subject->getLabelTableInstance();
         $t_label->setTransaction($o_trans);
         $vs_label_display_fld = $t_subject->getLabelDisplayField();
         $vs_subject_table = $t_subject->tableName();
         $vs_type_id_fld = $t_subject->getTypeFieldName();
         $vs_idno_fld = $t_subject->getProperty('ID_NUMBERING_ID_FIELD');
         // get mapping rules
         $va_mapping_rules = $t_mapping->getRules();
         // get mapping groups
         $va_mapping_groups = $t_mapping->getGroups();
         $va_mapping_items = $t_mapping->getItems();
         //
         // Mapping-level settings
         //
         $vs_type_mapping_setting = $t_mapping->getSetting('type');
         $vn_num_initial_rows_to_skip = $t_mapping->getSetting('numInitialRowsToSkip');
         if (!in_array($vs_import_error_policy = $t_mapping->getSetting('errorPolicy'), array('ignore', 'stop'))) {
             $vs_import_error_policy = 'ignore';
         }
         if (!in_array($vs_existing_record_policy = $t_mapping->getSetting('existingRecordPolicy'), array('none', 'skip_on_idno', 'skip_on_preferred_labels', 'merge_on_idno', 'merge_on_preferred_labels', 'merge_on_idno_and_preferred_labels', 'merge_on_idno_with_replace', 'merge_on_preferred_labels_with_replace', 'merge_on_idno_and_preferred_labels_with_replace', 'overwrite_on_idno', 'overwrite_on_preferred_labels', 'overwrite_on_idno_and_preferred_labels'))) {
             $vs_existing_record_policy = 'none';
         }
         // Analyze mapping for figure out where type, idno, preferred label and other mandatory fields are coming from
         $vn_type_id_mapping_item_id = $vn_idno_mapping_item_id = null;
         $va_preferred_label_mapping_ids = array();
         $va_mandatory_field_mapping_ids = array();
         $va_mandatory_fields = $t_subject->getMandatoryFields();
         foreach ($va_mapping_items as $vn_item_id => $va_item) {
             $vs_destination = $va_item['destination'];
             if (sizeof($va_dest_tmp = explode(".", $vs_destination)) >= 2) {
                 if ($va_dest_tmp[0] == $vs_subject_table && $va_dest_tmp[1] == 'preferred_labels') {
                     if (isset($va_dest_tmp[2])) {
                         $va_preferred_label_mapping_ids[$vn_item_id] = $va_dest_tmp[2];
                     } else {
                         $va_preferred_label_mapping_ids[$vn_item_id] = $vs_label_display_fld;
                     }
                     continue;
                 }
             }
             switch ($vs_destination) {
                 case 'representation_id':
                     if ($vs_subject_table == 'ca_representation_annotations') {
                         $vn_type_id_mapping_item_id = $vn_item_id;
                     }
                     break;
                 case "{$vs_subject_table}.{$vs_type_id_fld}":
                     $vn_type_id_mapping_item_id = $vn_item_id;
                     break;
                 case "{$vs_subject_table}.{$vs_idno_fld}":
                     $vn_idno_mapping_item_id = $vn_item_id;
                     break;
             }
             foreach ($va_mandatory_fields as $vs_mandatory_field) {
                 if ($vs_mandatory_field == $vs_type_id_fld) {
                     continue;
                 }
                 // type is handled separately
                 if ($vs_destination == "{$vs_subject_table}.{$vs_mandatory_field}") {
                     $va_mandatory_field_mapping_ids[$vs_mandatory_field] = $vn_item_id;
                 }
             }
         }
         $va_items_by_group = array();
         foreach ($va_mapping_items as $vn_item_id => $va_item) {
             $va_items_by_group[$va_item['group_id']][$va_item['item_id']] = $va_item;
         }
         $o_log->logDebug(_t('Finished analyzing mapping at %1 seconds', $t->getTime(4)));
         //
         // Set up environment
         //
         $va_environment = caGetOption('environment', $pa_options, array(), array('castTo' => 'array'));
         if (is_array($va_environment_config = $t_mapping->getEnvironment())) {
             foreach ($va_environment_config as $vn_i => $va_environment_item) {
                 $va_env_tmp = explode("|", $va_environment_item['value']);
                 if (!($o_env_reader = $t_mapping->getDataReader($ps_source, $ps_format))) {
                     break;
                 }
                 if (!$o_env_reader->read($ps_source, array('basePath' => ''))) {
                     break;
                 }
                 $o_env_reader->setCurrentDataset($vn_dataset);
                 $o_env_reader->nextRow();
                 switch (sizeof($va_env_tmp)) {
                     case 1:
                         $vs_env_value = $o_env_reader->get($va_environment_item['value'], array('returnAsArray' => false));
                         break;
                     case 2:
                         $vn_seek = (int) $va_env_tmp[0];
                         $o_env_reader->seek($vn_seek > 0 ? $vn_seek - 1 : $vn_seek);
                         $o_env_reader->nextRow();
                         $vs_env_value = $o_env_reader->get($va_env_tmp[1], array('returnAsArray' => false));
                         $o_env_reader->seek(0);
                         break;
                     default:
                         $vs_env_value = $va_environment_item['value'];
                         break;
                 }
                 $va_environment[$va_environment_item['name']] = $vs_env_value;
             }
         }
         //
         // Run through rows
         //
         $vn_row = 0;
         ca_data_importers::$s_num_records_processed = 0;
         while ($o_reader->nextRow()) {
             $va_mandatory_field_values = array();
             $vs_preferred_label_for_log = null;
             $vb_use_parent_as_subject = false;
             if ($vn_row < $vn_num_initial_rows_to_skip) {
                 // skip over initial header rows
                 $o_log->logDebug(_t('Skipped initial row %1 of %2', $vn_row, $vn_num_initial_rows_to_skip));
                 $vn_row++;
                 continue;
             }
             $vn_row++;
             $t->startTimer();
             $o_log->logDebug(_t('Started reading row %1 at %2 seconds', $vn_row, $t->getTime(4)));
             //
             // Get data for current row
             //
             $va_row = array_replace($o_reader->getRow(), $va_environment);
             //
             // Apply rules
             //
             foreach ($va_mapping_rules as $va_rule) {
                 if (!isset($va_rule['trigger']) || !$va_rule['trigger']) {
                     continue;
                 }
                 if (!isset($va_rule['actions']) || !is_array($va_rule['actions']) || !sizeof($va_rule['actions'])) {
                     continue;
                 }
                 $vm_ret = ExpressionParser::evaluate($va_rule['trigger'], $va_row);
                 if (!ExpressionParser::hadError() && (bool) $vm_ret) {
                     foreach ($va_rule['actions'] as $va_action) {
                         if (!is_array($va_action) && strtolower($va_action) == 'skip') {
                             $va_action = array('action' => 'skip');
                         }
                         switch ($vs_action_code = strtolower($va_action['action'])) {
                             case 'set':
                                 $va_row[$va_action['target']] = $va_action['value'];
                                 // TODO: transform value using mapping rules?
                                 break;
                             case 'skip':
                             default:
                                 if ($vs_action_code != 'skip') {
                                     $o_log->logInfo(_t('Row was skipped using rule "%1" with default action because an invalid action ("%2") was specified', $va_rule['trigger'], $vs_action_code));
                                 } else {
                                     $o_log->logDebug(_t('Row was skipped using rule "%1" with action "%2"', $va_rule['trigger'], $vs_action_code));
                                 }
                                 continue 4;
                                 break;
                         }
                     }
                 }
             }
             //
             // Perform mapping and insert
             //
             // Get minimal info for imported row (type_id, idno, label)
             // Get type
             if ($vn_type_id_mapping_item_id) {
                 // Type is specified in row
                 $vs_type = ca_data_importers::getValueFromSource($va_mapping_items[$vn_type_id_mapping_item_id], $o_reader, array('environment' => $va_environment));
             } else {
                 // Type is constant for all rows
                 $vs_type = $vs_type_mapping_setting;
             }
             // Get idno
             $vs_idno = $va_idnos_for_row = null;
             if ($vn_idno_mapping_item_id) {
                 // idno is specified in row
                 $vs_idno = ca_data_importers::getValueFromSource($va_mapping_items[$vn_idno_mapping_item_id], $o_reader, array('environment' => $va_environment));
                 if (isset($va_mapping_items[$vn_idno_mapping_item_id]['settings']['default']) && strlen($va_mapping_items[$vn_idno_mapping_item_id]['settings']['default']) && !strlen($vs_idno)) {
                     $vs_idno = $va_mapping_items[$vn_idno_mapping_item_id]['settings']['default'];
                 }
                 if (!is_array($vs_idno) && $vs_idno[0] == '^' && preg_match("!^\\^[^ ]+\$!", $vs_idno)) {
                     // Parse placeholder when it's at the beginning of the value
                     if (!is_null($vm_parsed_val = BaseRefinery::parsePlaceholder($vs_idno, $va_row, $va_item, null, array('reader' => $o_reader, 'returnAsString' => true)))) {
                         $vs_idno = $vm_parsed_val;
                     }
                 }
                 // Apply prefix/suffix *AFTER* setting default
                 if ($vs_idno && isset($va_mapping_items[$vn_idno_mapping_item_id]['settings']['prefix']) && strlen($va_mapping_items[$vn_idno_mapping_item_id]['settings']['prefix'])) {
                     $vs_idno = $va_mapping_items[$vn_idno_mapping_item_id]['settings']['prefix'] . $vs_idno;
                 }
                 if ($vs_idno && isset($va_mapping_items[$vn_idno_mapping_item_id]['settings']['suffix']) && strlen($va_mapping_items[$vn_idno_mapping_item_id]['settings']['suffix'])) {
                     $vs_idno .= $va_mapping_items[$vn_idno_mapping_item_id]['settings']['suffix'];
                 }
                 if (isset($va_mapping_items[$vn_idno_mapping_item_id]['settings']['formatWithTemplate']) && strlen($va_mapping_items[$vn_idno_mapping_item_id]['settings']['formatWithTemplate'])) {
                     $vs_idno = caProcessTemplate($va_mapping_items[$vn_idno_mapping_item_id]['settings']['formatWithTemplate'], $va_row);
                 }
                 if ($va_mapping_items[$vn_idno_mapping_item_id]['settings']['delimiter'] && $va_mapping_items[$vn_idno_mapping_item_id]['settings']['treatAsIdentifiersForMultipleRows']) {
                     $va_idnos_for_row = explode($va_mapping_items[$vn_idno_mapping_item_id]['settings']['delimiter'], $vs_idno);
                 }
             } else {
                 $vs_idno = "%";
             }
             $vb_idno_is_template = (bool) preg_match('![%]+!', $vs_idno);
             if (!$vb_idno_is_template) {
                 $va_idnos_for_row = !$va_idnos_for_row ? array($vs_idno) : $va_idnos_for_row;
             } else {
                 $va_idnos_for_row = array($vs_idno);
             }
             foreach ($va_idnos_for_row as $vs_idno) {
                 $t_subject = $o_dm->getInstanceByTableNum($vn_table_num);
                 if ($o_trans) {
                     $t_subject->setTransaction($o_trans);
                 }
                 $t_subject->setMode(ACCESS_WRITE);
                 // get preferred labels
                 $va_pref_label_values = array();
                 foreach ($va_preferred_label_mapping_ids as $vn_preferred_label_mapping_id => $vs_preferred_label_mapping_fld) {
                     $vs_label_val = ca_data_importers::getValueFromSource($va_mapping_items[$vn_preferred_label_mapping_id], $o_reader, array('environment' => $va_environment));
                     // If a template is specified format the label value with it so merge-on-preferred_label doesn't fail
                     if (isset($va_mapping_items[$vn_preferred_label_mapping_id]['settings']['formatWithTemplate']) && strlen($va_mapping_items[$vn_preferred_label_mapping_id]['settings']['formatWithTemplate'])) {
                         $vs_label_val = caProcessTemplate($va_mapping_items[$vn_preferred_label_mapping_id]['settings']['formatWithTemplate'], $va_row);
                     }
                     if ($vs_opt = $va_mapping_items[$vn_preferred_label_mapping_id]['settings']['displaynameFormat']) {
                         $va_label_val = DataMigrationUtils::splitEntityName($vs_label_val, array('displaynameFormat' => $vs_opt));
                         $vs_label_val = $va_label_val['displayname'];
                     }
                     $va_pref_label_values[$vs_preferred_label_mapping_fld] = $vs_label_val;
                 }
                 $vs_display_label = isset($va_pref_label_values[$vs_label_display_fld]) ? $va_pref_label_values[$vs_label_display_fld] : $vs_idno;
                 //
                 // Look for existing record?
                 //
                 if (is_array($pa_force_import_for_primary_keys) && sizeof($pa_force_import_for_primary_keys) > 0) {
                     $vn_id = array_shift($pa_force_import_for_primary_keys);
                     if (!$t_subject->load($vn_id)) {
                         $o_log->logInfo(_t('[%1] Skipped import because of forced primary key \'%1\' does not exist', $vn_id));
                         ca_data_importers::$s_num_records_skipped++;
                         continue;
                         // skip because primary key does not exist
                     }
                 } elseif ($vs_existing_record_policy != 'none') {
                     switch ($vs_existing_record_policy) {
                         case 'skip_on_idno':
                             if (!$vb_idno_is_template) {
                                 $va_ids = call_user_func_array($t_subject->tableName() . "::find", array(array('type_id' => $vs_type, $t_subject->getProperty('ID_NUMBERING_ID_FIELD') => $vs_idno, 'deleted' => 0), array('returnAs' => 'ids')));
                                 if (is_array($va_ids) && sizeof($va_ids) > 0) {
                                     $o_log->logInfo(_t('[%1] Skipped import because of existing record matched on identifier by policy %2', $vs_idno, $vs_existing_record_policy));
                                     ca_data_importers::$s_num_records_skipped++;
                                     continue 2;
                                     // skip because idno matched
                                 }
                             }
                             break;
                         case 'skip_on_preferred_labels':
                             $va_ids = call_user_func_array($t_subject->tableName() . "::find", array(array('type_id' => $vs_type, 'preferred_labels' => $va_pref_label_values, 'deleted' => 0), array('returnAs' => 'ids')));
                             if (is_array($va_ids) && sizeof($va_ids) > 0) {
                                 $o_log->logInfo(_t('[%1] Skipped import because of existing record matched on label by policy %2', $vs_idno, $vs_existing_record_policy));
                                 ca_data_importers::$s_num_records_skipped++;
                                 continue 2;
                                 // skip because label matched
                             }
                             break;
                         case 'merge_on_idno_and_preferred_labels':
                         case 'merge_on_idno':
                         case 'merge_on_idno_and_preferred_labels_with_replace':
                         case 'merge_on_idno_with_replace':
                             if (!$vb_idno_is_template) {
                                 $va_ids = call_user_func_array($t_subject->tableName() . "::find", array(array('type_id' => $vs_type, $t_subject->getProperty('ID_NUMBERING_ID_FIELD') => $vs_idno, 'deleted' => 0), array('returnAs' => 'ids')));
                                 if (is_array($va_ids) && sizeof($va_ids) > 0) {
                                     $t_subject->load($va_ids[0]);
                                     $o_log->logInfo(_t('[%1] Merged with existing record matched on identifer by policy %2', $vs_idno, $vs_existing_record_policy));
                                     break;
                                 }
                             }
                             if (in_array($vs_existing_record_policy, array('merge_on_idno', 'merge_on_idno_with_replace'))) {
                                 break;
                             }
                             // fall through if merge_on_idno_and_preferred_labels
                         // fall through if merge_on_idno_and_preferred_labels
                         case 'merge_on_preferred_labels':
                         case 'merge_on_preferred_labels_with_replace':
                             $va_ids = call_user_func_array($t_subject->tableName() . "::find", array(array('type_id' => $vs_type, 'preferred_labels' => $va_pref_label_values, 'deleted' => 0), array('returnAs' => 'ids')));
                             if (is_array($va_ids) && sizeof($va_ids) > 0) {
                                 $t_subject->load($va_ids[0]);
                                 $o_log->logInfo(_t('[%1] Merged with existing record matched on label by policy %2', $vs_idno, $vs_existing_record_policy));
                             }
                             break;
                         case 'overwrite_on_idno_and_preferred_labels':
                         case 'overwrite_on_idno':
                             if (!$vb_idno_is_template && $vs_idno) {
                                 $va_ids = call_user_func_array($t_subject->tableName() . "::find", array(array('type_id' => $vs_type, $t_subject->getProperty('ID_NUMBERING_ID_FIELD') => $vs_idno, 'deleted' => 0), array('returnAs' => 'ids')));
                                 if (is_array($va_ids) && sizeof($va_ids) > 0) {
                                     $t_subject->load($va_ids[0]);
                                     $t_subject->setMode(ACCESS_WRITE);
                                     $t_subject->delete(true, array('hard' => true));
                                     if ($t_subject->numErrors()) {
                                         ca_data_importers::logImportError(_t('[%1] Could not delete existing record matched on identifier by policy %2', $vs_idno, $vs_existing_record_policy));
                                         // Don't stop?
                                     } else {
                                         $o_log->logInfo(_t('[%1] Overwrote existing record matched on identifier by policy %2', $vs_idno, $vs_existing_record_policy));
                                         $t_subject->clear();
                                         break;
                                     }
                                 }
                             }
                             if ($vs_existing_record_policy == 'overwrite_on_idno') {
                                 break;
                             }
                             // fall through if overwrite_on_idno_and_preferred_labels
                         // fall through if overwrite_on_idno_and_preferred_labels
                         case 'overwrite_on_preferred_labels':
                             $va_ids = call_user_func_array($t_subject->tableName() . "::find", array(array('type_id' => $vs_type, 'preferred_labels' => $va_pref_label_values, 'deleted' => 0), array('returnAs' => 'ids')));
                             if (is_array($va_ids) && sizeof($va_ids) > 0) {
                                 $t_subject->load($va_ids[0]);
                                 $t_subject->setMode(ACCESS_WRITE);
                                 $t_subject->delete(true, array('hard' => true));
                                 if ($t_subject->numErrors()) {
                                     ca_data_importers::logImportError(_t('[%1] Could not delete existing record matched on label by policy %2', $vs_idno, $vs_existing_record_policy));
                                     // Don't stop?
                                 } else {
                                     $o_log->logInfo(_t('[%1] Overwrote existing record matched on label by policy %2', $vs_idno, $vs_existing_record_policy));
                                     break;
                                 }
                                 $t_subject->clear();
                             }
                             break;
                     }
                 }
                 $o_progress->next(_t("Importing %1", $vs_idno), array('window' => $r_progress));
                 if ($po_request && isset($pa_options['progressCallback']) && ($ps_callback = $pa_options['progressCallback'])) {
                     $ps_callback($po_request, $pn_file_number, $pn_number_of_files, $ps_source, ca_data_importers::$s_num_records_processed, $vn_num_items, _t("[%3/%4] Processing %1 (%2)", caTruncateStringWithEllipsis($vs_display_label, 50), $vs_idno, ca_data_importers::$s_num_records_processed, $vn_num_items), time() - $vn_start_time, memory_get_usage(true), ca_data_importers::$s_num_records_processed, ca_data_importers::$s_num_import_errors);
                 }
                 $vb_output_subject_preferred_label = false;
                 $va_content_tree = array();
                 foreach ($va_items_by_group as $vn_group_id => $va_items) {
                     $va_group = $va_mapping_groups[$vn_group_id];
                     $vs_group_destination = $va_group['destination'];
                     $va_group_tmp = explode(".", $vs_group_destination);
                     if (sizeof($va_items) < 2 && sizeof($va_group_tmp) > 2) {
                         array_pop($va_group_tmp);
                     }
                     $vs_target_table = $va_group_tmp[0];
                     if (!($t_target = $o_dm->getInstanceByTableName($vs_target_table, true))) {
                         // Invalid target table
                         $o_log->logWarn(_t('[%1] Skipped group %2 because target %3 is invalid', $vs_idno, $vn_group_id, $vs_target_table));
                         continue;
                     }
                     if ($o_trans) {
                         $t_target->setTransaction($o_trans);
                     }
                     $va_group_buf = array();
                     foreach ($va_items as $vn_item_id => $va_item) {
                         if ($vb_use_as_single_value = caGetOption('useAsSingleValue', $va_item['settings'], false)) {
                             // Force repeating values to be imported as a single value
                             $va_vals = array(ca_data_importers::getValueFromSource($va_item, $o_reader, array('delimiter' => caGetOption('delimiter', $va_item['settings'], ''), 'returnAsArray' => false, 'lookahead' => caGetOption('lookahead', $va_item['settings'], 0))));
                         } else {
                             $va_vals = ca_data_importers::getValueFromSource($va_item, $o_reader, array('returnAsArray' => true, 'environment' => $va_environment, 'lookahead' => caGetOption('lookahead', $va_item['settings'], 0)));
                         }
                         if (!sizeof($va_vals)) {
                             $va_vals = array(0 => null);
                         }
                         // consider missing values equivalent to blanks
                         // Do value conversions
                         foreach ($va_vals as $vn_i => $vm_val) {
                             // Evaluate skip-if-empty options before setting default value, addings prefix/suffix or formatting with templates
                             // because "empty" refers to the source value before this sort of additive processing.
                             if (isset($va_item['settings']['skipRowIfEmpty']) && (bool) $va_item['settings']['skipRowIfEmpty'] && !strlen($vm_val)) {
                                 $o_log->logInfo(_t('[%1] Skipped row %2 because value for %3 in group %4 is empty', $vs_idno, $vn_row, $vs_item_terminal, $vn_group_id));
                                 continue 4;
                             }
                             if (isset($va_item['settings']['skipGroupIfEmpty']) && (bool) $va_item['settings']['skipGroupIfEmpty'] && !strlen($vm_val)) {
                                 $o_log->logInfo(_t('[%1] Skipped group %2 because value for %3 is empty', $vs_idno, $vn_group_id, $vs_item_terminal));
                                 continue 3;
                             }
                             if (isset($va_item['settings']['skipIfEmpty']) && (bool) $va_item['settings']['skipIfEmpty'] && !strlen($vm_val)) {
                                 $o_log->logInfo(_t('[%1] Skipped mapping because value for %2 is empty', $vs_idno, $vs_item_terminal));
                                 continue 2;
                             }
                             if (isset($va_item['settings']['default']) && strlen($va_item['settings']['default']) && !strlen($vm_val)) {
                                 $vm_val = $va_item['settings']['default'];
                             }
                             // Apply prefix/suffix *AFTER* setting default
                             if ($vm_val && isset($va_item['settings']['prefix']) && strlen($va_item['settings']['prefix'])) {
                                 $vm_val = $va_item['settings']['prefix'] . $vm_val;
                             }
                             if ($vm_val && isset($va_item['settings']['suffix']) && strlen($va_item['settings']['suffix'])) {
                                 $vm_val .= $va_item['settings']['suffix'];
                             }
                             if (!is_array($vm_val) && $vm_val[0] == '^' && preg_match("!^\\^[^ ]+\$!", $vm_val)) {
                                 // Parse placeholder
                                 if (!is_null($vm_parsed_val = BaseRefinery::parsePlaceholder($vm_val, $va_row, $va_item, $vn_i, array('reader' => $o_reader, 'returnAsString' => true)))) {
                                     $vm_val = $vm_parsed_val;
                                 }
                             }
                             if (isset($va_item['settings']['formatWithTemplate']) && strlen($va_item['settings']['formatWithTemplate'])) {
                                 $vm_val = caProcessTemplate($va_item['settings']['formatWithTemplate'], array_replace($va_row, array((string) $va_item['source'] => ca_data_importers::replaceValue($vm_val, $va_item))), array('getFrom' => $o_reader));
                             }
                             if (isset($va_item['settings']['applyRegularExpressions']) && is_array($va_item['settings']['applyRegularExpressions'])) {
                                 if (is_array($va_item['settings']['applyRegularExpressions'])) {
                                     foreach ($va_item['settings']['applyRegularExpressions'] as $vn_regex_index => $va_regex) {
                                         if (!strlen($va_regex['match'])) {
                                             continue;
                                         }
                                         $vm_val = preg_replace("!" . str_replace("!", "\\!", $va_regex['match']) . "!" . (isset($va_regex['caseSensitive']) && (bool) $va_regex['caseSensitive'] ? '' : 'i'), $va_regex['replaceWith'], $vm_val);
                                     }
                                 }
                             }
                             $va_vals[$vn_i] = $vm_val;
                             if ($o_reader->valuesCanRepeat()) {
                                 $va_row[$va_item['source']][$vn_i] = $va_row[mb_strtolower($va_item['source'])][$vn_i] = $vm_val;
                             } else {
                                 $va_row[$va_item['source']] = $va_row[mb_strtolower($va_item['source'])] = $vm_val;
                             }
                         }
                         // Process each value
                         $vn_c = -1;
                         foreach ($va_vals as $vn_i => $vm_val) {
                             $vn_c++;
                             if (isset($va_item['settings']['convertNewlinesToHTML']) && (bool) $va_item['settings']['convertNewlinesToHTML'] && is_string($vm_val)) {
                                 $vm_val = nl2br($vm_val);
                             }
                             // Get location in content tree for addition of new content
                             $va_item_dest = explode(".", $va_item['destination']);
                             $vs_item_terminal = $va_item_dest[sizeof($va_item_dest) - 1];
                             if (isset($va_item['settings']['restrictToTypes']) && is_array($va_item['settings']['restrictToTypes']) && !in_array($vs_type, $va_item['settings']['restrictToTypes'])) {
                                 $o_log->logInfo(_t('[%1] Skipped row %2 because of type restriction', $vs_idno, $vn_row));
                                 continue 4;
                             }
                             if ($va_item['settings']['skipRowIfValue'] && !is_array($va_item['settings']['skipRowIfValue'])) {
                                 $va_item['settings']['skipRowIfValue'] = array($va_item['settings']['skipRowIfValue']);
                             }
                             if (isset($va_item['settings']['skipRowIfValue']) && is_array($va_item['settings']['skipRowIfValue']) && strlen($vm_val) && in_array($vm_val, $va_item['settings']['skipRowIfValue'])) {
                                 $o_log->logInfo(_t('[%1] Skipped row %2 because value for %3 in group %4 matches value %5', $vs_idno, $vn_row, $vs_item_terminal, $vn_group_id, $vm_val));
                                 continue 4;
                             }
                             if ($va_item['settings']['skipRowIfNotValue'] && !is_array($va_item['settings']['skipRowIfNotValue'])) {
                                 $va_item['settings']['skipRowIfNotValue'] = array($va_item['settings']['skipRowIfNotValue']);
                             }
                             if (isset($va_item['settings']['skipRowIfNotValue']) && is_array($va_item['settings']['skipRowIfNotValue']) && strlen($vm_val) && !in_array($vm_val, $va_item['settings']['skipRowIfNotValue'])) {
                                 $o_log->logInfo(_t('[%1] Skipped row %2 because value for %3 in group %4 is not in list of values', $vs_idno, $vn_row, $vs_item_terminal, $vn_group_id, $vm_val));
                                 continue 4;
                             }
                             if (isset($va_item['settings']['skipRowIfExpression']) && strlen(trim($va_item['settings']['skipRowIfExpression']))) {
                                 if ($vm_ret = ExpressionParser::evaluate($va_item['settings']['skipRowIfExpression'], $va_row)) {
                                     $o_log->logInfo(_t('[%1] Skipped row %2 because expression %3 is true', $vs_idno, $vn_row, $va_item['settings']['skipRowIfExpression']));
                                     continue 4;
                                 }
                             }
                             if ($va_item['settings']['skipIfValue'] && !is_array($va_item['settings']['skipIfValue'])) {
                                 $va_item['settings']['skipIfValue'] = array($va_item['settings']['skipIfValue']);
                             }
                             if (isset($va_item['settings']['skipIfValue']) && is_array($va_item['settings']['skipIfValue']) && strlen($vm_val) && in_array($vm_val, $va_item['settings']['skipIfValue'])) {
                                 $o_log->logInfo(_t('[%1] Skipped mapping %2 because value for %3 matches value %4', $vs_idno, $vn_row, $vs_item_terminal, $vm_val));
                                 continue 2;
                             }
                             if ($va_item['settings']['skipIfNotValue'] && !is_array($va_item['settings']['skipIfNotValue'])) {
                                 $va_item['settings']['skipIfNotValue'] = array($va_item['settings']['skipIfNotValue']);
                             }
                             if (isset($va_item['settings']['skipIfNotValue']) && is_array($va_item['settings']['skipIfNotValue']) && strlen($vm_val) && !in_array($vm_val, $va_item['settings']['skipIfNotValue'])) {
                                 $o_log->logInfo(_t('[%1] Skipped mapping %2 because value %4 for %3 is not in list of values', $vs_idno, $vn_row, $vs_item_terminal, $vm_val));
                                 continue 2;
                             }
                             if (isset($va_item['settings']['skipGroupIfExpression']) && strlen(trim($va_item['settings']['skipGroupIfExpression']))) {
                                 if ($vm_ret = ExpressionParser::evaluate($va_item['settings']['skipGroupIfExpression'], $va_row)) {
                                     $o_log->logInfo(_t('[%1] Skipped group %2 because expression %3 is true', $vs_idno, $vn_group_id, $va_item['settings']['skipGroupIfExpression']));
                                     continue 3;
                                 }
                             }
                             if ($va_item['settings']['skipGroupIfValue'] && !is_array($va_item['settings']['skipGroupIfValue'])) {
                                 $va_item['settings']['skipGroupIfValue'] = array($va_item['settings']['skipGroupIfValue']);
                             }
                             if (isset($va_item['settings']['skipGroupIfValue']) && is_array($va_item['settings']['skipGroupIfValue']) && strlen($vm_val) && in_array($vm_val, $va_item['settings']['skipGroupIfValue'])) {
                                 $o_log->logInfo(_t('[%1] Skipped group %2 because value for %3 matches value %4', $vs_idno, $vn_group_id, $vs_item_terminal, $vm_val));
                                 continue 3;
                             }
                             if ($va_item['settings']['skipGroupIfNotValue'] && !is_array($va_item['settings']['skipGroupIfNotValue'])) {
                                 $va_item['settings']['skipGroupIfNotValue'] = array($va_item['settings']['skipGroupIfNotValue']);
                             }
                             if (isset($va_item['settings']['skipGroupIfNotValue']) && is_array($va_item['settings']['skipGroupIfNotValue']) && strlen($vm_val) && !in_array($vm_val, $va_item['settings']['skipGroupIfNotValue'])) {
                                 $o_log->logInfo(_t('[%1] Skipped group %2 because value for %3 matches is not in list of values', $vs_idno, $vn_group_id, $vs_item_terminal));
                                 continue 3;
                             }
                             if (isset($va_item['settings']['skipIfExpression']) && strlen(trim($va_item['settings']['skipIfExpression']))) {
                                 if ($vm_ret = ExpressionParser::evaluate($va_item['settings']['skipIfExpression'], $va_row)) {
                                     $o_log->logInfo(_t('[%1] Skipped mapping because expression %2 is true', $vs_idno, $va_item['settings']['skipIfExpression']));
                                     continue 2;
                                 }
                             }
                             if ($vn_type_id_mapping_item_id && $vn_item_id == $vn_type_id_mapping_item_id) {
                                 continue;
                             }
                             if ($vn_idno_mapping_item_id && $vn_item_id == $vn_idno_mapping_item_id) {
                                 continue;
                             }
                             if (is_null($vm_val)) {
                                 continue;
                             }
                             // Get mapping error policy
                             $vb_item_error_policy_is_default = false;
                             if (!isset($va_item['settings']['errorPolicy']) || !in_array($vs_item_error_policy = $va_item['settings']['errorPolicy'], array('ignore', 'stop'))) {
                                 $vs_item_error_policy = 'ignore';
                                 $vb_item_error_policy_is_default = true;
                             }
                             //
                             if (isset($va_item['settings']['relationshipType']) && strlen($vs_rel_type = $va_item['settings']['relationshipType']) && $vs_target_table != $vs_subject_table) {
                                 $va_group_buf[$vn_c]['_relationship_type'] = $vs_rel_type;
                             }
                             if (isset($va_item['settings']['matchOn'])) {
                                 $va_group_buf[$vn_c]['_matchOn'] = $va_item['settings']['matchOn'];
                             }
                             // Is it a constant value?
                             if (preg_match("!^_CONSTANT_:[\\d]+:(.*)!", $va_item['source'], $va_matches)) {
                                 $va_group_buf[$vn_c][$vs_item_terminal] = $va_matches[1];
                                 // Set it and go onto the next item
                                 if ($vs_target_table == $vs_subject_table_name && ($vs_k = array_search($vn_item_id, $va_mandatory_field_mapping_ids)) !== false) {
                                     $va_mandatory_field_values[$vs_k] = $vm_val;
                                 }
                                 continue;
                             }
                             // Perform refinery call (if required) per value
                             if (isset($va_item['settings']['refineries']) && is_array($va_item['settings']['refineries'])) {
                                 foreach ($va_item['settings']['refineries'] as $vs_refinery) {
                                     if (!$vs_refinery) {
                                         continue;
                                     }
                                     if ($o_refinery = RefineryManager::getRefineryInstance($vs_refinery)) {
                                         $va_refined_values = $o_refinery->refine($va_content_tree, $va_group, $va_item, $va_row, array('mapping' => $t_mapping, 'source' => $ps_source, 'subject' => $t_subject, 'locale_id' => $vn_locale_id, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'reader' => $o_reader, 'valueIndex' => $vn_i));
                                         if (!$va_refined_values || is_array($va_refined_values) && !sizeof($va_refined_values)) {
                                             continue 2;
                                         }
                                         if ($o_refinery->returnsMultipleValues()) {
                                             foreach ($va_refined_values as $va_refined_value) {
                                                 $va_refined_value['_errorPolicy'] = $vs_item_error_policy;
                                                 if (!is_array($va_group_buf[$vn_c])) {
                                                     $va_group_buf[$vn_c] = array();
                                                 }
                                                 $va_group_buf[$vn_c] = array_merge($va_group_buf[$vn_c], $va_refined_value);
                                                 $vn_c++;
                                             }
                                         } else {
                                             $va_group_buf[$vn_c]['_errorPolicy'] = $vs_item_error_policy;
                                             $va_group_buf[$vn_c][$vs_item_terminal] = $va_refined_values;
                                             $vn_c++;
                                         }
                                         if ($vs_target_table == $vs_subject_table_name && ($vs_k = array_search($vn_item_id, $va_mandatory_field_mapping_ids)) !== false) {
                                             $va_mandatory_field_values[$vs_k] = $vm_val;
                                         }
                                         continue 2;
                                     } else {
                                         ca_data_importers::logImportError(_t('[%1] Invalid refinery %2 specified', $vs_idno, $vs_refinery));
                                     }
                                 }
                             }
                             if ($vs_target_table == $vs_subject_table_name && ($vs_k = array_search($vn_item_id, $va_mandatory_field_mapping_ids)) !== false) {
                                 $va_mandatory_field_values[$vs_k] = $vm_val;
                             }
                             $vn_max_length = !is_array($vm_val) && isset($va_item['settings']['maxLength']) && (int) $va_item['settings']['maxLength'] ? (int) $va_item['settings']['maxLength'] : null;
                             if (isset($va_item['settings']['delimiter']) && $va_item['settings']['delimiter']) {
                                 if (!is_array($va_item['settings']['delimiter'])) {
                                     $va_item['settings']['delimiter'] = array($va_item['settings']['delimiter']);
                                 }
                                 if (sizeof($va_item['settings']['delimiter'])) {
                                     foreach ($va_item['settings']['delimiter'] as $vn_index => $vs_delim) {
                                         $va_item['settings']['delimiter'][$vn_index] = preg_quote($vs_delim, "!");
                                     }
                                     $va_val_list = preg_split("!(" . join("|", $va_item['settings']['delimiter']) . ")!", $vm_val);
                                     // Add delimited values
                                     foreach ($va_val_list as $vs_list_val) {
                                         $vs_list_val = trim(ca_data_importers::replaceValue($vs_list_val, $va_item));
                                         if ($vn_max_length && mb_strlen($vs_list_val) > $vn_max_length) {
                                             $vs_list_val = mb_substr($vs_list_val, 0, $vn_max_length);
                                         }
                                         $va_group_buf[$vn_c] = array($vs_item_terminal => $vs_list_val, '_errorPolicy' => $vs_item_error_policy);
                                         $vn_c++;
                                     }
                                     continue;
                                     // Don't add "regular" value below
                                 }
                             }
                             if ($vn_max_length && mb_strlen($vm_val) > $vn_max_length) {
                                 $vm_val = mb_substr($vm_val, 0, $vn_max_length);
                             }
                             if (in_array('preferred_labels', $va_item_dest) || in_array('nonpreferred_labels', $va_item_dest)) {
                                 if (isset($va_item['settings']['truncateLongLabels']) && $va_item['settings']['truncateLongLabels']) {
                                     $va_group_buf[$vn_c]['_truncateLongLabels'] = true;
                                 }
                             }
                             switch ($vs_item_terminal) {
                                 case 'preferred_labels':
                                 case 'nonpreferred_labels':
                                     if ($t_instance = $o_dm->getInstanceByTableName($vs_target_table, true)) {
                                         $va_group_buf[$vn_c][$t_instance->getLabelDisplayField()] = $vm_val;
                                     }
                                     if ($o_trans) {
                                         $t_instance->setTransaction($o_trans);
                                     }
                                     if (!$vb_item_error_policy_is_default || !isset($va_group_buf[$vn_c]['_errorPolicy'])) {
                                         if (is_array($va_group_buf[$vn_c])) {
                                             $va_group_buf[$vn_c]['_errorPolicy'] = $vs_item_error_policy;
                                         }
                                     }
                                     if ($vs_item_terminal == 'preferred_labels') {
                                         $vs_preferred_label_for_log = $vm_val;
                                     }
                                     break;
                                 default:
                                     $va_group_buf[$vn_c][$vs_item_terminal] = $vm_val;
                                     if (!$vb_item_error_policy_is_default || !isset($va_group_buf[$vn_c]['_errorPolicy'])) {
                                         if (is_array($va_group_buf[$vn_c])) {
                                             $va_group_buf[$vn_c]['_errorPolicy'] = $vs_item_error_policy;
                                         }
                                     }
                                     break;
                             }
                         }
                         // end foreach($va_vals as $vm_val)
                     }
                     foreach ($va_group_buf as $vn_group_index => $va_group_data) {
                         $va_ptr =& $va_content_tree;
                         foreach ($va_group_tmp as $vs_tmp) {
                             if (!is_array($va_ptr[$vs_tmp])) {
                                 $va_ptr[$vs_tmp] = array();
                             }
                             $va_ptr =& $va_ptr[$vs_tmp];
                             if ($vs_tmp == $vs_target_table) {
                                 // add numeric index after table to ensure repeat values don't overwrite each other
                                 $va_parent =& $va_ptr;
                                 $va_ptr[] = array();
                                 $va_ptr =& $va_ptr[sizeof($va_ptr) - 1];
                             }
                         }
                         $va_ptr = $va_group_data;
                     }
                     if ($va_item['settings']['useParentAsSubject']) {
                         $vb_use_parent_as_subject = true;
                     }
                 }
                 //
                 // Process out self-relationships
                 //
                 if (is_array($va_content_tree[$vs_subject_table])) {
                     $va_self_related_content = array();
                     foreach ($va_content_tree[$vs_subject_table] as $vn_i => $va_element_data) {
                         if (isset($va_element_data['_relationship_type'])) {
                             $va_self_related_content[] = $va_element_data;
                             unset($va_content_tree[$vs_subject_table][$vn_i]);
                         }
                     }
                     if (sizeof($va_self_related_content) > 0) {
                         $va_content_tree["related.{$vs_subject_table}"] = $va_self_related_content;
                     }
                 }
                 $o_log->logDebug(_t('Finished building content tree for %1 at %2 seconds [%3]', $vs_idno, $t->getTime(4), $vn_row));
                 $o_log->logDebug(_t("Content tree is\n%1", print_R($va_content_tree, true)));
                 if ((bool) $t_mapping->getSetting('dontDoImport')) {
                     $o_log->logDebug(_t("Skipped import of row because dontDoImport was set"));
                     continue;
                 }
                 //
                 // Process data in subject record
                 //
                 //print_r($va_content_tree);
                 //die("END\n\n");
                 //continue;
                 if (!$opa_app_plugin_manager->hookDataImportContentTree(array('mapping' => $t_mapping, 'content_tree' => &$va_content_tree, 'idno' => &$vs_idno, 'type_id' => &$vs_type, 'transaction' => &$o_trans, 'log' => &$o_log, 'reader' => $o_reader, 'environment' => $va_environment, 'importEvent' => $o_event, 'importEventSource' => $vn_row))) {
                     continue;
                 }
                 //print_r($va_content_tree);
                 //die("done\n");
                 if (!sizeof($va_content_tree) && !str_replace("%", "", $vs_idno)) {
                     continue;
                 }
                 if ($vb_use_parent_as_subject) {
                     foreach ($va_content_tree[$vs_subject_table] as $vn_i => $va_element_data) {
                         foreach ($va_element_data as $vs_element => $va_element_value) {
                             if ($vs_element == 'parent_id') {
                                 if (($vn_parent_id = (int) $va_element_value['parent_id']) > 0) {
                                     if ($t_subject->load($vn_parent_id)) {
                                         $va_content_tree[$vs_subject_table][$vn_i]['parent_id']['parent_id'] = $t_subject->get('parent_id');
                                         $va_content_tree[$vs_subject_table][$vn_i]['idno']['idno'] = $t_subject->get('idno');
                                     }
                                 }
                             }
                         }
                     }
                 }
                 if (!$t_subject->getPrimaryKey()) {
                     $o_event->beginItem($vn_row, $t_subject->tableNum(), 'I');
                     $t_subject->setMode(ACCESS_WRITE);
                     $t_subject->set($vs_type_id_fld, $vs_type);
                     if ($vb_idno_is_template) {
                         $t_subject->setIdnoWithTemplate($vs_idno);
                     } else {
                         $t_subject->set($vs_idno_fld, $vs_idno, array('assumeIdnoForRepresentationID' => true, 'assumeIdnoStubForLotID' => true));
                         // assumeIdnoStubForLotID forces ca_objects.lot_id values to always be considered as a potential idno_stub first, before use as a ca_objects.lot_id
                     }
                     // Look for parent_id in the content tree
                     $vs_parent_id_fld = $t_subject->getProperty('HIERARCHY_PARENT_ID_FLD');
                     foreach ($va_content_tree as $vs_table_name => $va_content) {
                         if ($vs_table_name == $vs_subject_table) {
                             foreach ($va_content as $va_element_data) {
                                 foreach ($va_element_data as $vs_element => $va_element_content) {
                                     switch ($vs_element) {
                                         case $vs_parent_id_fld:
                                             if ($va_element_content[$vs_parent_id_fld]) {
                                                 $t_subject->set($vs_parent_id_fld, $va_element_content[$vs_parent_id_fld], array('treatParentIDAsIdno' => true));
                                             }
                                             break;
                                     }
                                 }
                             }
                         }
                     }
                     foreach ($va_mandatory_field_mapping_ids as $vs_mandatory_field => $vn_mandatory_mapping_item_id) {
                         $va_field_info = $t_subject->getFieldInfo($vs_mandatory_field);
                         $va_opts = array('assumeIdnoStubForLotID' => true);
                         if ($va_field_info['FIELD_TYPE'] == FT_MEDIA) {
                             $va_opts['original_filename'] = basename($va_mandatory_field_values[$vs_mandatory_field]);
                         }
                         $t_subject->set($vs_mandatory_field, $va_mandatory_field_values[$vs_mandatory_field], $va_opts);
                     }
                     $t_subject->insert();
                     if ($vs_error = DataMigrationUtils::postError($t_subject, _t("Could not insert new record for %1: ", $t_subject->getProperty('NAME_SINGULAR')), array('dontOutputLevel' => true, 'dontPrint' => true))) {
                         ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                         if ($vs_import_error_policy == 'stop') {
                             $o_log->logAlert(_t('Import stopped due to import error policy'));
                             $o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_FAILURE__, _t('Failed to import %1', $vs_idno));
                             if ($o_trans) {
                                 $o_trans->rollback();
                             }
                             return false;
                         }
                         continue;
                     }
                     $o_log->logDebug(_t('Created idno %1 at %2 seconds', $vs_idno, $t->getTime(4)));
                 } else {
                     $o_event->beginItem($vn_row, $t_subject->tableNum(), 'U');
                     // update
                     $t_subject->setMode(ACCESS_WRITE);
                     if ($vb_idno_is_template) {
                         $t_subject->setIdnoWithTemplate($vs_idno);
                     } else {
                         $t_subject->set($vs_idno_fld, $vs_idno, array('assumeIdnoStubForLotID' => true));
                         // assumeIdnoStubForLotID forces ca_objects.lot_id values to always be considered as a potential idno_stub first, before use as a ca_objects.lot_di
                     }
                     $t_subject->update();
                     if ($vs_error = DataMigrationUtils::postError($t_subject, _t("Could not update matched record"), array('dontOutputLevel' => true, 'dontPrint' => true))) {
                         ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                         if ($vs_import_error_policy == 'stop') {
                             $o_log->logAlert(_t('Import stopped due to import error policy'));
                             $o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_FAILURE__, _t('Failed to import %1', $vs_idno));
                             if ($o_trans) {
                                 $o_trans->rollback();
                             }
                             return false;
                         }
                         continue;
                     }
                     $t_subject->clearErrors();
                     if (sizeof($va_preferred_label_mapping_ids) && $t_subject->getPreferredLabelCount() > 0) {
                         $t_subject->removeAllLabels(__CA_LABEL_TYPE_PREFERRED__);
                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("Could not update remove preferred labels from matched record"), array('dontOutputLevel' => true, 'dontPrint' => true))) {
                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                             if ($vs_import_error_policy == 'stop') {
                                 $o_log->logAlert(_t('Import stopped due to import error policy'));
                                 $o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_FAILURE__, _t('Failed to import %1', $vs_idno));
                                 if ($o_trans) {
                                     $o_trans->rollback();
                                 }
                                 return false;
                             }
                         }
                     }
                     $o_log->logDebug(_t('Updated idno %1 at %2 seconds', $vs_idno, $t->getTime(4)));
                 }
                 if ($vs_idno_fld && ($o_idno = $t_subject->getIDNoPlugInInstance())) {
                     $va_values = $o_idno->htmlFormValuesAsArray($vs_idno_fld, $t_subject->get($vs_idno_fld));
                     if (!is_array($va_values)) {
                         $va_values = array($va_values);
                     }
                     if (($vs_proc_idno = join($o_idno->getSeparator(), $va_values)) && $vs_proc_idno != $vs_idno) {
                         $t_subject->set($vs_idno_fld, $vs_proc_idno);
                         $t_subject->update();
                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("Could update idno"), array('dontOutputLevel' => true, 'dontPrint' => true))) {
                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                             if ($vs_import_error_policy == 'stop') {
                                 $o_log->logAlert(_t('Import stopped due to import error policy'));
                                 $o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_FAILURE__, _t('Failed to import %1', $vs_idno));
                                 if ($o_trans) {
                                     $o_trans->rollback();
                                 }
                                 return false;
                             }
                             continue;
                         }
                     }
                 }
                 $va_elements_set_for_this_record = array();
                 foreach ($va_content_tree as $vs_table_name => $va_content) {
                     if ($vs_table_name == $vs_subject_table) {
                         foreach ($va_content as $vn_i => $va_element_data) {
                             foreach ($va_element_data as $vs_element => $va_element_content) {
                                 if (is_array($va_element_content)) {
                                     $vb_truncate_long_labels = caGetOption('_truncateLongLabels', $va_element_content, false);
                                     unset($va_element_content['_truncateLongLabels']);
                                     $vs_item_error_policy = $va_element_content['_errorPolicy'];
                                     unset($va_element_content['_errorPolicy']);
                                 } else {
                                     $vb_truncate_long_labels = false;
                                     $vs_item_error_policy = null;
                                 }
                                 $t_subject->clearErrors();
                                 $t_subject->setMode(ACCESS_WRITE);
                                 switch ($vs_element) {
                                     case 'preferred_labels':
                                         $t_subject->addLabel($va_element_content, $vn_locale_id, isset($va_element_content['type_id']) ? $va_element_content['type_id'] : null, true, array('truncateLongLabels' => $vb_truncate_long_labels));
                                         if ($t_subject->numErrors() == 0) {
                                             $vb_output_subject_preferred_label = true;
                                         }
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add preferred label to %2. Record was deleted because no preferred label could be applied: ", $vs_idno, $t_subject->tableName()), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             $t_subject->delete(true, array('hard' => false));
                                             if ($vs_import_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to import error policy %1', $vs_import_error_policy));
                                                 $o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_FAILURE__, _t('Failed to import %1', $vs_idno));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                             continue 5;
                                         }
                                         break;
                                     case 'nonpreferred_labels':
                                         $t_subject->addLabel($va_element_content, $vn_locale_id, isset($va_element_content['type_id']) ? $va_element_content['type_id'] : null, false, array('truncateLongLabels' => $vb_truncate_long_labels));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add non-preferred label to %2:", $vs_idno, $t_subject->tableName()), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 $o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_FAILURE__, _t('Failed to import %1', $vs_idno));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                         break;
                                     default:
                                         if ($t_subject->hasField($vs_element)) {
                                             $va_field_info = $t_subject->getFieldInfo($vs_element);
                                             $va_opts = array('assumeIdnoStubForLotID' => true);
                                             if ($va_field_info['FIELD_TYPE'] == FT_MEDIA) {
                                                 $va_opts['original_filename'] = basename($va_element_content[$vs_element]);
                                             }
                                             $t_subject->set($vs_element, $va_element_content[$vs_element], $va_opts);
                                             $t_subject->update();
                                             if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add intrinsic %2 to %3:", $vs_idno, $vs_elenent, $t_subject->tableName()), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                                 ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                                 if ($vs_item_error_policy == 'stop') {
                                                     $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                     $o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_FAILURE__, _t('Failed to import %1', $vs_idno));
                                                     if ($o_trans) {
                                                         $o_trans->rollback();
                                                     }
                                                     return false;
                                                 }
                                                 continue 5;
                                             }
                                             break;
                                         }
                                         if ($vs_subject_table == 'ca_representation_annotations' && $vs_element == 'properties') {
                                             foreach ($va_element_content as $vs_prop => $vs_prop_val) {
                                                 $t_subject->setPropertyValue($vs_prop, $vs_prop_val);
                                             }
                                             break;
                                         }
                                         if (is_array($va_element_content)) {
                                             $va_element_content['locale_id'] = $vn_locale_id;
                                         }
                                         if (!isset($va_elements_set_for_this_record[$vs_element]) && !$va_elements_set_for_this_record[$vs_element] && in_array($vs_existing_record_policy, array('merge_on_idno_with_replace', 'merge_on_preferred_labels_with_replace', 'merge_on_idno_and_preferred_labels_with_replace'))) {
                                             $t_subject->removeAttributes($vs_element, array('force' => true));
                                         }
                                         $va_elements_set_for_this_record[$vs_element] = true;
                                         $va_opts = array('showRepeatCountErrors' => true, 'alwaysTreatValueAsIdno' => true);
                                         if ($va_match_on = caGetOption('_matchOn', $va_element_content, null)) {
                                             $va_opts['matchOn'] = $va_match_on;
                                         }
                                         $t_subject->addAttribute($va_element_content, $vs_element, null, $va_opts);
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Failed to add value for %2; values were %3: ", $vs_idno, $vs_element, ca_data_importers::formatValuesForLog($va_element_content)), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 $o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_FAILURE__, _t('Failed to import %1', $vs_idno));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                         break;
                                 }
                             }
                         }
                         $t_subject->update();
                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Invalid %2; values were %3: ", $vs_idno, $vs_element, ca_data_importers::formatValuesForLog($va_element_content)), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                             if ($vs_item_error_policy == 'stop') {
                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                 if ($vb_use_ncurses) {
                                     ncurses_end();
                                 }
                                 $o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_FAILURE__, _t('Failed to import %1', $vs_idno));
                                 if ($o_trans) {
                                     $o_trans->rollback();
                                 }
                                 return false;
                             }
                         }
                     } else {
                         // related
                         $vs_table_name = preg_replace('!^related\\.!', '', $vs_table_name);
                         foreach ($va_content as $vn_i => $va_element_data) {
                             $va_match_on = caGetOption('_matchOn', $va_element_data, null);
                             $vb_dont_create = caGetOption('_dontCreate', $va_element_data, null);
                             $va_data_for_rel_table = $va_element_data;
                             $va_nonpreferred_labels = isset($va_data_for_rel_table['nonpreferred_labels']) ? $va_data_for_rel_table['nonpreferred_labels'] : null;
                             unset($va_data_for_rel_table['preferred_labels']);
                             unset($va_data_for_rel_table['_relationship_type']);
                             unset($va_data_for_rel_table['_type']);
                             unset($va_data_for_rel_table['_parent_id']);
                             unset($va_data_for_rel_table['_errorPolicy']);
                             unset($va_data_for_rel_table['_matchOn']);
                             $va_data_for_rel_table = array_merge($va_data_for_rel_table, ca_data_importers::_extractIntrinsicValues($va_data_for_rel_table, $vs_table_name));
                             $t_subject->clearErrors();
                             switch ($vs_table_name) {
                                 case 'ca_objects':
                                     if ($vn_rel_id = DataMigrationUtils::getObjectID($va_element_data['preferred_labels']['name'], $va_element_data['_parent_id'], $va_element_data['_type'], $vn_locale_id, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'nonPreferredLabels' => $va_nonpreferred_labels))) {
                                         // kill it if no relationship type is set ... unless its objects_x_representations
                                         // (from the representation side), where the rel type is optional
                                         if (!($vs_rel_type = $va_element_data['_relationship_type']) && !($vs_rel_type = $va_element_data['idno']['_relationship_type']) && $t_subject->tableName() != 'ca_object_representations') {
                                             $o_log->logError(_t('Reltionship type is missing for ca_objects relationship'));
                                             break;
                                         }
                                         $t_subject->addRelationship($vs_table_name, $vn_rel_id, trim($va_element_data['_relationship_type']), null, null, null, null, array('interstitialValues' => $va_element_data['_interstitial']));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related object with relationship %2:", $vs_idno, trim($va_element_data['_relationship_type'])), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                     }
                                     break;
                                 case 'ca_object_lots':
                                     $vs_idno_stub = null;
                                     if (is_array($va_element_data['idno_stub'])) {
                                         $vs_idno_stub = isset($va_element_data['idno_stub']['idno_stub']) ? $va_element_data['idno_stub']['idno_stub'] : '';
                                     } else {
                                         $vs_idno_stub = isset($va_element_data['idno_stub']) ? $va_element_data['idno_stub'] : '';
                                     }
                                     if ($vn_rel_id = DataMigrationUtils::getObjectLotID($vs_idno_stub, $va_element_data['preferred_labels']['name'], $va_element_data['_type'], $vn_locale_id, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'nonPreferredLabels' => $va_nonpreferred_labels))) {
                                         if (!($vs_rel_type = $va_element_data['_relationship_type']) && !($vs_rel_type = $va_element_data['idno_stub']['_relationship_type'])) {
                                             break;
                                         }
                                         $t_subject->addRelationship($vs_table_name, $vn_rel_id, trim($va_element_data['_relationship_type']), null, null, null, null, array('interstitialValues' => $va_element_data['_interstitial']));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related object lot with relationship %2:", $vs_idno, trim($va_element_data['_relationship_type'])), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                     }
                                     break;
                                 case 'ca_entities':
                                     if ($vn_rel_id = DataMigrationUtils::getEntityID($va_element_data['preferred_labels'], $va_element_data['_type'], $vn_locale_id, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'nonPreferredLabels' => $va_nonpreferred_labels))) {
                                         if (!($vs_rel_type = $va_element_data['_relationship_type']) && !($vs_rel_type = $va_element_data['idno']['_relationship_type'])) {
                                             break;
                                         }
                                         $t_subject->addRelationship($vs_table_name, $vn_rel_id, trim($va_element_data['_relationship_type']), null, null, null, null, array('interstitialValues' => $va_element_data['_interstitial']));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related entity with relationship %2:", $vs_idno, trim($va_element_data['_relationship_type'])), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                     }
                                     break;
                                 case 'ca_places':
                                     if ($vn_rel_id = DataMigrationUtils::getPlaceID($va_element_data['preferred_labels']['name'], $va_element_data['_parent_id'], $va_element_data['_type'], $vn_locale_id, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'nonPreferredLabels' => $va_nonpreferred_labels))) {
                                         if (!($vs_rel_type = $va_element_data['_relationship_type']) && !($vs_rel_type = $va_element_data['idno']['_relationship_type'])) {
                                             break;
                                         }
                                         $t_subject->addRelationship($vs_table_name, $vn_rel_id, trim($va_element_data['_relationship_type']), null, null, null, null, array('interstitialValues' => $va_element_data['_interstitial']));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related place with relationship %2:", $vs_idno, trim($va_element_data['_relationship_type'])), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                     }
                                     break;
                                 case 'ca_collections':
                                     if ($vn_rel_id = DataMigrationUtils::getCollectionID($va_element_data['preferred_labels']['name'], $va_element_data['_type'], $vn_locale_id, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'nonPreferredLabels' => $va_nonpreferred_labels))) {
                                         if (!($vs_rel_type = $va_element_data['_relationship_type']) && !($vs_rel_type = $va_element_data['idno']['_relationship_type'])) {
                                             break;
                                         }
                                         $t_subject->addRelationship($vs_table_name, $vn_rel_id, $vs_rel_type, null, null, null, null, array('interstitialValues' => $va_element_data['_interstitial']));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related collection with relationship %2:", $vs_idno, $vs_rel_type), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                     }
                                     break;
                                 case 'ca_occurrences':
                                     if ($vn_rel_id = DataMigrationUtils::getOccurrenceID($va_element_data['preferred_labels']['name'], $va_element_data['_parent_id'], $va_element_data['_type'], $vn_locale_id, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'nonPreferredLabels' => $va_nonpreferred_labels))) {
                                         if (!($vs_rel_type = $va_element_data['_relationship_type']) && !($vs_rel_type = $va_element_data['idno']['_relationship_type'])) {
                                             break;
                                         }
                                         $t_subject->addRelationship($vs_table_name, $vn_rel_id, $vs_rel_type, null, null, null, null, array('interstitialValues' => $va_element_data['_interstitial']));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related occurrence with relationship %2:", $vs_idno, $vs_rel_type), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                     }
                                     break;
                                 case 'ca_storage_locations':
                                     if ($vn_rel_id = DataMigrationUtils::getStorageLocationID($va_element_data['preferred_labels']['name'], $va_element_data['_parent_id'], $va_element_data['_type'], $vn_locale_id, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'nonPreferredLabels' => $va_nonpreferred_labels))) {
                                         if (!($vs_rel_type = $va_element_data['_relationship_type']) && !($vs_rel_type = $va_element_data['idno']['_relationship_type'])) {
                                             break;
                                         }
                                         $t_subject->addRelationship($vs_table_name, $vn_rel_id, trim($va_element_data['_relationship_type']), null, null, null, null, array('interstitialValues' => $va_element_data['_interstitial']));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related storage location with relationship %2:", $vs_idno, trim($va_element_data['_relationship_type'])), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                     }
                                     break;
                                 case 'ca_list_items':
                                     $va_data_for_rel_table['is_enabled'] = 1;
                                     $va_data_for_rel_table['preferred_labels'] = $va_element_data['preferred_labels'];
                                     if ($vn_rel_id = DataMigrationUtils::getListItemID($va_element_data['_list'], $va_element_data['idno'] ? $va_element_data['idno'] : null, $va_element_data['_type'], $vn_locale_id, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'nonPreferredLabels' => $va_nonpreferred_labels))) {
                                         if (!($vs_rel_type = $va_element_data['_relationship_type']) && !($vs_rel_type = $va_element_data['idno']['_relationship_type'])) {
                                             break;
                                         }
                                         $t_subject->addRelationship($vs_table_name, $vn_rel_id, trim($va_element_data['_relationship_type']), null, null, null, null, array('interstitialValues' => $va_element_data['_interstitial']));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related list item with relationship %2:", $vs_idno, trim($va_element_data['_relationship_type'])), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                     }
                                     break;
                                 case 'ca_object_representations':
                                     if ($vn_rel_id = DataMigrationUtils::getObjectRepresentationID($va_element_data['preferred_labels']['name'], $va_element_data['_type'], $vn_locale_id, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'nonPreferredLabels' => $va_nonpreferred_labels, 'matchMediaFilesWithoutExtension' => true))) {
                                         $t_subject->linkRepresentation($vn_rel_id, null, null, null, null, array('type_id' => trim($va_element_data['_relationship_type']), 'is_primary' => true));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related object representation with:", $vs_idno), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                     }
                                     //
                                     // 									if (($vs_subject_table_name == 'ca_objects') && $va_element_data['media']['media']) {
                                     // 										unset($va_data_for_rel_table['media']);
                                     //
                                     // 										foreach($va_data_for_rel_table as $vs_key => $vm_val) {
                                     // 											// Attributes, including intrinsics are in two-level format, eg. idno is $va_attributes['idno']['idno']
                                     // 											// but addRepresentations() expects intrinsics to be single level (eg. $va_attributes['idno']) so
                                     // 											// we do some rewriting here
                                     // 											if (is_array($vm_val) && isset($vm_val[$vs_key])) {
                                     // 												$va_data_for_rel_table[$vs_key] = $vm_val[$vs_key];
                                     // 											}
                                     // 										}
                                     //
                                     // 										if (!($t_subject->addRepresentation($va_element_data['media']['media'], isset($va_element_data['_type']) ? $va_element_data['_type'] : caGetDefaultItemID('object_representation_types'), $vn_locale_id, 0, 0, true, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on)))) {
                                     // 											$vs_error = join("; ", $t_subject->getErrors());
                                     // 											ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                     // 											if ($vs_item_error_policy == 'stop') {
                                     // 												$o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                     //
                                     // 												if ($o_trans) { $o_trans->rollback(); }
                                     // 												return false;
                                     // 											}
                                     // 										}
                                     // 									}
                                     break;
                                 case 'ca_loans':
                                     if ($vn_rel_id = DataMigrationUtils::getLoanID($va_element_data['preferred_labels']['name'], $va_element_data['_type'], $vn_locale_id, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'nonPreferredLabels' => $va_nonpreferred_labels))) {
                                         if (!($vs_rel_type = $va_element_data['_relationship_type']) && !($vs_rel_type = $va_element_data['idno']['_relationship_type'])) {
                                             break;
                                         }
                                         $t_subject->addRelationship($vs_table_name, $vn_rel_id, trim($va_element_data['_relationship_type']), null, null, null, null, array('interstitialValues' => $va_element_data['_interstitial']));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related loan with relationship %2:", $vs_idno, trim($va_element_data['_relationship_type'])), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                     }
                                     break;
                                 case 'ca_movements':
                                     if ($vn_rel_id = DataMigrationUtils::getMovementID($va_element_data['preferred_labels']['name'], $va_element_data['_type'], $vn_locale_id, $va_data_for_rel_table, array('forceUpdate' => true, 'dontCreate' => $vb_dont_create, 'matchOn' => $va_match_on, 'log' => $o_log, 'transaction' => $o_trans, 'importEvent' => $o_event, 'importEventSource' => $vn_row, 'nonPreferredLabels' => $va_nonpreferred_labels))) {
                                         if (!($vs_rel_type = $va_element_data['_relationship_type']) && !($vs_rel_type = $va_element_data['idno']['_relationship_type'])) {
                                             break;
                                         }
                                         $t_subject->addRelationship($vs_table_name, $vn_rel_id, trim($va_element_data['_relationship_type']), null, null, null, null, array('interstitialValues' => $va_element_data['_interstitial']));
                                         if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related movement with relationship %2:", $vs_idno, trim($va_element_data['_relationship_type'])), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                             ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                             if ($vs_item_error_policy == 'stop') {
                                                 $o_log->logAlert(_t('Import stopped due to mapping error policy'));
                                                 if ($o_trans) {
                                                     $o_trans->rollback();
                                                 }
                                                 return false;
                                             }
                                         }
                                     }
                                     break;
                             }
                             if (is_array($va_element_data['_related_related']) && sizeof($va_element_data['_related_related'])) {
                                 foreach ($va_element_data['_related_related'] as $vs_rel_rel_table => $va_rel_rels) {
                                     foreach ($va_rel_rels as $vn_i => $va_rel_rel) {
                                         if (!($t_rel_instance = $o_dm->getInstanceByTableName($vs_table_name))) {
                                             $o_log->logWarn(_t("[%1] Could not instantiate related table %2", $vs_idno, $vs_table_name));
                                             continue;
                                         }
                                         if ($o_trans) {
                                             $t_rel_instance->setTransaction($o_trans);
                                         }
                                         if ($t_rel_instance->load($vn_rel_id)) {
                                             if ($t_rel_rel = $t_rel_instance->addRelationship($vs_rel_rel_table, $va_rel_rel['id'], $va_rel_rel['_relationship_type'])) {
                                                 $o_log->logInfo(_t('[%1] Related %2 (%3) to related %4 with relationship %5', $vs_idno, $o_dm->getTableProperty($vs_rel_rel_table, 'NAME_SINGULAR'), $va_rel_rel['id'], $t_rel_instance->getProperty('NAME_SINGULAR'), trim($va_rel_rel['_relationship_type'])));
                                             } else {
                                                 if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add related %2 (%3) to related %4 with relationship %5:", $vs_idno, $o_dm->getTableProperty($vs_rel_rel_table, 'NAME_SINGULAR'), $va_rel_rel['id'], $t_rel_instance->getProperty('NAME_SINGULAR'), trim($va_rel_rel['_relationship_type'])), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                                                     ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                                                 }
                                             }
                                         }
                                     }
                                 }
                             }
                         }
                     }
                 }
                 // $t_subject->update();
                 //
                 // 			if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Invalid %2; values were %3: ", $vs_idno, 'attributes', ca_data_importers::formatValuesForLog($va_element_content)), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                 // 				ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                 // 				if ($vs_item_error_policy == 'stop') {
                 // 					$o_log->logAlert(_t('Import stopped due to mapping error policy'));
                 //
                 //
                 // 					$o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_FAILURE__, _t('Failed to import %1', $vs_idno));
                 //
                 // 					if ($o_trans) { $o_trans->rollback(); }
                 // 					return false;
                 // 				}
                 // 			}
                 //
                 $o_log->logDebug(_t('Finished inserting content tree for %1 at %2 seconds into database', $vs_idno, $t->getTime(4)));
                 if (!$vb_output_subject_preferred_label && $t_subject->getPreferredLabelCount() == 0) {
                     $t_subject->addLabel(array($vs_label_display_fld => '???'), $vn_locale_id, null, true);
                     if ($vs_error = DataMigrationUtils::postError($t_subject, _t("[%1] Could not add default label", $vs_idno), __CA_DATA_IMPORT_ERROR__, array('dontOutputLevel' => true, 'dontPrint' => true))) {
                         ca_data_importers::logImportError($vs_error, $va_log_import_error_opts);
                         if ($vs_import_error_policy == 'stop') {
                             $o_log->logAlert(_t('Import stopped due to import error policy'));
                             $o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_FAILURE__, _t('Failed to import %1', $vs_idno));
                             if ($o_trans) {
                                 $o_trans->rollback();
                             }
                             return false;
                         }
                     }
                 }
                 $opa_app_plugin_manager->hookDataPostImport(array('subject' => $t_subject, 'mapping' => $t_mapping, 'content_tree' => &$va_content_tree, 'idno' => &$vs_idno, 'transaction' => &$o_trans, 'log' => &$o_log, 'reader' => $o_reader, 'environment' => $va_environment, 'importEvent' => $o_event, 'importEventSource' => $vn_row));
                 $o_log->logInfo(_t('[%1] Imported %2 as %3 ', $vs_idno, $vs_preferred_label_for_log, $vs_subject_table_name));
                 $o_event->endItem($t_subject->getPrimaryKey(), __CA_DATA_IMPORT_ITEM_SUCCESS__, _t('Imported %1', $vs_idno));
                 ca_data_importers::$s_num_records_processed++;
             }
         }
     }
     $o_log->logInfo(_t('Import of %1 completed using mapping %2: %3 imported/%4 skipped/%5 errors', $ps_source, $t_mapping->get('importer_code'), ca_data_importers::$s_num_records_processed, ca_data_importers::$s_num_records_skipped, ca_data_importers::$s_num_import_errors));
     //if ($vb_show_cli_progress_bar) {
     $o_progress->finish();
     //}
     if ($po_request && isset($pa_options['progressCallback']) && ($ps_callback = $pa_options['progressCallback'])) {
         $ps_callback($po_request, $pn_file_number, $pn_number_of_files, $ps_source, $vn_num_items, $vn_num_items, _t('Import completed'), time() - $vn_start_time, memory_get_usage(true), ca_data_importers::$s_num_records_processed, ca_data_importers::$s_num_import_errors);
     }
     if (isset($pa_options['reportCallback']) && ($ps_callback = $pa_options['reportCallback'])) {
         $va_general = array('elapsedTime' => time() - $vn_start_time, 'numErrors' => ca_data_importers::$s_num_import_errors, 'numProcessed' => ca_data_importers::$s_num_records_processed);
         $ps_callback($po_request, $va_general, ca_data_importers::$s_import_error_list, true);
     }
     if ($pb_dry_run) {
         if ($o_trans) {
             $o_trans->rollback();
         }
         $o_log->logInfo(_t('Rollback successful import run in "dry run" mode'));
     } else {
         if ($o_trans) {
             $o_trans->commit();
         }
     }
     return true;
 }