Как разорвать цикл for-each, когда установлено значение, чтобы предотвратить двойную оценку? - PullRequest
1 голос
/ 19 августа 2010

ОБНОВЛЕНИЕ: Улучшено объяснение, поэтому, надеюсь, теперь проблема ясна:)

Привет всем!

У меня есть XML, в котором переходы FSMобъявлены (и события и состояния вне поля).

Вот упрощенная версия XML, которая также вызывает проблему, которая описана ниже:

<?xml version="1.0" encoding="UTF-8"?>
<FSM name="MediaPlayer">
    <state name="FastForward"/>
    <state name="PlayingMediaFile"/>
    <transition name="FastForward to FastForward" source="FastForward" target="FastForward">
        <trigger name="FastForwardButtonPressed" action="playingSpeed * 2" guard="currentMediaFile.CurrentPosition U+003C currentMediaFile.Lengtha"/>
    </transition>
    <transition name="FastForward to PlayingMediaFile" source="FastForward" target="PlayingMediaFile">
        <trigger name="PlayButtonPressed" action="playingSpeed = 1" guard=""/>
    </transition>
    <event name="FastForwardButtonPressed"/>
    <event name="PlayButtonPressed"/>
</FSM>

Моя цель - создать FSM в C # с XSLT.Реализация FSM должна быть в соответствии с государственной схемой.Из-за этого мне нужно создать класс для каждого штата.В каждом классе методы будут представлять события.Поскольку классы являются производными от абстрактного класса, который содержит все методы (события), каждый класс / состояние должен реализовывать все методы (события).Конечно, не все методы фактически реализованы, потому что государство не использует все события.Поэтому я решил реализовать неиспользуемые события с исключением.

Для генерации кода C # мне нужно пройти по XML и сгенерировать класс для каждого состояния.Когда класс создается, следующим шагом является создание метода для каждого события.На данный момент это сложная часть.Для каждого метода (события) мне нужно проверить, существует ли событие для текущего состояния, которое я оцениваю.Поэтому я просматриваю XML и ищу переход, атрибут которого «источник» равен текущему оцененному состоянию / классу.Если он найден, я проверяю, совпадает ли дочерний триггер с текущим оцениваемым событием.Если оба теста верны, то в качестве реализации текущего метода / события необходимо использовать содержимое «transtion / trigger / @ action» и «transtion / trigger / @ guard».Если не найдено допустимого перехода, это означает, что событие не существует для состояния и поэтому должно быть реализовано как исключение.

На данный момент я не написал код для чтения действия и защитыатрибуты, поэтому не будет сгенерированного кода для этого.Сейчас я использую текст «реализовать охрану / действие», когда переход действителен для текущего состояния и события, и использую текст «throw new NotImplementedException ();»;когда переход не найден.Или, лучше сказать, это должно быть сделано ....

Я написал код, который почти привел меня туда:

<xsl:for-each select="//state">
<xsl:variable name="currentStateName" select="@name"/>
public class <xsl:value-of select="@name"/> : <xsl:value-of select="$FsmName"/>States
{
  public <xsl:value-of select="@name"/>()
  {
      Console.WriteLine("In <xsl:value-of select="@name"/>State");
  }
  <xsl:for-each select="//event">
  public override void <xsl:value-of select="@name"/>(MediaPlayer player)
  {
    <xsl:variable name="currentEventName" select="@name"/>
    <xsl:for-each select="//transition">
      <xsl:if test="@source = $currentStateName">
        <xsl:choose>
          <xsl:when test="trigger/@name = $currentEventName">
            implement guard/action
          </xsl:when>
          <xsl:otherwise>
            throw new NotImplementedException();
          </xsl:otherwise>
        </xsl:choose>
      </xsl:if>
    </xsl:for-each>
  }
  </xsl:for-each>
}
</xsl:for-each>

Теперь вывод этого кода:

public class FastForward : MediaPlayerStates
{
  public FastForward()
  {
      Console.WriteLine("In FastForwardState");
  }

  public override void FastForwardButtonPressed(MediaPlayer player)
  {
      implement guard/action

      throw new NotImplementedException();
  }

  public override void PlayButtonPressed(MediaPlayer player)
  {
      throw new NotImplementedException();

      implement guard/action
  }
}

