Location: PHPKode > projects > Sierra-php PHP Application Framework > sierra/lib/model/SRA_EntityModeler.php
<?php
// {{{ Header
/*
 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
 | SIERRA : PHP Application Framework  http://code.google.com/p/sierra-php |
 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
 | Copyright 2005 Jason Read                                               |
 |                                                                         |
 | Licensed under the Apache License, Version 2.0 (the "License");         |
 | you may not use this file except in compliance with the License.        |
 | You may obtain a copy of the License at                                 |
 |                                                                         |
 |     http://www.apache.org/licenses/LICENSE-2.0                          |
 |                                                                         |
 | Unless required by applicable law or agreed to in writing, software     |
 | distributed under the License is distributed on an "AS IS" BASIS,       |
 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.|
 | See the License for the specific language governing permissions and     |
 | limitations under the License.                                          |
 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
 */
// }}}

// {{{ Imports
require_once('model/SRA_AttributeValidator.php');
require_once('model/SRA_DaoFactory.php');
require_once('model/SRA_EntityGenerator.php');
// }}}

// {{{ Constants
/**
 * the delimiter for scalar attributes with cardinality stored in the same table 
 * as the entity
 */
define('SRA_AGGREGATE_CARDINALITY_DELIM', '|#|');

/**
 * Constant that identifies a blob data type (large binary object)
 */
define('SRA_DATA_TYPE_BLOB', 'blob');

/**
 * Constant that identifies a boolean data type
 */
define('SRA_DATA_TYPE_BOOLEAN', 'boolean');

/**
 * Constant that identifies a date data type. This data type uses the 
 * SRA_GregorianDate object to encapsulate its value (time is ignored)
 */
define('SRA_DATA_TYPE_DATE', 'date');

/**
 * Constant that identifies the a floating point data type
 */
define('SRA_DATA_TYPE_FLOAT', 'float');

/**
 * Constant that identifies an integer data type
 */
define('SRA_DATA_TYPE_INT', 'int');

/**
 * Constant that identifies a text data type
 */
define('SRA_DATA_TYPE_STRING', 'string');

/**
 * Constant that identifies a timestamp data type. This data type uses the 
 * SRA_GregorianDate object to encapsulate its value (time is used)
 */
define('SRA_DATA_TYPE_TIME', 'time');

/**
 * Defines the default app relative location for generated DAOs and VOs
 */
define('SRA_ENTITY_MODELER_DEFAULT_GENERATE_DIR', SRA_DEFAULT_LIB_DIR . '/model');

/**
 * the dtd element name to assign to the file element
 */
define('SRA_ENTITY_MODELER_FILE_DTD_NAME', 'SraFile');

/**
 * the base entity dtd file
 */
define('SRA_ENTITY_MODELER_BASE_DTD_TPL', SRA_LIB_DIR . '/model/base-entity-dtd.tpl');

/**
 * template to describe an entity using dtd
 */
define('SRA_ENTITY_MODELER_ENTITY_DTD_TPL', SRA_LIB_DIR . '/model/entity-dtd.tpl');

/**
 * the key in the apps resource bundle containing the string identifying 
 * that an attribute may have one or more of the options specified for it each 
 * separated by a space
 */
define('SRA_ENTITY_MODELER_MULTIPLE_OPTIONS_API_RESOURCE', 'entity-model.multiple_options_separated_by_a_space');

/**
 * the default system error resource
 */
define('SRA_ENTITY_MODELER_DEFAULT_SYS_ERR_RESOURCE', 'error.sys');

/**
 * the memory limit to apply when generating a model
 */
define('SRA_ENTITY_MODELER_MEMORY_LIMIT', '128M');

/**
 * the template variable to assign the entity model path to
 */
define('SRA_ENTITY_MODELER_PATH_TPL_VAR', 'entityModelPath');

/**
 * the dtd element name to assign to the view resources element
 */
define('SRA_ENTITY_MODELER_VIEW_RESOURCES_DTD_NAME', 'ViewResources');

/**
 * the parameter type used to specify resource bundles for auto-inclusion into 
 * a view
 */
define('SRA_ENTITY_MODELER_VIEW_RESOURCES_PARAM_TYPE', 'view-resources');

/**
 * the dtd element name to assign to the view resources string element
 */
define('SRA_ENTITY_MODELER_VIEW_RESOURCES_STRING_DTD_NAME', 'ViewResourcesString');

/**
 * a semaphore id used to guarantee that only one process rebuilds the entity 
 * model at a time.
 */
define('SRA_ENTITY_MODELER_BUILD_LOCK', 12345);
// }}}

// {{{ SRA_EntityModeler
/**
 * Facade used to manage and initiate initialization of entity models
 * 
 * @author  Jason Read <hide@address.com>
 * @package sierra.model
 */
class SRA_EntityModeler {
  // {{{ Attributes
  // public attributes
  
  // private attributes
	
  // }}}
  
  // {{{ Operations
  // constructor(s)
	// {{{ SRA_EntityModeler
	/**
	 * Constructor - does nothing
   * @access  private
	 */
	function SRA_EntityModeler() {}
	// }}}
	
  
  // public operations
	
