/** * Fetch a qname for a class. * * @param lang.XPClass class * @return var xml.QName or NULL if no mapping exists */ public function qnameFor(XPClass $class) { if (!isset($this->_c2q[$class->getName()])) { return NULL; } return $this->_qnames[$this->_c2q[$class->getName()]]; }
/** * Creates a new array type instance * * @param lang.XPClass $base * @param lang.Type[] $components */ public function __construct(XPClass $base, array $components) { $this->base = $base; $this->components = $components; parent::__construct(sprintf('%s<%s>', $base->getName(), implode(',', array_map(function ($e) { return $e->getName(); }, $components))), null); }
/** * Tries to get class uri via reflection * * @param lang.XPClass class The class to return the URI for * @return string */ private function uriFor(XPClass $class) { try { $Urimethod = $class->getClassLoader()->getClass()->getMethod('classURI'); $Urimethod->setAccessible(TRUE); return $Urimethod->invoke($class->getClassLoader(), $class->getName()); } catch (Exception $ignored) { return $class->getClassName(); } }
/** * Returns the enumeration member uniquely identified by its name * * @param lang.XPClass class class object * @param string name enumeration member * @return lang.Enum * @throws lang.IllegalArgumentException in case the enum member does not exist or when the given class is not an enum */ public static function valueOf(XPClass $class, $name) { if (!$class->isEnum()) { throw new IllegalArgumentException('Argument class must be lang.XPClass<? extends lang.Enum>'); } try { $prop = $class->reflect()->getStaticPropertyValue($name); if ($class->isInstance($prop)) { return $prop; } } catch (\ReflectionException $e) { throw new IllegalArgumentException($e->getMessage()); } throw new IllegalArgumentException('No such member "' . $name . '" in ' . $class->getName()); }
/** * Show usage * * @param lang.XPClass class */ public static function showUsage(XPClass $class) { // Description if (NULL !== ($comment = $class->getComment())) { self::$err->writeLine(self::textOf($comment)); self::$err->writeLine(str_repeat('=', 72)); } $extra = $details = $positional = array(); foreach ($class->getMethods() as $method) { if (!$method->hasAnnotation('arg')) { continue; } $arg = $method->getAnnotation('arg'); $name = strtolower(preg_replace('/^set/', '', $method->getName())); $comment = self::textOf($method->getComment()); if (0 == $method->numParameters()) { $optional = TRUE; } else { list($first, ) = $method->getParameters(); $optional = $first->isOptional(); } if (isset($arg['position'])) { $details['#' . ($arg['position'] + 1)] = $comment; $positional[$arg['position']] = $name; } else { if (isset($arg['name'])) { $details['--' . $arg['name'] . ' | -' . (isset($arg['short']) ? $arg['short'] : $arg['name'][0])] = $comment; $extra[$arg['name']] = $optional; } else { $details['--' . $name . ' | -' . (isset($arg['short']) ? $arg['short'] : $name[0])] = $comment; $extra[$name] = $optional; } } } // Usage asort($positional); self::$err->write('Usage: $ xpcli ', $class->getName(), ' '); foreach ($positional as $name) { self::$err->write('<', $name, '> '); } foreach ($extra as $name => $optional) { self::$err->write($optional ? '[' : '', '--', $name, $optional ? '] ' : ' '); } self::$err->writeLine(); // Argument details self::$err->writeLine('Arguments:'); foreach ($details as $which => $comment) { self::$err->writeLine('* ', $which, "\n ", str_replace("\n", "\n ", $comment), "\n"); } }
/** * Gets class name (and generic components if this class is a * generic definition) * * @param lang.XPClass class * @return string */ protected static function displayNameOf(XPClass $class) { return $class->getName() . ($class->isGenericDefinition() ? '<' . implode(', ', $class->genericComponents()) . '>' : ''); }
/** * Returns the enumeration members for a given class * * @param lang.XPClass class class object * @return self[] * @throws lang.IllegalArgumentException in case the given class is not an enum */ public static function valuesOf(XPClass $class) { if (!$class->isEnum()) { throw new IllegalArgumentException('Argument class must be lang.XPClass<? extends lang.Enum>'); } $r = []; if ($class->isSubclassOf(self::class)) { foreach ($class->reflect()->getStaticProperties() as $prop) { $class->isInstance($prop) && ($r[] = $prop); } } else { $t = ClassLoader::defineClass($class->getName() . 'Enum', self::class, []); foreach ($class->reflect()->getMethod('getValues')->invoke(null) as $name => $ordinal) { $r[] = $t->newInstance($ordinal, $name); } } return $r; }
/** * Creates a generic type * * @param lang.XPClass self * @param lang.Type[] arguments * @return string created type's literal name */ public static function createGenericType(XPClass $self, array $arguments) { // Verify $annotations = $self->getAnnotations(); if (!isset($annotations['generic']['self'])) { throw new IllegalStateException('Class ' . $self->name . ' is not a generic definition'); } $components = array(); foreach (explode(',', $annotations['generic']['self']) as $cs => $name) { $components[] = ltrim($name); } $cs++; if ($cs !== sizeof($arguments)) { throw new IllegalArgumentException(sprintf('Class %s expects %d component(s) <%s>, %d argument(s) given', $self->name, $cs, implode(', ', $components), sizeof($arguments))); } // Compose names $cn = $qc = ''; foreach ($arguments as $typearg) { $cn .= '¸' . strtr($typearg->literal(), '\\', '¦'); $qc .= ',' . $typearg->getName(); } $name = $self->literal() . '··' . substr($cn, 1); $qname = $self->name . '<' . substr($qc, 1) . '>'; // Create class if it doesn't exist yet if (!class_exists($name, FALSE) && !interface_exists($name, FALSE)) { $meta = xp::$meta[$self->name]; // Parse placeholders into a lookup map $placeholders = array(); foreach ($components as $i => $component) { $placeholders[$component] = $arguments[$i]->getName(); } // Work on sourcecode $cl = self::_classLoaderFor($self->name); if (!$cl || !($bytes = $cl->loadClassBytes($self->name))) { throw new IllegalStateException($self->name); } // Namespaced class if (FALSE !== ($ns = strrpos($name, '\\'))) { $decl = substr($name, $ns + 1); $namespace = substr($name, 0, $ns); $src = 'namespace ' . $namespace . ';'; } else { $decl = $name; $namespace = NULL; $src = ''; } // Replace source $annotation = NULL; $matches = array(); $state = array(0); $counter = 0; $tokens = token_get_all($bytes); for ($i = 0, $s = sizeof($tokens); $i < $s; $i++) { if (T_COMMENT === $tokens[$i][0]) { continue; } else { if (0 === $state[0]) { if (T_ABSTRACT === $tokens[$i][0] || T_FINAL === $tokens[$i][0]) { $src .= $tokens[$i][1] . ' '; } else { if (T_CLASS === $tokens[$i][0] || T_INTERFACE === $tokens[$i][0]) { $meta['class'][DETAIL_GENERIC] = array($self->name, $arguments); $src .= $tokens[$i][1] . ' ' . $decl; array_unshift($state, $tokens[$i][0]); } } continue; } else { if (T_CLASS === $state[0]) { if (T_EXTENDS === $tokens[$i][0]) { $i += 2; $parent = ''; while ((T_STRING === $tokens[$i][0] || T_NS_SEPARATOR === $tokens[$i][0]) && $i < $s) { $parent .= $tokens[$i][1]; $i++; } $i--; '\\' === $parent[0] || ($parent = $namespace . '\\' . $parent); if (isset($annotations['generic']['parent'])) { $xargs = array(); foreach (explode(',', $annotations['generic']['parent']) as $j => $placeholder) { $xargs[] = Type::forName(strtr(ltrim($placeholder), $placeholders)); } $src .= ' extends \\' . self::createGenericType($self->getParentClass(), $xargs); } else { $src .= ' extends ' . $parent; } } else { if (T_IMPLEMENTS === $tokens[$i][0]) { $src .= ' implements'; $counter = 0; $annotation = @$annotations['generic']['implements']; array_unshift($state, 5); } else { if ('{' === $tokens[$i][0]) { array_shift($state); array_unshift($state, 1); $src .= ' {'; } } } continue; } else { if (T_INTERFACE === $state[0]) { if (T_EXTENDS === $tokens[$i][0]) { $src .= ' extends'; $counter = 0; $annotation = @$annotations['generic']['extends']; array_unshift($state, 5); } else { if ('{' === $tokens[$i][0]) { array_shift($state); array_unshift($state, 1); $src .= ' {'; } } continue; } else { if (1 === $state[0]) { // Class body if (T_FUNCTION === $tokens[$i][0]) { $braces = 0; $parameters = $default = array(); array_unshift($state, 3); array_unshift($state, 2); $m = $tokens[$i + 2][1]; $p = 0; $annotations = array($meta[1][$m][DETAIL_ANNOTATIONS], $meta[1][$m][DETAIL_TARGET_ANNO]); } else { if ('}' === $tokens[$i][0]) { $src .= '}'; break; } else { if (T_CLOSE_TAG === $tokens[$i][0]) { break; } } } } else { if (2 === $state[0]) { // Method declaration if ('(' === $tokens[$i][0]) { $braces++; } else { if (')' === $tokens[$i][0]) { $braces--; if (0 === $braces) { array_shift($state); $src .= ')'; continue; } } } if (T_VARIABLE === $tokens[$i][0]) { $parameters[] = $tokens[$i][1]; } else { if ('=' === $tokens[$i][0]) { $p = sizeof($parameters) - 1; $default[$p] = ''; } else { if (T_WHITESPACE !== $tokens[$i][0] && isset($default[$p])) { $default[$p] .= is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i]; } } } } else { if (3 === $state[0]) { // Method body if (';' === $tokens[$i][0]) { // Abstract method if (isset($annotations[0]['generic']['return'])) { $meta[1][$m][DETAIL_RETURNS] = strtr($annotations[0]['generic']['return'], $placeholders); } if (isset($annotations[0]['generic']['params'])) { foreach (explode(',', $annotations[0]['generic']['params']) as $j => $placeholder) { if ('' !== ($replaced = strtr(ltrim($placeholder), $placeholders))) { $meta[1][$m][DETAIL_ARGUMENTS][$j] = $replaced; } } } $annotations = array(); unset($meta[1][$m][DETAIL_ANNOTATIONS]['generic']); array_shift($state); } else { if ('{' === $tokens[$i][0]) { $braces = 1; array_shift($state); array_unshift($state, 4); $src .= '{'; if (isset($annotations[0]['generic']['return'])) { $meta[1][$m][DETAIL_RETURNS] = strtr($annotations[0]['generic']['return'], $placeholders); } if (isset($annotations[0]['generic']['params'])) { $generic = array(); foreach (explode(',', $annotations[0]['generic']['params']) as $j => $placeholder) { if ('' === ($replaced = strtr(ltrim($placeholder), $placeholders))) { $generic[$j] = NULL; } else { $meta[1][$m][DETAIL_ARGUMENTS][$j] = $replaced; $generic[$j] = $replaced; } } foreach ($generic as $j => $type) { if (NULL === $type) { continue; } else { if ('...' === substr($type, -3)) { $src .= $j ? '$·args= array_slice(func_get_args(), ' . $j . ');' : '$·args= func_get_args();'; $src .= ' if (!is(\'' . substr($generic[$j], 0, -3) . '[]\', $·args)) throw new \\lang\\IllegalArgumentException(' . '"Vararg ' . ($j + 1) . ' passed to ".__METHOD__."' . ' must be of ' . $type . ', ".\\xp::stringOf($·args)." given"' . ');'; } else { $src .= ' if (' . (isset($default[$j]) ? '(' . $default[$j] . ' !== ' . $parameters[$j] . ') && ' : '') . '!is(\'' . $generic[$j] . '\', ' . $parameters[$j] . ')) throw new \\lang\\IllegalArgumentException(' . '"Argument ' . ($j + 1) . ' passed to ".__METHOD__."' . ' must be of ' . $type . ', ".\\xp::typeOf(' . $parameters[$j] . ')." given"' . ');'; } } } } $annotations = array(); unset($meta[1][$m][DETAIL_ANNOTATIONS]['generic']); continue; } } } else { if (4 === $state[0]) { // Method body if ('{' === $tokens[$i][0]) { $braces++; } else { if ('}' === $tokens[$i][0]) { $braces--; if (0 === $braces) { array_shift($state); } } } } else { if (5 === $state[0]) { // Implements (class), Extends (interface) if (T_STRING === $tokens[$i][0]) { $rel = ''; while ((T_STRING === $tokens[$i][0] || T_NS_SEPARATOR === $tokens[$i][0]) && $i < $s) { $rel .= $tokens[$i][1]; $i++; } $i--; '\\' === $rel[0] || ($rel = $namespace . '\\' . $rel); if (isset($annotation[$counter])) { $iargs = array(); foreach (explode(',', $annotation[$counter]) as $j => $placeholder) { $iargs[] = Type::forName(strtr(ltrim($placeholder), $placeholders)); } $src .= '\\' . self::createGenericType(new XPClass(new ReflectionClass($rel)), $iargs); } else { $src .= $rel; } $counter++; continue; } else { if ('{' === $tokens[$i][0]) { array_shift($state); array_unshift($state, 1); } } } } } } } } } } } $src .= is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i]; } // Create class // DEBUG fputs(STDERR, "@* ".substr($src, 0, strpos($src, '{'))." -> $qname\n"); eval($src); method_exists($name, '__static') && call_user_func(array($name, '__static')); unset($meta['class'][DETAIL_ANNOTATIONS]['generic']); xp::$meta[$qname] = $meta; xp::$cn[$name] = $qname; // Create alias if no PHP namespace is present and a qualified name exists if (!$ns && strstr($qname, '.')) { class_alias($name, strtr($self->getName(), '.', '\\') . '··' . substr($cn, 1)); } } return $name; }
/** * Registers a class map * * @param xml.QName object * @param lang.XPClass class */ public function registerMapping(QName $qname, XPClass $class) { $this->map[$qname->localpart] = xp::reflect($class->getName()); }