public class PlayingMediaFile : MediaPlayerStates
{
  public PlayingMediaFile()
  {
      Console.WriteLine("In PlayingMediaFileState");
  }

  public override void FastForwardButtonPressed(MediaPlayer player)
  {

  }

  public override void PlayButtonPressed(MediaPlayer player)
  {

  }
}

Как вы понимаете, это неправильно.В «FastForward» класс генерируется неправильно.Для этого требуется только «реализовать охрану / действие», а не «бросить новое NotImplementedException ();».В классе «PlayingMediaFile» ничего не генерируется, в то время как его необходимо заполнить «throw new NotImplementedException ();»;потому что у этого класса нет событий.

public class FastForward : MediaPlayerStates
{
  public FastForward()
  {
      Console.WriteLine("In FastForwardState");
  }

  public override void FastForwardButtonPressed(MediaPlayer player)
  {
      implement guard/action
  }

  public override void PlayButtonPressed(MediaPlayer player)
  {
      implement guard/action
  }
}

public class PlayingMediaFile : MediaPlayerStates
{
  public PlayingMediaFile()
  {
      Console.WriteLine("In PlayingMediaFileState");
  }

  public override void FastForwardButtonPressed(MediaPlayer player)
  {
      throw new NotImplementedException();
  }

  public override void PlayButtonPressed(MediaPlayer player)
  {
      throw new NotImplementedException();
  }
}

Теперь я понимаю, почему он идет не так, что я попытаюсь объяснить: при зацикливании всех переходов он сталкивается с двумя переходами.Первый из них оценивается как верный для теста, поэтому он генерирует текст «реализовать защиту / действие».Теперь он переходит ко второму переходному узлу, который оценивает false.Из-за ложной оценки он генерирует NotImplementedException ().Все это происходит для одного и того же события и, следовательно, неверно.

Итак, насколько я вижу, переход для каждого цикла должен прерваться, когда проверка верна.Если не было найдено соответствующего перехода, необходимо сгенерировать текст NotImplementedException.

Я пробовал все виды вещей, но не могу этого сделать.Может кто-нибудь, пожалуйста, помогите мне?

Спасибо

Ответы [ 4 ]

0 голосов
/ 19 августа 2010

Мне не понятно, о чем вы спрашиваете, но эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:param name="vMatch" select="'MediaPlayerStates'"/>
    <xsl:output method="text"/>
    <xsl:key name="kTransitionBySource" match="transition" use="@source"/>
    <xsl:template match="state">
        <xsl:value-of select="concat('public class ',@name,' : MediaPlayerStates&#xA;',
                                     '{&#xA;',
                                     '  public ',@name,'&#xA;',
                                     '  {&#xA;',
                                     '      Console.WriteLine(&quot;In ',@name,'State&quot;);&#xA;',
                                     '  }&#xA;')"/>
        <xsl:apply-templates select="../event" mode="event">
            <xsl:with-param name="pState" select="@name"/>
        </xsl:apply-templates>
        <xsl:text>}&#xA;</xsl:text>
    </xsl:template>
    <xsl:template match="event" mode="event">
        <xsl:param name="pState"/>
        <xsl:value-of select="concat('  public override void ',@name,'(MediaPlayer player)&#xA;',
                                     '  {&#xA;')"/>
        <xsl:variable name="vTrigger" select="key('kTransitionBySource',$pState)/trigger"/>
        <xsl:choose>
            <xsl:when test="$vTrigger">
                <xsl:value-of
                      select="concat('            implement guard/action  (=> action=&quot;',$vTrigger/@action,'&quot;)&#xA;')"/>
            </xsl:when>
            <xsl:otherwise>
                            <xsl:text>            throw new NotImplementedException();&#xA;</xsl:text>
            </xsl:otherwise>
        </xsl:choose>
                            <xsl:text>  }&#xA;</xsl:text>
    </xsl:template>
</xsl:stylesheet>

Вывод:

public class FastForward : MediaPlayerStates
{
  public FastForward
  {
      Console.WriteLine("In FastForwardState");
  }
  public override void FastForwardButtonPressed(MediaPlayer player)
  {
            implement guard/action  (=> action="playingSpeed * 2")
  }
  public override void PlayButtonPressed(MediaPlayer player)
  {
            implement guard/action  (=> action="playingSpeed * 2")
  }
}
public class PlayingMediaFile : MediaPlayerStates
{
  public PlayingMediaFile
  {
      Console.WriteLine("In PlayingMediaFileState");
  }
  public override void FastForwardButtonPressed(MediaPlayer player)
  {
            throw new NotImplementedException();
  }
  public override void PlayButtonPressed(MediaPlayer player)
  {
            throw new NotImplementedException();
  }
}
0 голосов
/ 19 августа 2010