	// {{{ init
	/**
	 * Initializes the entity model for a given app
	 * @param string $appKey the identifier of the app to initialize
	 * @param mixed $entityModel the identifier or the entity model within the 
	 * "app-config" "use-entity-model" "key" attribute or the full path to the 
   * entity model xml file. alternatively, this parameter may be an array 
   * already pulled from xml defining the model
   * @param boolean $forceBuild whether or not to force the build to occur 
   * regardless of whether or not the xml cache file is update to date
   * @param boolean $skipClassGeneration whether or not to skip class generation 
   * (if TRUE only database synchronization will occur)
   * @access  public
	 * @return String
	 */
	function init($appKey, &$entityModel, $forceBuild=FALSE, $skipClassGeneration=FALSE, $resetDaos=FALSE) {
    if (!is_array($entityModel)) {
      $path = file_exists($entityModel) && SRA_Util::beginsWith($entityModel, '/') ? $entityModel : SRA_Controller::getAppConfAttr(array(SRA_ENTITY_MODELER_CONFIG_KEY, $entityModel, 'attributes', 'path'));
      if (!($conf = SRA_EntityModeler::findEntityModelXml($path))) {
        $msg = "SRA_EntityModeler::init: Failed - Unable to locate entity model ${entityModel} for app ${appKey}";
        return SRA_Error::logError($msg, __FILE__, __LINE__);
      }
		
      SRA_Util::printDebug("SRA_EntityModeler::init - Initializing app ${appKey}. Looking for entity model in " . $conf, SRA_Controller::isSysInDebug(), __FILE__, __LINE__);
    }
    else {
      $data =& $entityModel['entity-model'];
    }
    
		$lock = sem_get(SRA_ENTITY_MODELER_BUILD_LOCK, 1);
		sem_acquire($lock);
    
    // check if model is already being build
    if (!is_array($entityModel)) {
      $lockFile = SRA_Controller::getAppTmpDir() . '/.' . str_replace('/', '_', $conf) . '.lock';
      $curPid = file_exists($lockFile) ? SRA_File::toString($lockFile)*1 : NULL;
      if (file_exists($lockFile) && (!$curPid || SRA_Util::isProcessActive($curPid))) {
        $tpl =& SRA_Controller::getAppTemplate();
        $tpl->assign('lockFile', $lockFile);
        $tpl->assign('entityModel', str_replace('.xml', '', basename($conf)));
        $tpl->display(defined('SRA_CONSOLE') && SRA_CONSOLE ? 'sra-model-wait-console.tpl' : 'sra-model-wait.tpl');
        exit;
      }
    }
    
		if (is_array($entityModel) || !SRA_XmlParser::isCached($conf, TRUE) || $forceBuild) {
      if (!is_array($entityModel)) {
        $fp = fopen($lockFile, 'w');
        fwrite($fp, getmypid());
        fclose($fp);
        chmod($lockFile, 0666);
      }
      ini_set('memory_limit', SRA_ENTITY_MODELER_MEMORY_LIMIT);
      
      if ($resetDaos) {
        SRA_DaoFactory::resetDAOs();
      }
      
      if (!is_array($entityModel)) SRA_Controller::registerShutdownMethod($tmp = 'SRA_File', 'unlink', array($lockFile));
			$tpl =& SRA_Controller::getAppTemplate();
			$tpl->assignByRef(SRA_ENTITY_MODELER_PATH_TPL_VAR, $conf);
			
			SRA_Util::printDebug("SRA_EntityModeler::init - ${appKey} is NOT initialized", SRA_Controller::isSysInDebug(), __FILE__, __LINE__);
			if (!$data && SRA_Error::isError($data =& SRA_EntityModeler::getEntityModelData($conf))) {
        sem_release($lock);
				$msg = "SRA_EntityModeler::init: Failed - Entity model '${conf}' could not be parsed.";
				return SRA_Error::logError($msg, __FILE__, __LINE__);
			}
			// abstract
			if (isset($data['attributes']['abstract']) && $data['attributes']['abstract'] == '1') {
        sem_release($lock);
				$msg = "SRA_EntityModeler::init: Failed - Entity model '${conf}' is abstract. Abstract entity models can only be used with the 'import' element in an enclosing non-abstract 'entity-model'";
				SRA_XmlParser::deleteCache($conf, TRUE);
				return SRA_Error::logError($msg, __FILE__, __LINE__);
			}
			
			// get paths
			$generatePath = SRA_File::getRelativePath(SRA_ENTITY_MODELER_DEFAULT_GENERATE_DIR);
			if (array_key_exists('attributes', $data) && array_key_exists('generate-path', $data['attributes'])) {
				$generatePath = SRA_File::getRelativePath($data['attributes']['generate-path']);
			}

      // web services gateway uri
      $wsApiCssUri = isset($data['attributes']['ws-api-css-uri']) ? $data['attributes']['ws-api-css-uri'] : NULL;
      $wsGatewayRewrite = isset($data['attributes']['ws-gateway-rewrite']) && $data['attributes']['ws-gateway-rewrite'] == '1';
      $wsGatewaySkipAppId = isset($data['attributes']['ws-gateway-skip-app']) && $data['attributes']['ws-gateway-skip-app'] == '1';
      $wsGatewayUri = isset($data['attributes']['ws-gateway-uri']) ? $data['attributes']['ws-gateway-uri'] : NULL;
      $wsDb = isset($data['attributes']['ws-db']) ? $data['attributes']['ws-db'] : NULL;
      if ($wsDb && (!function_exists('curl_init') || !function_exists('json_decode'))) {
        sem_release($lock);
				$msg = 'SRA_EntityModeler::init: Failed - curl and json extensions must be installed in order to utilize ws-db';
				SRA_XmlParser::deleteCache($conf, TRUE);
				return SRA_Error::logError($msg, __FILE__, __LINE__);
      }
      
			// global file handling attributes
			$fileDir = FALSE;
			$fileDirUri = FALSE;
			$fileHandling = SRA_FILE_ATTRIBUTE_TYPE_DB;
      $fileIconDir = NULL;
			$fileScriptUri = FALSE;
			$fileScriptRewrite = FALSE;
			if (array_key_exists('attributes', $data) && array_key_exists('file-dir', $data['attributes'])) {
				$fileDir = $data['attributes']['file-dir'];
			}
			if (array_key_exists('attributes', $data) && array_key_exists('file-dir-uri', $data['attributes'])) {
				$fileDirUri = $data['attributes']['file-dir-uri'];
			}
			if (array_key_exists('attributes', $data) && array_key_exists('file-handling', $data['attributes'])) {
				$fileHandling = $data['attributes']['file-handling'];
			}
			if (array_key_exists('attributes', $data) && array_key_exists('file-icon-dir', $data['attributes'])) {
				$fileIconDir = $data['attributes']['file-icon-dir'];
			}
			if (array_key_exists('attributes', $data) && array_key_exists('file-script-uri', $data['attributes'])) {
				$fileScriptUri = $data['attributes']['file-script-uri'];
			}
			if (array_key_exists('attributes', $data) && array_key_exists('file-script-rewrite', $data['attributes'])) {
				$fileScriptRewrite = $data['attributes']['file-script-rewrite'];
			}
      if (array_key_exists('attributes', $data) && array_key_exists('header-tpl', $data['attributes'])) {
				$tpl->assign('headerTemplate', $data['attributes']['header-tpl']);
			}
			$resources = FALSE;
			if (array_key_exists('attributes', $data) && array_key_exists('resources', $data['attributes'])) {
				$resources = $data['attributes']['resources'];
			}
			$sysErrResource = SRA_ENTITY_MODELER_DEFAULT_SYS_ERR_RESOURCE;
			if (array_key_exists('attributes', $data) && array_key_exists('sys-err-resource', $data['attributes'])) {
				$sysErrResource = $data['attributes']['sys-err-resource'];
			}
			// validate directories
			if (!is_dir($generatePath)) {
        sem_release($lock);
				$msg = "SRA_EntityModeler::init: Failed - generatePath ${generatePath} is not valid";
				SRA_XmlParser::deleteCache($conf, TRUE);
				return SRA_Error::logError($msg, __FILE__, __LINE__);
			}
			if (!is_writable($generatePath)) {
        sem_release($lock);
				$msg = "SRA_EntityModeler::init: Failed - generatePath ${generatePath} is not writable";
				SRA_XmlParser::deleteCache($conf, TRUE);
				return SRA_Error::logError($msg, __FILE__, __LINE__);
			}
        
      // global web services
      $webServices = array();
      if (array_key_exists('ws-global', $data)) {
        require_once('model/SRA_WSGlobal.php');
        
        $keys = array_keys($data['ws-global']);
        foreach ($keys as $key) {
          $webServices[] = new SRA_WSGlobal($data['ws-global'][$key]);
          if (!SRA_WSGlobal::isValid($webServices[count($webServices) - 1])) {
            sem_release($lock);
            $msg = "SRA_EntityModeler::init: Failed - global web service ${key} produced error";
            SRA_XmlParser::deleteCache($conf, TRUE);
            return SRA_Error::logError($msg, __FILE__, __LINE__);
          }
        }
      }
			
			// view processors
			$viewProcessors = array();
			if (array_key_exists('view-processor', $data)) {
				$keys = array_keys($data['view-processor']);
				foreach ($keys as $key) {
					$vpAttrs = $data['view-processor'][$key]['attributes'];
					$viewProcessors[$key] = new SRA_EntityViewProcessor($key, $vpAttrs['path'], $vpAttrs['args'], $vpAttrs['input-view'], $vpAttrs['output-file-path'], $vpAttrs['post-process-cmd'], $vpAttrs['pre-process-cmd']);
				}
			}
			
			// create entity generators
			SRA_Util::printDebug("SRA_EntityModeler::init - entity model loaded with: generate-path=${generatePath}", SRA_Controller::isSysInDebug(), __FILE__, __LINE__);
			$keys = array_keys($data['entity']);
			if (!count($keys)) {
        sem_release($lock);
				$msg = "SRA_EntityModeler::init: Failed - XML file '${conf}' does not contain any entity elements";
				SRA_XmlParser::deleteCache($conf, TRUE);
				return SRA_Error::logError($msg, __FILE__, __LINE__);
			}
						
			$ddlCamelCase = FALSE;
			$ddlUpperCase = FALSE;
			$dtdCamelCase = FALSE;
			if (array_key_exists('attributes', $data) && array_key_exists('ddl-camel-case', $data['attributes'])) {
				$ddlCamelCase = $data['attributes']['ddl-camel-case'] == '1';
			}
			if (array_key_exists('attributes', $data) && array_key_exists('ddl-upper-case', $data['attributes'])) {
				$ddlUpperCase = $data['attributes']['ddl-upper-case'] == '1';
			}
			if (array_key_exists('attributes', $data) && array_key_exists('dtd-camel-case', $data['attributes'])) {
				$dtdCamelCase = $data['attributes']['dtd-camel-case'] == '1';
			}
			
			$ddlPath = FALSE;
			$dtdPath = FALSE;
      $dtdUri = FALSE;
			if (array_key_exists('attributes', $data) && array_key_exists('ddl-path', $data['attributes'])) {
				$ddlPath = $data['attributes']['ddl-path'];
			}
			if (array_key_exists('attributes', $data) && array_key_exists('dtd-path', $data['attributes'])) {
				$dtdPath = $data['attributes']['dtd-path'];
			}
      if (array_key_exists('attributes', $data) && array_key_exists('dtd-uri', $data['attributes'])) {
				$dtdUri = $data['attributes']['dtd-uri'];
			}
			if ($ddlPath) {
				if (!is_dir(dirname($ddlPath))) {
					if (substr($ddlPath, 0, 1) != '/') {
						$ddlPath = '/' . $ddlPath;
					}
					$ddlPath = SRA_Controller::getAppDir() . $ddlPath;
				}
				if ((file_exists($ddlPath) && !unlink($ddlPath)) || ($fp = fopen($ddlPath, 'w')) === FALSE) {
          sem_release($lock);
					$msg = "SRA_EntityModeler::init: Failed - XML file '${conf}' contains an invalid ddl-path: $ddlPath";
					SRA_XmlParser::deleteCache($conf, TRUE);
					return SRA_Error::logError($msg, __FILE__, __LINE__);
				}
				fclose($fp);
        chmod($ddlPath, 0666);
			}
			if ($dtdPath) {
				if (!is_dir(dirname($dtdPath))) {
					if (substr($dtdPath, 0, 1) != '/') {
						$dtdPath = '/' . $dtdPath;
					}
					$dtdPath = SRA_Controller::getAppDir() . $dtdPath;
				}
				if ((file_exists($dtdPath) && !unlink($dtdPath)) || ($fp = fopen($dtdPath, 'w')) === FALSE) {
          sem_release($lock);
					$msg = "SRA_EntityModeler::init: Failed - XML file '${conf}' contains an invalid dtd-path: $dtdPath";
					SRA_XmlParser::deleteCache($conf, TRUE);
					return SRA_Error::logError($msg, __FILE__, __LINE__);
				}
        fclose($fp);
        chmod($dtdPath, 0666);
			}
			
			$msgs = array();
			if (isset($data['msg'])) {
				$keys = array_keys($data['msg']);
				foreach ($keys as $key) {
					$msgs[$key] = $data['msg'][$key]['attributes']['resource'];
				}
			}
			
			$nestedQueriesOk = TRUE;
			if (isset($data['attributes']['nested-queries-ok'])) {
				$nestedQueriesOk = $data['attributes']['nested-queries-ok'] == '1';
			}
			
			// views
			if (SRA_Error::isError($globalViews =& SRA_Generator::getViews($data['global-views'][0]['view'], $viewProcessors))) {
        sem_release($lock);
				$msg = "SRA_EntityModeler::init: Failed - Cannot instantiate global views for entity-model '${conf}'";
				SRA_XmlParser::deleteCache($conf, TRUE);
				return SRA_Error::logError($msg, __FILE__, __LINE__);
			}
			if (is_array($globalViews) && count($globalViews)) {
				SRA_EntityView::mergeExtends($globalViews, $globalViews);
			}
      // default views
      $defaultViews = NULL;
      if (isset($data['global-views'][0]['default-view']) && SRA_Error::isError($defaultViews = SRA_Generator::getDefaultViews($data['global-views'][0]['default-view'], $globalViews))) {
        sem_release($lock);
        SRA_XmlParser::deleteCache($conf, TRUE);
        return $defaultViews;
      }
			
      $db = FALSE;
      if (SRA_Controller::hasAppDb()) {
        $db =& SRA_Controller::getAppDb();
      }
			
			// db referential integrity
			if ($db && (strtolower(get_class($db)) == 'sra_databasemysql' || strtolower(get_class($db)) == 'sra_databasesqlite')) {
				$dbRefIntegrity = isset($data['attributes']['ref-integrity']) && $data['attributes']['ref-integrity'] == 'db';
			}
			else if ($db) {
				$dbRefIntegrity = !isset($data['attributes']['ref-integrity']) || $data['attributes']['ref-integrity'] == 'db';
			}
			
			// db
			$dbKey = FALSE;
			if ($db && isset($data['attributes']['db'])) {
				$dbKey = $data['attributes']['db'];
				if (SRA_Error::isError($db =& SRA_Controller::getAppDb($dbKey))) {
          sem_release($lock);
					$msg = "SRA_EntityModeler::init: Failed - Invalid db identifier '${dbKey}'";
					SRA_XmlParser::deleteCache($conf, TRUE);
					return SRA_Error::logError($msg, __FILE__, __LINE__);
				}
			}
			
			static $entityGenerators = array();
			$keys = array_keys($data['entity']);
			// merge up attribute type resources
			foreach ($keys as $key) {
				$akeys = array_keys($data['entity'][$key]['attribute']);
				foreach ($akeys as $akey) {
					if (isset($data['entity'][$key]['attribute'][$akey]['attributes']['type']) && isset($data['entity'][$data['entity'][$key]['attribute'][$akey]['attributes']['type']]['attributes']['resources'])) {
						$baseResources = isset($data['entity'][$key]['attributes']['resources']) ? $data['entity'][$key]['attributes']['resources'] : $resources;
						$baseResources .= ' ' . $data['entity'][$data['entity'][$key]['attribute'][$akey]['attributes']['type']]['attributes']['resources'];
						$baseResources = trim($baseResources);
						$data['entity'][$key]['attributes']['resources'] = $baseResources;
					}
				}
			}
			
			$daoSuffix = isset($data['attributes']['dao-suffix']) ? $data['attributes']['dao-suffix'] : SRA_ENTITY_GENERATOR_DAO_SUFFIX;
			$voSuffix = isset($data['attributes']['vo-suffix']) ? $data['attributes']['vo-suffix'] : SRA_ENTITY_GENERATOR_VO_SUFFIX;
      
			// instantiate entity generators
			foreach ($keys as $key) {
        // entity generate is already cached
        if (isset($entityGenerators[$key]) && SRA_EntityGenerator::isValid($entityGenerators[$key])) continue;
        
				SRA_Util::printDebug("SRA_EntityModeler::init - Initializing entity ${key}", SRA_Controller::isSysInDebug(), __FILE__, __LINE__);
				// extends
				if (isset($data['entity'][$key]['attributes']['extends']) && $data['entity'][$key]['attributes']['extends'] && !array_key_exists($data['entity'][$key]['attributes']['extends'], $data['entity'])) {
          sem_release($lock);
					$msg = "SRA_EntityModeler::init: Failed - entity ${key} cannot extend non-existent entity " . $data['entity'][$key]['attributes']['extends'];
					SRA_XmlParser::deleteCache($conf, TRUE);
					return SRA_Error::logError($msg, __FILE__, __LINE__);
				}
				else if (isset($data['entity'][$key]['attributes']['extends']) && $data['entity'][$key]['attributes']['extends']) {
          // abstract and skipPersistence attributes should not carry over into extending entity
          $entityAbstract = isset($data['entity'][$data['entity'][$key]['attributes']['extends']]['attributes']['abstract']) && $data['entity'][$data['entity'][$key]['attributes']['extends']]['attributes']['abstract'] == '1' ? '1' : '0';
          $entitySkipPersistence = isset($data['entity'][$data['entity'][$key]['attributes']['extends']]['attributes']['skip-persistence']) && $data['entity'][$data['entity'][$key]['attributes']['extends']]['attributes']['skip-persistence'] == '1' ? '1' : '0';
          unset($data['entity'][$data['entity'][$key]['attributes']['extends']]['attributes']['abstract']);
          unset($data['entity'][$data['entity'][$key]['attributes']['extends']]['attributes']['skip-persistence']);
          
					$am =& SRA_ArrayManager::merge($data['entity'][$key], $data['entity'][$data['entity'][$key]['attributes']['extends']]);
					
          // reset extended attributes
          $data['entity'][$data['entity'][$key]['attributes']['extends']]['attributes']['abstract'] = $entityAbstract;
          $data['entity'][$data['entity'][$key]['attributes']['extends']]['attributes']['skip-persistence'] = $entitySkipPersistence;
          
          $data['entity'][$key] =& $am->getData();
					$baseResources = isset($data['entity'][$key]['attributes']['resources']) ? $data['entity'][$key]['attributes']['resources'] : $resources;
					$baseResources = isset($data['entity'][$data['entity'][$key]['attributes']['extends']]['attributes']['resources']) ? $baseResources  . ' ' . $data['entity'][$data['entity'][$key]['attributes']['extends']]['attributes']['resources'] : $baseResources;
					$baseResources = trim($baseResources);
					if ($baseResources) {
						$data['entity'][$key]['attributes']['resources'] = $baseResources;
					}
					SRA_Util::printDebug("SRA_EntityModeler::init - Merged entity ${key} with extended entity " . $data['entity'][$key]['attributes']['extends'], SRA_Controller::isSysInDebug(), __FILE__, __LINE__);
				}

				if (!array_key_exists('file-dir', $data['entity'][$key]['attributes'])) {
					$data['entity'][$key]['attributes']['file-dir'] = $fileDir;
				}
				if (!array_key_exists('file-dir-uri', $data['entity'][$key]['attributes'])) {
					$data['entity'][$key]['attributes']['file-dir-uri'] = $fileDirUri;
				}
				if (!array_key_exists('file-handling', $data['entity'][$key]['attributes'])) {
					$data['entity'][$key]['attributes']['file-handling'] = $fileHandling;
				}
        if (!array_key_exists('file-icon-dir', $data['entity'][$key]['attributes'])) {
					$data['entity'][$key]['attributes']['file-icon-dir'] = $fileIconDir;
				}
				if (!array_key_exists('file-script-uri', $data['entity'][$key]['attributes'])) {
					$data['entity'][$key]['attributes']['file-script-uri'] = $fileScriptUri;
				}
				if (!array_key_exists('file-script-rewrite', $data['entity'][$key]['attributes'])) {
					$data['entity'][$key]['attributes']['file-script-rewrite'] = $fileScriptRewrite;
				}
				// add auto primary key attribute if applicable
				if (isset($data['attributes']['auto-pk']) && $data['attributes']['auto-pk'] && 
				    !isset($data['entity'][$key]['attributes']['primary-key']) && 
				    (!isset($data['entity'][$key]['attributes']['abstract']) || $data['entity'][$key]['attributes']['abstract'] != '1') && 
						(!isset($data['entity'][$key]['attributes']['skip-persistence']) || $data['entity'][$key]['attributes']['skip-persistence'] != '1')) {
					$pkName = str_replace('{$name}', strtolower(substr($data['entity'][$key]['attributes']['key'], 0, 1)) . substr($data['entity'][$key]['attributes']['key'], 1), $data['attributes']['auto-pk']);
					$data['entity'][$key]['attributes']['primary-key'] = $pkName;
					$data['entity'][$key]['attribute'][$pkName] = array('attributes' => array('key' => $pkName, 'sequence' => '1'));
				}
        
				if (!SRA_EntityGenerator::isValid($entityGenerators[$key] = new SRA_EntityGenerator($dbKey, isset($dbRefIntegrity) ? $dbRefIntegrity : FALSE, $ddlCamelCase, $ddlUpperCase, $dtdCamelCase, $msgs, $nestedQueriesOk, $generatePath, $keys, $conf, $resources, $sysErrResource, $data['entity'][$key], $viewProcessors, $globalViews, $defaultViews, $daoSuffix, $voSuffix, $wsDb))) {
          sem_release($lock);
					$msg = "SRA_EntityModeler::init: Failed - Entity ${key} could not be initialzed";
					SRA_XmlParser::deleteCache($conf, TRUE);
					return SRA_Error::logError($msg, __FILE__, __LINE__);
				}
        $ajkeys = array_keys($entityGenerators[$key]->_webServices);
        foreach($ajkeys as $ajkey) {
          $webServices[] =& $entityGenerators[$key]->_webServices[$ajkey];
        }
			}
      
      // save web services
      if ($wsGatewayUri && $webServices) {
        require_once('model/SRA_WSGateway.php');
        if (SRA_Error::isError(SRA_WSGateway::cacheServices($webServices, $wsGatewayUri, $wsGatewayRewrite, $wsGatewaySkipAppId, $wsApiCssUri))) {
          sem_release($lock);
          $msg = "SRA_EntityModeler::init: Failed - Unable to cache web services";
          SRA_XmlParser::deleteCache($conf, TRUE);
          return SRA_Error::logError($msg, __FILE__, __LINE__);
        }
      }
      
      // set xml related template variables
      $pieces = explode('.', basename($dtdPath));
      $pieces = explode('_', $pieces[0]);
      $docType = $pieces[0];
      $tpl->assign('docType', $docType);
      $tpl->assign('dtdPath', $dtdPath);
      $tpl->assign('dtdUri', $dtdUri);
      $tpl->assign('fileElementName', $dtdCamelCase ? SRA_ENTITY_MODELER_FILE_DTD_NAME : SRA_Util::camelCaseToDashes(SRA_ENTITY_MODELER_FILE_DTD_NAME));
      $tpl->assign('viewResourcesElementName', $dtdCamelCase ? SRA_ENTITY_MODELER_VIEW_RESOURCES_DTD_NAME : SRA_Util::camelCaseToDashes(SRA_ENTITY_MODELER_VIEW_RESOURCES_DTD_NAME));
      $tpl->assign('viewResourcesStringElementName', $dtdCamelCase ? SRA_ENTITY_MODELER_VIEW_RESOURCES_STRING_DTD_NAME : SRA_Util::camelCaseToDashes(SRA_ENTITY_MODELER_VIEW_RESOURCES_STRING_DTD_NAME));
			
			// generate entities
      if (!$skipClassGeneration) {
        $keys = array_keys($entityGenerators);
        foreach ($keys as $key) {
          if ($entityGenerators[$key]->_abstract) { continue; }
          if (SRA_Error::isError($entityGenerators[$key]->setEntityGenerators($entityGenerators))) {
            sem_release($lock);
            $msg = "SRA_EntityModeler::init: Failed - SRA_EntityGenerators could not be set for entity ${key}";
            SRA_XmlParser::deleteCache($conf, TRUE);
            return SRA_Error::logError($msg, __FILE__, __LINE__);
          }
        }
        foreach ($keys as $key) {
          if ($entityGenerators[$key]->_abstract) { continue; }
          $msg = "SRA_EntityModeler::init - Generating entity ${key}";
          SRA_Util::printDebug($msg, SRA_Controller::isSysInDebug(), __FILE__, __LINE__);
          if (SRA_Error::isError($entityGenerators[$key]->generate())) {
            sem_release($lock);
            $msg = "SRA_EntityModeler::init: Failed - Entity ${key} could not be generated";
            SRA_XmlParser::deleteCache($conf, TRUE);
            return SRA_Error::logError($msg, __FILE__, __LINE__);
          }
        }
      }
			
			// instantiate indexes
			$indexes = array();
      if (!isset($data['index'])) {
        $data['index'] = array();
      }
			$ikeys = array_keys($data['index']);
			foreach ($ikeys as $ikey) {
				if (!isset($data['index'][$ikey]['attributes']['columns']) || !isset($data['index'][$ikey]['attributes']['table'])) {
          sem_release($lock);
					$msg = "SRA_EntityModeler::init: Failed - Index definition ${ikey} does not contain a columns or table attribute";
					SRA_XmlParser::deleteCache($conf, TRUE);
					return SRA_Error::logError($msg, __FILE__, __LINE__);
				}
				$index = new SRA_SchemaIndex($data['index'][$ikey]['attributes']['columns'], $ikey, $data['index'][$ikey]['attributes']['table']);
				if (isset($data['index'][$ikey]['attributes']['modifier'])) {
					$index->setModifier($data['index'][$ikey]['attributes']['modifier']);
				}
				if (isset($data['index'][$ikey]['attributes']['postfix'])) {
					$index->setModifier($data['index'][$ikey]['attributes']['postfix']);
				}
				if (!isset($indexes[$index->getTable()])) {
					$indexes[$index->getTable()] = array();
				}
				$indexes[$index->getTable()][] = $index;
			}
			
			// generate database schema
			$tables = array();
			foreach ($keys as $key) {
				if ($entityGenerators[$key]->_abstract || $entityGenerators[$key]->usesWsDb()) { continue; }
				$schema =& $entityGenerators[$key]->getSchema();
				if (SRA_EntitySchema ::isValid($schema)) {
					$etables =& $schema->getTables();
					$tkeys = array_keys($etables);
					foreach ($tkeys as $tkey) {
						if (!isset($tables[$etables[$tkey]->getName()])) {
							$tables[$etables[$tkey]->getName()] =& $etables[$tkey];
							if (isset($indexes[$etables[$tkey]->getName()])) {
								$tables[$etables[$tkey]->getName()]->addIndex($indexes[$etables[$tkey]->getName()]);
							}
						}
						else {
							$tables[$etables[$tkey]->getName()]->addColumn($etables[$tkey]->getColumns(), $etables[$tkey]->isPrimary());
							$tables[$etables[$tkey]->getName()]->addIndex($etables[$tkey]->getIndexes());
							if ($etables[$tkey]->isPrimary() && $etables[$tkey]->getPrimaryKey()) {
								$tables[$etables[$tkey]->getName()]->setPrimaryKey($etables[$tkey]->getPrimaryKey());
							}
							if (!$etables[$tkey]->isPrimary() && $etables[$tkey]->getPrimaryKey()) {
								$tables[$etables[$tkey]->getName()]->addPrimaryKey($etables[$tkey]->getPrimaryKey());
							}
						}
					}
				}
			}
			
			// synchronize database schema
			if (isset($data['attributes']['mysql-table-type'])) {
				$mysqlTableType = $data['attributes']['mysql-table-type'];
			}
			else if (isset($dbRefIntegrity) && $dbRefIntegrity) {
				$mysqlTableType = 'InnoDB';
			}
			else {
				$mysqlTableType = 'MyISAM';
			}
      if (count($tables) && !$db) {
        sem_release($lock);
        $msg = 'SRA_EntityModeler::init: Failed - Persistence cannot be enabled if no database has been defined';
        return SRA_Error::logError($msg, __FILE__, __LINE__);
      }
			else if (count($tables) && $data['attributes']['abstract'] != '1' && isset($data['attributes']['sync-schema']) && $data['attributes']['sync-schema'] == '1') {
				$tkeys = array_keys($tables);
        // determine synchronization ordering where tables that reference others are synchronized after those tables that they reference
        $references = array();
				foreach ($tkeys as $tkey) {
					$columns =& $tables[$tkey]->getColumns();
          $ckeys = array_keys($columns);
          foreach($ckeys as $ckey) {
            if (is_object($columns[$ckey])) {
              if ($columns[$ckey]->getReferences()) {
                if (!isset($references[$tkey])) {
                  $references[$tkey] = array();
                }
                $references[$tkey][] = $columns[$ckey]->getReferences();
              }
            }
            else {
              unset($columns[$ckey]);
            }
          }
				}
        $tableOrder = array();
        $tableKeys = array_keys($tables);
        // arrange table creation in correct order when db referential integrity is used
        if ($dbRefIntegrity) {
          $count = 0;
          while(count($tableKeys)) {
            foreach(array_keys($tableKeys) as $tableKey) {
              $tk = $tableKeys[$tableKey];
              if (isset($references[$tk])) {
                foreach(array_keys($references[$tk]) as $ref) {
                  $refTable = explode('(', $references[$tk][$ref]);
                  $refTable = $refTable[0];
                  if (in_array($refTable, $tableOrder)  || $refTable == $tk) {
                    unset($references[$tk][$ref]);
                  }
                }
                if (!count($references[$tk])) {
                  unset($references[$tk]);
                }
              }
              if (!isset($references[$tk]) && !in_array($tk, $tableOrder)) {
                $tableOrder[] = $tk;
                unset($tableKeys[$tableKey]);
              }
            }
            if ($count == 100) {
              sem_release($lock);
              $msg = 'SRA_EntityModeler::init: Failed - Could not resolve relational depedencies to create table synchronization order. Un-resolved dependencies include: ' . implode(', ', $tableKeys);
              SRA_XmlParser::deleteCache($conf, TRUE);
              return SRA_Error::logError($msg, __FILE__, __LINE__);
            }
            $count++;
          }
        }
        // order doesn't matter because db does not enforce referential integrity
        else {
          $tableOrder = $tableKeys;
        }
        
        $imports = array();
        $keys = array_keys($entityGenerators);
				foreach ($tableOrder as $tkey) {
					if (!($ret = SRA_EntityModeler::_synchronizeTable($db, $tables[$tkey], $dbRefIntegrity, $mysqlTableType))) {
            sem_release($lock);
						$msg = 'SRA_EntityModeler::init: Failed - Could not synchronize table: ' . $tables[$tkey]->getName();
						SRA_XmlParser::deleteCache($conf, TRUE);
						return SRA_Error::logError($msg, __FILE__, __LINE__);
					}
          // table was created, check for on-create-import
          else if ($ret === 1) {
            foreach ($keys as $key) {
              if ($entityGenerators[$key]->_table == $tables[$tkey]->getName() && $entityGenerators[$key]->_onCreateImport) {
                $files = explode(' ', $entityGenerators[$key]->_onCreateImport);
                foreach ($files as $import) {
                  if ($file = SRA_File::getRelativePath('etc', $import)) {
                    $imports[] = $file;
                  }
                  else if (!$entityGenerators[$key]->_ignoreBadImport) {
                    $msg = "SRA_EntityModeler::init: Error - Invalid import ${import}";
                    SRA_Error::logError($msg, __FILE__, __LINE__);
                  }
                }
              }
            }
          }
				}
        // process imports
        foreach($imports as $import) {
          $pieces = file($import);
          foreach ($pieces as $query) {
            $query = str_replace("\n", '', $query);
            if (trim($query) && substr($query, 0, 2) != '/*' && SRA_Error::isError($db->execute($query))) {
              $msg = "SRA_EntityModeler::init: Error - Invalid query ${query} in import ${import}";
              SRA_Error::logError($msg, __FILE__, __LINE__);
            }
          }
        }
			}
			
			// write ddl to file
			if ($db && count($tables) && $ddlPath) {
        require_once('sql/SRA_DatabaseMySql.php');
        
				$tpl->assign('isMysql', SRA_DatabaseMySql::isValid($db));
				$tpl->assign('mysqlTableType', $mysqlTableType);
				$tpl->assign('dbRefIntegrity', $dbRefIntegrity);
				$tpl->assignByRef('tables', $tables);
				$tpl->assignByRef('db', $db);
				$fp = fopen($ddlPath, 'w');
				$buffer =& $tpl->fetch(dirname(realpath(__FILE__)) . '/ddl-header.tpl');
				$keys = array_keys($tables);
				foreach ($keys as $key) {
					$tpl->assignByRef('table', $tables[$key]);
					$buffer .= $tpl->fetch(dirname(realpath(__FILE__)) . '/table.tpl');
				}
				fwrite($fp, $buffer);
				fclose($fp);
			}
			
			// write dtd to file
			if ($dtdPath) {
        $fp = fopen($dtdPath, 'w');
        $keys = array_keys($entityGenerators);
        $entityElements = array();
        foreach ($keys as $key) {
          if (!SRA_EntityGenerator::isValid($entityGenerators[$key]) || $entityGenerators[$key]->_abstract) { continue; }
          $entityElements[] = $entityGenerators[$key]->getDtdName();
        }
        $tpl->assignByRef('entityElements', $entityElements);
        fwrite($fp, $tpl->fetch(SRA_ENTITY_MODELER_BASE_DTD_TPL));
        foreach ($keys as $key) {
          if (!SRA_EntityGenerator::isValid($entityGenerators[$key]) || $entityGenerators[$key]->_abstract) { continue; }
          $tpl->assignByRef('entity', $entityGenerators[$key]);
          $tpl->assignByRef(SRA_APP_RB_NAME, $entityGenerators[$key]->getResources());
          fwrite($fp, $tpl->fetch(SRA_ENTITY_MODELER_ENTITY_DTD_TPL));
        }
        fclose($fp);
			}
			
      if (!is_array($entityModel)) SRA_File::unlink($lockFile);
		}
		else {
			SRA_Util::printDebug("SRA_EntityModeler::init - ${appKey} is already initialized", SRA_Controller::isSysInDebug(), __FILE__, __LINE__);
		}
		sem_release($lock);
	}
	// }}}
	
