/**
  * Execute this configuration handler.
  *
  * @param      string An absolute filesystem path to a configuration file.
  * @param      string An optional context in which we are currently running.
  *
  * @return     string Data to be written to a cache file.
  *
  * @throws     <b>AgaviUnreadableException</b> If a requested configuration
  *                                             file does not exist or is not
  *                                             readable.
  * @throws     <b>AgaviParseException</b> If a requested configuration file is
  *                                        improperly formatted.
  *
  * @author     Dominik del Bondio <*****@*****.**>
  * @since      0.11.0
  */
 public function execute($config, $context = null)
 {
     $pathParts = pathinfo($config);
     // unlike basename, filename does not contain the extension, which is what we need there
     $lookupPaths = AgaviLocale::getLookupPath($pathParts['filename']);
     $lookupPaths[] = 'root';
     $data = array('layout' => array('orientation' => array('lines' => 'top-to-bottom', 'characters' => 'left-to-right')));
     foreach (array_reverse($lookupPaths) as $basename) {
         $filePath = $pathParts['dirname'] . '/' . $basename . '.' . $pathParts['extension'];
         if (is_readable($filePath)) {
             $ldmlTree = AgaviConfigCache::parseConfig($filePath, false, $this->getValidationFile(), $this->parser);
             $this->prepareParentInformation($ldmlTree);
             $this->parseLdmlTree($ldmlTree->ldml, $data);
         }
     }
     $dayMap = array('sun' => AgaviDateDefinitions::SUNDAY, 'mon' => AgaviDateDefinitions::MONDAY, 'tue' => AgaviDateDefinitions::TUESDAY, 'wed' => AgaviDateDefinitions::WEDNESDAY, 'thu' => AgaviDateDefinitions::THURSDAY, 'fri' => AgaviDateDefinitions::FRIDAY, 'sat' => AgaviDateDefinitions::SATURDAY);
     // fix the day indices for all day fields
     foreach ($data['calendars'] as $calKey => &$calValue) {
         // skip the 'default' => '' key => value pair
         if (is_array($calValue)) {
             if (isset($calValue['days']['format'])) {
                 foreach ($calValue['days']['format'] as $formatKey => &$formatValue) {
                     if (is_array($formatValue)) {
                         $newData = array();
                         foreach ($formatValue as $day => $value) {
                             $newData[$dayMap[$day]] = $value;
                         }
                         $formatValue = $newData;
                     }
                 }
             }
             if (isset($calValue['days']['stand-alone'])) {
                 foreach ($calValue['days']['stand-alone'] as $formatKey => &$formatValue) {
                     if (is_array($formatValue)) {
                         $newData = array();
                         foreach ($formatValue as $day => $value) {
                             $newData[$dayMap[$day]] = $value;
                         }
                         $formatValue = $newData;
                     }
                 }
             }
         }
     }
     $code = array();
     $code[] = 'return ' . var_export($data, true) . ';';
     return $this->generate($code, $config);
 }
 private function loadDefaultFiles($tm, &$files)
 {
     $default = $tm->getDefaultDomain();
     $translator = $tm->getDomainTranslator($default, AgaviTranslationManager::MESSAGE);
     $locale = $tm->getCurrentLocale();
     if ($translator instanceof AppKitGettextTranslator) {
         foreach ($translator->getDomainPaths() as $domain => $path) {
             foreach (AgaviLocale::getLookupPath($tm->getCurrentLocale()->getIdentifier()) as $prefix) {
                 $result = $this->loadFile($path, $prefix);
                 if ($result) {
                     $files[$domain] = $result;
                 }
             }
         }
     }
 }
 /**
  * Get the full, resolved stream location name to the template resource.
  *
  * @return     string A PHP stream resource identifier.
  *
  * @throws     AgaviException If the template could not be found.
  *
  * @author     David Zülke <*****@*****.**>
  * @since      0.11.0
  */
 public function getResourceStreamIdentifier()
 {
     $template = $this->getParameter('template');
     if ($template === null) {
         // no template set, we return null so nothing gets rendered
         return null;
     }
     $args = array();
     if (AgaviConfig::get('core.use_translation')) {
         // i18n is enabled, build a list of sprintf args with the locale identifier
         foreach (AgaviLocale::getLookupPath($this->context->getTranslationManager()->getCurrentLocaleIdentifier()) as $identifier) {
             $args[] = array('locale' => $identifier);
         }
     }
     if (empty($args)) {
         $args[] = array();
         // add one empty arg to always trigger target lookups (even if i18n is disabled etc.)
     }
     $scheme = $this->getParameter('scheme');
     // FIXME: a simple workaround for broken ubuntu and debian packages (fixed already), we can remove that for final 0.11
     if ($scheme != 'file' && !in_array($scheme, stream_get_wrappers())) {
         throw new AgaviException('Unknown stream wrapper "' . $scheme . '", must be one of "' . implode('", "', stream_get_wrappers()) . '".');
     }
     $check = $this->getParameter('check');
     $attempts = array();
     // try each of the patterns
     foreach ((array) $this->getParameter('targets', array()) as $pattern) {
         // try pattern with each argument list
         foreach ($args as $arg) {
             $target = AgaviToolkit::expandVariables($pattern, array_merge(array_filter($this->getParameters(), 'is_scalar'), array_filter($this->getParameters(), 'is_null'), $arg));
             // FIXME (should they fix it): don't add file:// because suhosin's include whitelist is empty by default, does not contain 'file' as allowed uri scheme
             if ($scheme != 'file') {
                 $target = $scheme . '://' . $target;
             }
             if (!$check || is_readable($target)) {
                 return $target;
             }
             $attempts[] = $target;
         }
     }
     // no template found, time to throw an exception
     throw new AgaviException('Template "' . $template . '" could not be found. Paths tried:' . "\n" . implode("\n", $attempts));
 }
 /**
  * Loads the data from the data file for the given domain with the current 
  * locale.
  *
  * @param      string The domain to load the data for.
  *
  * @author     Dominik del Bondio <*****@*****.**>
  * @since      0.11.0
  */
 public function loadDomainData($domain)
 {
     $localeName = $this->locale->getIdentifier();
     $localeNameBases = AgaviLocale::getLookupPath($localeName);
     if (!isset($this->domainPaths[$domain])) {
         if (!$this->domainPathPattern) {
             throw new AgaviException('Using domain "' . $domain . '" which has no path specified');
         } else {
             $basePath = $this->domainPathPattern;
         }
     } else {
         $basePath = $this->domainPaths[$domain];
     }
     $basePath = AgaviToolkit::expandVariables($basePath, array('domain' => $domain));
     $data = array();
     foreach ($localeNameBases as $localeNameBase) {
         $fileName = AgaviToolkit::expandVariables($basePath, array('locale' => $localeNameBase));
         if ($fileName === $basePath) {
             // no replacing of $locale happened
             $fileName = $basePath . '/' . $localeNameBase . '.mo';
         }
         if (is_readable($fileName)) {
             $fileData = AgaviGettextMoReader::readFile($fileName);
             // instead of array_merge, which doesn't handle null bytes in keys properly. careful, the order matters here.
             $data = $fileData + $data;
         }
     }
     $headers = array();
     if (count($data)) {
         $headerData = str_replace("\r", '', $data['']);
         $headerLines = explode("\n", $headerData);
         foreach ($headerLines as $line) {
             $values = explode(':', $line, 2);
             // skip empty / invalid lines
             if (count($values) == 2) {
                 $headers[$values[0]] = $values[1];
             }
         }
     }
     $this->pluralFormFunc = null;
     if (isset($headers['Plural-Forms'])) {
         $pf = $headers['Plural-Forms'];
         if (preg_match('#nplurals=\\d+;\\s+plural=(.*)$#D', $pf, $match)) {
             $funcCode = $match[1];
             $validOpChars = array(' ', 'n', '!', '&', '|', '<', '>', '(', ')', '?', ':', ';', '=', '+', '*', '/', '%', '-');
             if (preg_match('#[^\\d' . preg_quote(implode('', $validOpChars), '#') . ']#', $funcCode, $errorMatch)) {
                 throw new AgaviException('Illegal character ' . $errorMatch[0] . ' in plural form ' . $funcCode);
             }
             // add parenthesis around all ternary expressions. This is done
             // to make the ternary operator (?) have precedence over the delimiter (:)
             // This will transform
             // "a ? 1 : b ? c ? 3 : 4 : 2" to "(a ? 1 : (b ? (c ? 3 : 4) : 2))" and
             // "a ? b ? c ? d ? 5 : 4 : 3 : 2 : 1" to "(a ? (b ? (c ? (d ? 5 : 4) : 3) : 2) : 1)"
             // "a ? b ? c ? 4 : 3 : d ? 5 : 2 : 1" to "(a ? (b ? (c ? 4 : 3) : (d ? 5 : 2)) : 1)"
             // "a ? b ? c ? 4 : 3 : d ? 5 : e ? 6 : 2 : 1" to "(a ? (b ? (c ? 4 : 3) : (d ? 5 : (e ? 6 : 2))) : 1)"
             $funcCode = rtrim($funcCode, ';');
             $parts = preg_split('#(\\?|\\:)#', $funcCode, -1, PREG_SPLIT_DELIM_CAPTURE);
             $parenthesisCount = 0;
             $unclosedParenthesisCount = 0;
             $firstParenthesis = true;
             $funcCode = '';
             for ($i = 0, $c = count($parts); $i < $c; ++$i) {
                 $lastPart = $i > 0 ? $parts[$i - 1] : null;
                 $part = $parts[$i];
                 $nextPart = $i + 1 < $c ? $parts[$i + 1] : null;
                 if ($nextPart == '?') {
                     if ($lastPart == ':') {
                         // keep track of parenthesis which need to be closed
                         // directly after this ternary expression
                         ++$unclosedParenthesisCount;
                         --$parenthesisCount;
                     }
                     $funcCode .= ' (' . $part;
                     ++$parenthesisCount;
                 } elseif ($lastPart == ':') {
                     $funcCode .= $part . ') ';
                     if ($unclosedParenthesisCount > 0) {
                         $funcCode .= str_repeat(')', $unclosedParenthesisCount);
                         $unclosedParenthesisCount = 0;
                     }
                     --$parenthesisCount;
                 } else {
                     $funcCode .= $part;
                 }
             }
             if ($parenthesisCount > 0) {
                 // add the missing top level parenthesis
                 $funcCode .= str_repeat(')', $parenthesisCount);
             }
             $funcCode .= ';';
             $funcCode = 'return ' . str_replace('n', '$n', $funcCode);
             $this->pluralFormFunc = create_function('$n', $funcCode);
         }
     }
     $this->domainData[$domain] = array('headers' => $headers, 'msgs' => $data);
 }
 /**
  * This method gets called by the translation manager when the default locale
  * has been changed.
  *
  * @param      string The new default locale.
  *
  * @author     Dominik del Bondio <*****@*****.**>
  * @author     David Zülke <*****@*****.**>
  * @since      0.11.0
  */
 public function localeChanged($newLocale)
 {
     $this->locale = $newLocale;
     $this->groupingSeparator = $this->locale->getNumberSymbolGroup();
     $this->decimalSeparator = $this->locale->getNumberSymbolDecimal();
     $format = $this->locale->getCurrencyFormat('__default');
     if (is_array($this->customFormat)) {
         $format = AgaviToolkit::getValueByKeyList($this->customFormat, AgaviLocale::getLookupPath($this->locale->getIdentifier()), $format);
     } elseif ($this->customFormat) {
         $format = $this->customFormat;
     }
     $this->setFormat($format);
 }
 /**
  * This method gets called by the translation manager when the default locale
  * has been changed.
  *
  * @param      string The new default locale.
  *
  * @author     Dominik del Bondio <*****@*****.**>
  * @author     David Zülke <*****@*****.**>
  * @since      0.11.0
  */
 public function localeChanged($newLocale)
 {
     $this->locale = $newLocale;
     $format = null;
     // ze default
     if (is_array($this->customFormat)) {
         $format = AgaviToolkit::getValueByKeyList($this->customFormat, AgaviLocale::getLookupPath($this->locale->getIdentifier()));
     } elseif ($this->customFormat && !$this->translationDomain) {
         $format = $this->customFormat;
     }
     if ($format === null || $this->isDateSpecifier($format)) {
         $format = $this->resolveSpecifier($this->locale, $format, $this->type);
     }
     $this->setFormat($format);
 }
 /**
  * Returns a new AgaviLocale object from the given identifier.
  *
  * @param      string The locale identifier
  * @param      bool   Force a new instance even if an identical one exists.
  *
  * @return     AgaviLocale The locale instance which matches the available
  *                         locales most.
  *
  * @author     Dominik del Bondio <*****@*****.**>
  * @author     David Zülke <*****@*****.**>
  * @since      0.11.0
  */
 public function getLocale($identifier, $forceNew = false)
 {
     // enable shortcut notation to only set options to the current locale
     if ($identifier[0] == '@' && $this->currentLocaleIdentifier) {
         $idData = AgaviLocale::parseLocaleIdentifier($this->currentLocaleIdentifier);
         $identifier = $idData['locale_str'] . $identifier;
         $newIdData = AgaviLocale::parseLocaleIdentifier($identifier);
         $idData['options'] = array_merge($idData['options'], $newIdData['options']);
     } else {
         $idData = AgaviLocale::parseLocaleIdentifier($identifier);
     }
     // this doesn't care about the options
     $availableLocale = $this->availableLocales[$this->getLocaleIdentifier($identifier)];
     // if the user wants all options reset he supplies an 'empty' option set (identifier ends with @)
     if (substr($identifier, -1) == '@') {
         $idData['options'] = array();
     } else {
         $idData['options'] = array_merge($availableLocale['identifierData']['options'], $idData['options']);
     }
     if (($atPos = strpos($identifier, '@')) !== false) {
         $identifier = $availableLocale['identifierData']['locale_str'] . substr($identifier, $atPos);
     } else {
         $identifier = $availableLocale['identifier'];
     }
     if (!$forceNew && isset($this->localeCache[$identifier])) {
         return $this->localeCache[$identifier];
     }
     if (!isset($this->localeDataCache[$idData['locale_str']])) {
         $lookupPath = AgaviLocale::getLookupPath($availableLocale['identifierData']);
         $cldrDir = AgaviConfig::get('core.cldr_dir');
         $data = null;
         foreach ($lookupPath as $localeName) {
             $fileName = $cldrDir . '/locales/' . $localeName . '.xml';
             if (is_readable($fileName)) {
                 $data = (include AgaviConfigCache::checkConfig($fileName));
                 break;
             }
         }
         if ($data === null) {
             throw new AgaviException('No data available for locale ' . $identifier);
         }
         if ($availableLocale['identifierData']['territory']) {
             $territory = $availableLocale['identifierData']['territory'];
             if (isset($this->supplementalData['territories'][$territory]['currencies'])) {
                 $slice = array_slice($this->supplementalData['territories'][$territory]['currencies'], 0, 1);
                 $currency = current($slice);
                 $data['locale']['currency'] = $currency['currency'];
             }
         }
         $this->localeDataCache[$idData['locale_str']] = $data;
     }
     $data = $this->localeDataCache[$idData['locale_str']];
     if (isset($idData['options']['calendar'])) {
         $data['locale']['calendar'] = $idData['options']['calendar'];
     }
     if (isset($idData['options']['currency'])) {
         $data['locale']['currency'] = $idData['options']['currency'];
     }
     if (isset($idData['options']['timezone'])) {
         $data['locale']['timezone'] = $idData['options']['timezone'];
     }
     $locale = new AgaviLocale();
     $locale->initialize($this->context, $availableLocale['parameters'], $identifier, $data);
     if (!$forceNew) {
         $this->localeCache[$identifier] = $locale;
     }
     return $locale;
 }
 /**
  * This method gets called by the translation manager when the default locale
  * has been changed.
  *
  * @param      AgaviLocale The new default locale.
  *
  * @author     David Zülke <*****@*****.**>
  * @since      0.11.0
  */
 public function localeChanged($newLocale)
 {
     $this->locale = $newLocale;
     $this->currentData = AgaviToolkit::getValueByKeyList($this->domainData, AgaviLocale::getLookupPath($this->locale->getIdentifier()), array());
 }