Было бы полезно иметь две for-each петли что-то вроде

<xsl:for-each select="//transition[trigger/@name = $currentEventName]">
  <xsl:if test="@source = $currentStateName">
    implement guard/action
  </xsl:if>
</xsl:for-each>
<xsl:for-each select="//transition[not(some x in trigger/@name satisfies . = $currentEventName)]">
  <xsl:if test="@source = $currentStateName">
    throw new NotImplementedException();
  </xsl:if>
</xsl:for-each>

0 голосов
/ 19 августа 2010

С риском сделать себя непопулярным, так как вы заявили, что XSLT даже не обладает самой базовой функциональностью любого приемлемого языка программирования (а именно, переменных, которые вы можете установить), мой инстинкт должен был бы прекратить его использование ииспользуйте то, с чем вы уже знакомы - C #.Поэтому я перевел ваш XSLT на C #, и насколько я могу судить, он работает просто отлично:

static void Main(string[] args)
{
    Console.OutputEncoding = Encoding.UTF8;
    Console.WriteLine(Transform(XDocument.Parse(xml)));
    Console.ReadLine();
}

static string Transform(XDocument doc)
{
    var topElem = doc.Root;
    var fsmName = topElem.Attribute("name").Value;
    var sb = new StringBuilder();
    foreach (var state in topElem.Elements("state"))
    {
        var currentStateName = state.Attribute("name").Value;
        sb.AppendLine(string.Format("public class {0} : {1}States", currentStateName, fsmName));
        sb.AppendLine("{");
        sb.AppendLine(string.Format("  public {0}()", currentStateName));
        sb.AppendLine("  {");
        sb.AppendLine(string.Format(@"      Console.WriteLine(""In {0}State"");", currentStateName));
        sb.AppendLine("  }");
        foreach (var @event in topElem.Elements("event"))
        {
            var eventName = @event.Attribute("name").Value;
            sb.AppendLine(string.Format("  public override void {0}(MediaPlayer player)", eventName));
            sb.AppendLine("  {");
            bool any = false;
            foreach (var transition in topElem.Elements("transition"))
            {
                if (transition.Attribute("source").Value == currentStateName && transition.Element("trigger").Attribute("name").Value == eventName)
                {
                    sb.AppendLine("        implement guard/action");
                    any = true;
                    break;
                }
            }
            if (!any)
                sb.AppendLine("        throw new NotImplementedException();");
            sb.AppendLine("  }");
        }
        sb.AppendLine("}");
    }
    return sb.ToString();
}

Конечно, применяется обычный отказ от ответственности: Пожалуйста, используйте этот код только в том случае, если вы его понимаете.Убедитесь, что логика соответствует тому, что вы хотите, чтобы она делала, и внесите необходимые изменения, соответствующие вашей конкретной проблеме.Например, я не совсем уверен, что утверждение «если» полностью верно.

0 голосов
/ 19 августа 2010

Есть два <transition> элемента.Следовательно, этот цикл:

<xsl:for-each select="//transition">

будет иметь две итерации.Одна из двух итераций генерирует ваш implement guard/action, другая генерирует код исключения.

Может быть, вы имеете в виду нечто подобное?(С использованием псевдо-XSLT)

<xsl:set-variable name="anyMatched" value="0" />
<xsl:for-each select="//transition">
  <xsl:if test="@source = $currentStateName">
    <xsl:if test="trigger/@name = $currentEventName">
      <xsl:set-variable name="anyMatched" value="1" />
      implement guard/action
    </xsl:if>
  </xsl:if>
</xsl:for-each>
<xsl:if test="anyMatched=1">
  throw new NotImplementedException();
</xsl>

Обратите внимание, что я не знаком с синтаксисом XSLT;Надеюсь, вы понимаете, что я имею в виду под «set-variable», хотя это не настоящий тег XSLT.Я уверен, что вы можете перевести его в настоящий XSLT.

...