	// {{{ toString
	/**
	 * Returns a string representation of this object
   * @access  public
	 * @return String
	 */
	function toString() {
		return SRA_Util::objectToString($this);
	}
	// }}}
	
	
	// Static methods
	
	// {{{ findEntityModelXml
	/**
	 * Returns the path to an entity model xml file based on the $conf specified. 
	 * the search order is defined in the SRA_File::getRelativePath() api where 
   * $dir is "conf". returns NULL if $conf is not valid
	 * @param string $conf the fixed or relative identifier for the entity model
   * @access public
	 * @return mixed
	 */
	function findEntityModelXml($conf) {
    static $priorDirs = array();
		$conf = SRA_Util::endsWith($conf, '.xml') ? $conf : $conf . '.xml';
    for($i = count($priorDirs) - 1; $i >= 0; $i--) {
      $dir = $priorDirs[$i];
      if (file_exists("${dir}/${conf}")) {
        $conf = "${dir}/${conf}";
        break;
      }
    }
		if ($dir = SRA_File::getRelativePath('etc', $conf)) {
      $priorDirs[] = dirname($dir);
    }
    else if ($dir = SRA_File::getRelativePath(NULL, $conf)) {
      $priorDirs[] = dirname($dir);
    }
    return is_file($dir) ? $dir : NULL;
	}
	// }}}
	
