Rev 3 |
    Go to most recent revision |
    Blame |
    Compare with Previous |
    Last modification |
    View Log
    | RSS feed
  
  
    1
  
  
<?php
require_once 'global.inc';
/**
 * A list of language features with URNs definitions
 * for reference links.
 */
class FeatureList
{
  public $versions = array();
  
  /**
   * Versions of implementations that are considered safe.
   * A feature is considered safe if it does not require
   * an implementation version above these versions.
   *
   * @var Array[string=>string]
   */
  public $safeVersions = array();
  
  /**
   * URNs that can be used for reference links.
   *
   * @var Array[string=>string]
   */
  protected $urns = array();
  
  /**
   * The list of language features
   *
   * @var array[Features]
   */
  protected $items = array();
  
  /**
   * Determines the number of printed items the table headings should be repeated
   *
   * @var int
   */
  protected $headerRepeat = 25;
  /**
   * Initializes the FeatureList object
   *
   * @param array|Object $a
   * @return FeatureList
   */
  public function __construct
($a)
  {
    $aVars = get_class_vars(__CLASS__);
    while ((list($key, $value) = each($aVars)))
    {
      if (isset($a[$key]))
      {
        $this->$key = $a[$key];
      }
    }
    
    /* Inform items of ourself so that URNs can be used for links */
    if (is_array($this->items))
    {
      foreach ($this->items as &$item)
      {
        $item->setList(&$this);
      }
    }
    
    /* resolve URN references that are URNs */
    if (is_array($this->urns))
    {
      foreach ($this->urns as &$urn)
      {
        if (($url = $this->resolveURN($urn)))
        {
          $urn = $url;
        }
      }
    }
  }
  
  public function printHeaders
()
  {
    foreach ($this->versions as $ver)
    {
?>
          <th>
<?php echo $ver; ?></th>
<?php
    }
  }
    
  /**
   * Prints the list of features.
   *
   * @see Feature::printMe()
   */
  public function printItems
()
  {
    $counter = 0;
    $headerRepeat = $this->headerRepeat;
    $repeatHeaders = ($headerRepeat > 1);
    
    foreach ($this->items as $feature)
    {
      if ($feature instanceof Feature
)
      {
        if ($repeatHeaders
            && $counter > 1
            && $counter % $headerRepeat === 0)
        {
?>
        <tr class="header">
          <th>Feature</th>
          
<?php $this->printHeaders(); ?>
        </tr>
<?php
        }
        
        $feature->printMe();
        
        $counter++;
      }
    }
  }
  
  /**
   * Resolves a URN according to the value of the
   * object's <code>$urn</code> property.
   *
   * @param string $urn
   *   URN to be resolved
   * @return string|boolean
   *   The resolved URN if successful,
   *   <code>false</code> otherwise.
   */
  public function resolveURN
($urn)
  {
    if (is_array($this->urns))
    {
      $reURN = '|^(.+?):(?!//)|';
      
      if (preg_match($reURN, $urn, $m) && isset($this->urns[$m[1]]))
      {
        return preg_replace($reURN, $this->urns[$m[1]], $urn);
      }
    }
    return $urn;
  }
}
/**
 * A language feature.
 */
class Feature
{
  /**
   * Fragment identifiers to be defined for quickly accessing
   * the feature description.
   *
   * @var Array[String]
   */
  protected $anchors = array();
  
  /**
   * Value of the explanatory <code>title</code> attribute for the feature.
   *
   * @var string
   */
  protected $title = '';
  
  /**
   * Name or example code of the feature
   *
   * @var string
   */
  protected $content = '';
    
  /**
   * Description of the feature.  Displayed directly if code is missing,
   * otherwise used as `title' attribute value.
   *
   * @var string
   */
  protected $descr = '';
  
  /**
   * Versions that support this feature
   *
   * @var Array
   */
  protected $versions = array();
    
  /**
   * Reference to the FeatureList that this feature belongs to
   *
   * @var FeatureList
   */
  protected $list = null;
  
  public function setList
($oList)
  {
    $this->list =& $oList;
  }
  /**
   * Creates a new Feature object, using values from the passed parameters
   * array.
   *
   * @param array|Object $params
   * @return Feature
   */
  public function __construct
($params = array())
  {
    while ((list($key, $value) = each($params)))
    {
//      if ($key != 'versions')
//      {
        if (property_exists
(__CLASS__, $key))
        {
          $this->$key = $value;
        }
//      }
//      else
//      {
//        $o =& $this->$key;
//
//        while ((list($key2, $value2) = each($value)))
//        {
//          $o[$key2] = $value2;
//        }
//      }
    }
  }
  