	// {{{ getEntityModelData
	/**
	 * Returns the data associated with an entity model based on the $conf 
	 * specified. the search order is defined in the SRA_File::getRelativePath() api
	 * where $dir is "conf"
	 * 
	 * @param string $conf the fixed or relative identifier for the entity model
   * @param boolean $preserveAbstract whether or not to preserve the abstract flag 
   * in the $data
   * @param boolean $ignoreBad whether or not to ignore invalid $conf params. by 
   * default if a $conf is not valid (i.e. invalid path) and error will be logged 
   * and returned
   * @access  public
	 * @return array or SRA_Error
	 */
	function & getEntityModelData($conf, $preserveAbstract = TRUE, $ignoreBad=FALSE) {
		$tmp = $conf;
		if (!$conf = SRA_EntityModeler::findEntityModelXml($conf)) {
			$msg = "SRA_EntityModeler::getEntityModelData: Failed - Unable to locate entity model ${tmp}";
			return $ignoreBad ? NULL : SRA_Error::logError($msg, __FILE__, __LINE__);
		}
		if (SRA_Error::isError($xmlParser =& SRA_XmlParser::getXmlParser($conf, TRUE))) {
			$msg = "SRA_EntityModeler::getEntityModelData: Failed - XML file '${conf}' could not be parsed.";
			return SRA_Error::logError($msg, __FILE__, __LINE__);
		}
		$data =& $xmlParser->getData('entity-model');
		
		// specify abstract entities
		if (isset($data['attributes']['abstract']) && $data['attributes']['abstract'] == '1') {
			$keys = array_keys($data['entity']);
			foreach ($keys as $key) {
				$data['entity'][$key]['attributes']['abstract'] = '1';
			}
		}
		
		// abstract
		if (!$preserveAbstract) {
			unset($data['attributes']['abstract']);
		}
		
		// import
		if (isset($data['attributes']['import'])) {
			$imports = array();
			$keys = explode(' ', $data['attributes']['import']);
			foreach ($keys as $key) {
				if (SRA_Error::isError($imports[$key] =& SRA_EntityModeler::getEntityModelData($key, FALSE, isset($data['attributes']['ignore-bad-import']) && $data['attributes']['ignore-bad-import'] == '1'))) {
					$msg = "SRA_EntityModeler::getEntityModelData: Failed - Unable to parse import entity model ${key}";
					SRA_XmlParser::deleteCache($conf, TRUE);
					return SRA_Error::logError($msg, __FILE__, __LINE__);
				}
        if ($imports[$key]) {
          $am =& SRA_ArrayManager::merge($data, $imports[$key]);
          if (isset($imports[$key]['attributes']['resources']) && $data['attributes']['resources'] != $imports[$key]['attributes']['resources']) {
            $data['attributes']['resources'] .= ' ' . $imports[$key]['attributes']['resources'];
          }
        }
			}
			$data =& $am->getData();
		}
		
		return $data;
	}
	// }}}
  
  
	// {{{ getAppEntityClasses
	/**
	 * returns a hash of all of the entity class names specified in $entityModelId 
   * for the current active app. the hash will be indexed by the name of the 
   * generated PHP source file containing that class. returns NULL for no 
   * entities. the returned hash will have the same order as 'getAppEntityIds' 
   * (i.e. index 0 in 'getAppEntityIds' is the same as index 0 in 
   * 'getAppEntityClasses' - although the latter is a hash)
   * @param boolean $classKey when TRUE, the key in the returned associative 
   * array will be  the classname and the value will be the path
	 * @param mixed $entityModel the id or ids (an array) of the entity model. if 
   * not specified, the return value will contain all of the entities for all of
   * the entity models defined for the current application
	 * @access public static
	 * @return hash
	 */
	function getAppEntityClasses($classKey=FALSE, $entityModel=NULL) {
    $entityModels = $entityModel ? (is_array($entityModel) ? $entityModel : array($entityModel)) : SRA_EntityModeler::getAppEntityModels();
    $entityClasses = array();
    foreach($entityModels as $entityModel) {
      if (!$conf = SRA_EntityModeler::findEntityModelXml(SRA_Controller::getAppConfAttr(array(SRA_ENTITY_MODELER_CONFIG_KEY, $entityModel, 'attributes', 'path')))) {
        $msg = "SRA_EntityModeler::getAppEntityModelEntityIds: Failed - Unable to locate entity model ${entityModel} for app ${appKey}";
        return SRA_Error::logError($msg, __FILE__, __LINE__);
      }
      if (SRA_Error::isError($data =& SRA_EntityModeler::getEntityModelData($conf))) {
        $msg = "SRA_EntityModeler::getAppEntityModelEntityIds: Failed - Entity model '${conf}' could not be parsed.";
        return SRA_Error::logError($msg, __FILE__, __LINE__);
      }
      
      $voSuffix = isset($data['attributes']['vo-suffix']) ? $data['attributes']['vo-suffix'] : SRA_ENTITY_GENERATOR_VO_SUFFIX;
			$generatePath = SRA_File::getRelativePath(array_key_exists('attributes', $data) && array_key_exists('generate-path', $data['attributes']) ? $data['attributes']['generate-path'] : SRA_ENTITY_MODELER_DEFAULT_GENERATE_DIR);
      
      $keys = array_keys($data['entity']);
      foreach ($keys as $key) {
        $name = $key . $voSuffix;
        $path = $generatePath . '/' . $name . '.' . SRA_SYS_PHP_EXTENSION;
        $classKey ? $entityClasses[$name] = $path : $entityClasses[$path] = $name;
      }
    }
		return $entityClasses ? $entityClasses : NULL;
	}
	// }}}
  
  
	// {{{ getAppEntityClass
	/**
	 * returns the name of the class for $entityId
	 * @param string $entityId the id (key="SomeEntity") of the entity to return 
   * the classname for
	 * @access public static
	 * @return string
	 */
	function getAppEntityClass($entityId) {
    if (in_array($entityId, $entityIds = SRA_EntityModeler::getAppEntityIds())) {
      $entityClasses = SRA_EntityModeler::getAppEntityClasses();
      $keys = array_keys($entityClasses);
      return $entityClasses[$keys[array_search($entityId, $entityIds)]];
    }
    else if ($entityId) {
      return str_replace('.' . SRA_SYS_PHP_EXTENSION, '', basename($entityId));
    }
	}
	// }}}
	
	
	// {{{ getAppEntityIds
	/**
	 * returns an array of all of the entity ids specified in $entityModelId for 
   * the current active app. returns NULL for no entities
	 * @param mixed $entityModel the id or ids (an array) of the entity model. if 
   * not specified, the return value will contain all of the entity ids for all 
   * of the entity models defined for the current application
	 * @access public static
	 * @return string[]
	 */
	function getAppEntityIds($entityModel=NULL) {
    $entityModels = $entityModel ? (is_array($entityModel) ? $entityModel : array($entityModel)) : SRA_EntityModeler::getAppEntityModels();
    $entityIds = array();
    foreach($entityModels as $entityModel) {
      if (!$conf = SRA_EntityModeler::findEntityModelXml(SRA_Controller::getAppConfAttr(array(SRA_ENTITY_MODELER_CONFIG_KEY, $entityModel, 'attributes', 'path')))) {
        $msg = "SRA_EntityModeler::getAppEntityModelEntityIds: Failed - Unable to locate entity model ${entityModel} for app ${appKey}";
        return SRA_Error::logError($msg, __FILE__, __LINE__);
      }
      if (SRA_Error::isError($data =& SRA_EntityModeler::getEntityModelData($conf))) {
        $msg = "SRA_EntityModeler::getAppEntityModelEntityIds: Failed - Entity model '${conf}' could not be parsed.";
        return SRA_Error::logError($msg, __FILE__, __LINE__);
      }
      $keys = array_keys($data['entity']);
      foreach ($keys as $key) {
        $entityIds[] = $key;
      }
    }
		return $entityIds ? $entityIds : NULL;
	}
	// }}}
  
  
	// {{{ getAppEntityModels
	/**
	 * returns an array representing the ids of all of the entity models defined 
   * for the current application. returns NULL if no entity models are defined 
	 * @access public static
	 * @return string[]
	 */
	function getAppEntityModels() {
		$entityModelIds = $entityModelIds;
		if (SRA_Controller::getAppConfAttr(SRA_ENTITY_MODELER_CONFIG_KEY)) {
			$entityModelIds = array_keys(SRA_Controller::getAppConfAttr(SRA_ENTITY_MODELER_CONFIG_KEY));
		}
		return $entityModelIds;
	}
	// }}}
  
  
	// {{{ getAppEntityPath
	/**
	 * returns the path to the file containing the class for $entityId
	 * @param string $entityId the id (key="SomeEntity") of the entity to return 
   * the path for
	 * @access public static
	 * @return string
	 */
	function getAppEntityPath($entityId) {
    if (in_array($entityId, $entityIds = SRA_EntityModeler::getAppEntityIds())) {
      $paths = array_keys(SRA_EntityModeler::getAppEntityClasses());
      return $paths[array_search($entityId, $entityIds)];
    }
    else {
      $file = str_replace('.' . SRA_SYS_PHP_EXTENSION, '', basename($entityId));
      if ($files = SRA_File::getFileList(SRA_Controller::getAppLibDir(), $file . '.' . SRA_SYS_PHP_EXTENSION, TRUE)) {
        return $files[0];
      }
    }
	}
	// }}}
  
  
	// {{{ getAppEntityUnitTests
	/**
	 * returns a hash of all of the entity unit tests specified in $entityModelId 
   * where the key is the name of the entity and the value is the path to the 
   * unit test
	 * @param mixed $entityModel the id or ids (an array) of the entity model. if 
   * not specified, the return value will contain all of the entities for all of
   * the entity models defined for the current application
	 * @access public static
	 * @return hash
	 */
	function getAppEntityUnitTests($entityModel=NULL) {
    $entityModels = $entityModel ? (is_array($entityModel) ? $entityModel : array($entityModel)) : SRA_EntityModeler::getAppEntityModels();
    $unitTests = array();
    foreach($entityModels as $entityModel) {
      if (!$conf = SRA_EntityModeler::findEntityModelXml(SRA_Controller::getAppConfAttr(array(SRA_ENTITY_MODELER_CONFIG_KEY, $entityModel, 'attributes', 'path')))) {
        $msg = "SRA_EntityModeler::getAppEntityUnitTests: Failed - Unable to locate entity model ${entityModel} for app ${appKey}";
        return SRA_Error::logError($msg, __FILE__, __LINE__);
      }
      if (SRA_Error::isError($data =& SRA_EntityModeler::getEntityModelData($conf))) {
        $msg = "SRA_EntityModeler::getAppEntityUnitTests: Failed - Entity model '${conf}' could not be parsed.";
        return SRA_Error::logError($msg, __FILE__, __LINE__);
      }
      
      $voSuffix = isset($data['attributes']['vo-suffix']) ? $data['attributes']['vo-suffix'] : SRA_ENTITY_GENERATOR_VO_SUFFIX;
      
      foreach (array_keys($data['entity']) as $key) {
        if (isset($data['entity'][$key]['attributes']['unit-test'])) {
          $unitTest = $data['entity'][$key]['attributes']['unit-test'];
          if (!SRA_Util::endsWith($unitTest, '.' . SRA_SYS_PHP_EXTENSION)) $unitTest .= '.' . SRA_SYS_PHP_EXTENSION;
          $unitTests[$key . $voSuffix] = file_exists($unitTest) ? $unitTest : SRA_Controller::getAppLibDir() . '/' . $unitTest;
        }
      }
    }
		return $unitTests ? $unitTests : NULL;
	}
	// }}}
  
  
	// {{{ isAppEntityClassValid
	/**
	 * returns TRUE if $entityClass is a valid class for an entity for the current 
   * application
	 * @param string $entityClass the classname of the entity to validate
	 * @access public static
	 * @return boolean
	 */
	function isAppEntityClassValid($entityClass) {
    return in_array($entityClass, SRA_EntityModeler::getAppEntityClasses());
	}
	// }}}
  
  
	// {{{ isAppEntityIdValid
	/**
	 * returns TRUE if $entityId is a valid id of an entity for the current 
   * application
	 * @param string $entityId the id (key="SomeEntity") of the entity to validate
	 * @access public static
	 * @return boolean
	 */
	function isAppEntityIdValid($entityId) {
    return in_array($entityId, SRA_EntityModeler::getAppEntityIds());
	}
	// }}}
	
	
	// {{{ isValid
	/**
	 * Static method that returns true if the object parameter is a SRA_EntityModeler object.
	 * @param object $object the object to validate
	 * @access public  static
	 * @return boolean
	 */
	function isValid(&$object) {
		return (is_object($object) && (!isset($object->err) || !SRA_Error::isError($object->err)) && strtolower(get_class($object)) == 'sra_entitymodeler');
	}
	// }}}
  
  // {{{ isValidType
  /**
   * Static method that returns TRUE if the object parameter references a valid 
   * data type
   * @param string $type the type to validate
   * @access public
   * @return boolean
   */
  function isValidType($type) {
    return ($type == SRA_DATA_TYPE_BLOB || 
            $type == SRA_DATA_TYPE_BOOLEAN || $type == SRA_DATA_TYPE_DATE || 
            $type == SRA_DATA_TYPE_TIME || $type == SRA_DATA_TYPE_FLOAT || 
            $type == SRA_DATA_TYPE_INT || $type == SRA_DATA_TYPE_STRING);
  }
  // }}}
	
  
  // private operations
	
	// {{{ _synchronizeTable
	/**
	 * used to synchronize a single table against in the database. if the table 
	 * specified does not contain any records (SELECT count(*) returns 0), it will 
	 * be dropped and re-created. if it does not exist, it will be created. if it 
	 * exists and contains records, each column will be checked and created if it 
	 * does not already exist. returns: FALSE on failure, TRUE on success and 1 if 
   * the table did not exist and was created as a result
	 *
	 * @param SRA_Database $db the database connection to use
	 * @param SRA_SchemaTable $table the table to synchronize
	 * @param boolean $dbRefIntegrity whether or not referential integrity should 
	 * be enforced at the database layer
	 * @param string $mysqlTableType the table type for mysql databases only
	 * @access	public
	 * @return	boolean
	 */
	function _synchronizeTable(& $db, & $table, $dbRefIntegrity, $mysqlTableType=FALSE) {
    require_once('sql/SRA_DatabaseMySql.php');
    
		$tpl =& SRA_Controller::getAppTemplate();
		$tpl->assign('isMysql', SRA_DatabaseMySql::isValid($db));
		$tpl->assign('mysqlTableType', $mysqlTableType);
		$tpl->assign('dbRefIntegrity', $dbRefIntegrity);
		$tpl->assignByRef('db', $db);
		
		$query = 'SELECT count(*) FROM ' . $table->getName();
		$results =& $db->fetch($query, array(SRA_DATA_TYPE_INT), FALSE, FALSE, SRA_ERROR_OPERATIONAL);
		$row = FALSE;
		if (SRA_ResultSet::isValid($results)) {
			$row =& $results->next();
		}
		
    $created = FALSE;
		$err = FALSE;
		// table does not exist or has no records, re-create
		if (SRA_Error::isError($results) || $row[0] == 0) {
      if (SRA_Error::isError($results)) {
        $created = TRUE;
      }
			$query = 'DROP TABLE ' . $table->getName() . ($dbRefIntegrity ? ' CASCADE' : '');
			if (!SRA_Error::isError($results) && SRA_Error::isError($db->execute($query, FALSE, SRA_ERROR_OPERATIONAL))) {
				$err = TRUE;
			}
			else {
				$tpl->assignByRef('table', $table);
				$queries = explode(';', $tpl->fetch(dirname(realpath(__FILE__)) . '/table.tpl'));
				foreach ($queries as $query) {
					if (!trim($query)) {
						continue;
					}
					$err = SRA_Error::isError($db->execute($query)) ? TRUE : NULL;
					if ($err) {
						break;
					}
				}
			}
		}
		// table exists
		else {
			// check if each column exists
			$columns =& $table->getColumns();
			$ckeys = array_keys($columns);
			foreach ($ckeys as $ckey) {
				$query = 'SELECT count(' . $columns[$ckey]->getName() . ') FROM ' . $table->getName();
				if (SRA_Error::isError($db->fetch($query, array(SRA_DATA_TYPE_INT), FALSE, FALSE, SRA_ERROR_OPERATIONAL))) {
					// column does not exist, add to table
					$query = 'ALTER TABLE ' . $table->getName() . ' ADD COLUMN ' . $columns[$ckey]->getName() . ' ' . $db->getColumnDefinition($table, $columns[$ckey]);
					$err = SRA_Error::isError($db->execute($query)) ? TRUE : NULL;
					if ($err) {
						break;
					}
				}
			}
		}
		return $err ? FALSE : ($created ? 1 : TRUE);
	}
	// }}}
  
}
// }}}
?>
Return current item: Sierra-php PHP Application Framework