  /**
   * Returns <code>' class="safe"'</code> if the feature
   * can be considered safe.  The required information
   * is stored in the <code>safeVersions</code> property
   * of the associated <code>FeatureList</code> object.
   *
   * @return string
   * @see FeatureList::defaultSafeVersions
   */
  protected function getSafeStr
()
  {
    if (!is_null($this->list))
    {
      foreach ($this->list->safeVersions as $impl => &$safeVer)
      {
        $thisImplVer =& $this->versions[$impl];
        if (is_array($thisImplVer))
        {
          if (isset($thisImplVer['tested']) && !is_bool($thisImplVer['tested']))
          {
            $thisImplVer =& $thisImplVer['tested'];
          }
          else
        {
            $thisImplVer =& $thisImplVer[0];
          }
        }
        
        /* DEBUG */
        // echo " $impl=$thisImplVer ";
        
        if ($thisImplVer === '-' || $thisImplVer > $safeVer)
        {
          return '';
        }
      }
      
      return ' class="safe"';
    }
    else
    {
      return '';
    }
  }
  
  protected function getTitleStr
()
  {
    if (!empty($this->title))
    {
      return " title=\"{$this->title}\"";
    }
    else
    {
      return '';
    }
  }
  
  protected function getAnchors
()
  {
    $result = array();
  
    foreach ($this->anchors as $anchor)
    {
      $result[] = "<a name=\"{$anchor}\"";
      
      if (preg_match('/^[a-z][a-z0-9_:.-]*/i', $anchor))
      {
        $result[] = " id=\"{$anchor}\"";
      }
      
      $result[] = '></a>';
    }
    
    return join('', $result);
  }
  protected function getAssumed
($v)
  {
    if (is_array($v) && isset($v['assumed']) && $v['assumed'])
    {
      return ' class="assumed"';
    }
    
    return '';
  }
  protected function getTested
($v)
  {
    if (is_array($v) && isset($v['tested']) && $v['tested'])
    {
      return ' class="tested"';
    }
    
    return '';
  }
  
  /**
   * Returns the version of a feature.
   *
   * @param string|VersionInfo $vInfo
   * @return mixed
   */
  protected function getVer
($vInfo)
  {
    if (is_array($vInfo))
    {
      /* TODO: Return all versions: documented, assumed, and tested */
      $vNumber = (isset($vInfo['tested'])
                  && gettype($vInfo['tested']) !== 'boolean')
                     ? 
$vInfo['tested']
                     : $vInfo[0];
      $section = isset($vInfo['section'])
                   ? 
' <span class="section" title="Specification section">['
                      . $vInfo['section'] . ']</span>'
                   : '';
      
      if (isset($vInfo['urn']))
      {
        if ($this->list instanceof FeatureList
)
        {
          $url = $this->list->resolveURN($vInfo['urn']);
          $vNumber = '<a href="' . $url . '">' . $vNumber
            . ($section ? 
$section  : '') . '</a>';
        }
      }
      else if ($section)
      {
        $vNumber .= $section;
      }
      return $vNumber === '-' ? 
'<span title="No">−</span>' : $vNumber;
    }
    else
    {
      return $vInfo === '-' ? 
'<span title="No">−</span>' : $vInfo;
    }
  }
    
  protected static 
function shl
($s)
  {
    /* stub */
  }
  
  public function printMe
()
  {
    ?>
<tr
<?php echo $this->getSafeStr(); ?>>
          <th
<?php echo $this->getTitleStr(); ?>>
<?php
            echo $this->getAnchors();
            echo /*preg_replace_callback(
              '#(<code>)(.+?)(</code>)#',
              array('self', 'shl'),*/
              preg_replace('/…/', '…', $this->content)/*)*/;
            ?></th>
<?php
    $versions = $this->versions;
    if (!is_null($this->list))
    {
      $versions =& $this->list->versions;
    }
    static 
$row = 0;
    $row++;
    
    $column = 0;
    
    foreach ($versions as $key => $value)
    {
      $column++;
      $id = "td$row-$column";
      $ver = $this->versions[$key];
?>
          <td id="
<?php echo $id; ?>"
<?php
            echo $this->getAssumed($ver) . $this->getTested($ver);
            if (!$key)
            {
              if (!empty($ver))
              {
                echo ' title="Test code: ' . htmlspecialchars(stripslashes($ver)) . '"';
              }
              else
              {
                echo ' title="Not applicable: No automated test case'
                  . ' is available for this feature.  Please click'
                  . ' the feature code in the first column to run'
                  . ' a manual test."';
              }
            }
            ?>>
<?php
            if ($key)
            {
              echo $this->getVer($ver);
            }
            else
            {
              if (!empty($ver))
              {
                ?><script type="text/javascript">
  // <![CDATA[
  var s = test(
<?php echo $ver; ?>, '<span title="Yes">+</span>', '<span title="No">−</span>');
  tryThis("document.write(s);",
          "document.getElementById('
<?php echo $id; ?>').appendChild("
          + "document.createTextNode(s));");
  // ]]>
</script>
<?php
              }
              else
              {
                echo '<abbr>N/A</abbr>';
              }
            }
            ?></td>
<?php
    }
?>
        </tr>
<?php
  }
}
  
?>