Я обрабатываю файл XML, который в упрощенном виде выглядит примерно так:
<resources>
<resource id="a">
<dependency idref="b"/>
<!-- some other stuff -->
</resource>
<resource id="b">
<!-- some other stuff -->
</resource>
</resources>
Таблица стилей XSLT должна обрабатывать конкретный интересующий нас ресурс, который я буду называть корневым ресурсом, и все рекурсивные зависимости. Зависимости — это другие ресурсы, однозначно идентифицируемые своим атрибутом id
.
Неважно, если ресурс обрабатывается дважды, хотя предпочтительнее обрабатывать каждый требуемый ресурс только один раз. Также не имеет значения, в каком порядке обрабатываются ресурсы.
Важно, чтобы обрабатывались только ресурс root и его рекурсивные зависимости. Мы не можем просто обработать все ресурсы и покончить с этим.
Наивная реализация выглядит следующим образом:
<xsl:key name="resource-id" match="resource" use="@id"/>
<xsl:template match="resource">
<!-- do whatever is required to process the resource. -->
<!-- then handle any dependencies -->
<xsl:apply-templates select="key('resource-id', dependency/@idref)"/>
</xsl:template>
Эта реализация отлично работает для приведенного выше примера, а также во многих реальных случаях. У него есть недостаток, заключающийся в том, что он часто обрабатывает один и тот же ресурс более одного раза, но, как указано выше, это не так уж важно.
Проблема в том, что иногда ресурсы имеют циклические зависимости:
<resources>
<resource id="a">
<dependency idref="b"/>
<dependency idref="d"/>
</resource>
<resource id="b">
<dependency idref="c"/>
</resource>
<resource id="c">
<dependency idref="a"/>
</resource>
<resource id="d"/>
</resources>
Если вы используете наивную реализацию для обработки этого примера и начинаете с обработки a, b или c, вы получаете бесконечную рекурсию.
К сожалению, я не могу контролировать входные данные, и в любом случае циклические зависимости вполне допустимы и разрешены соответствующей спецификацией.
Я придумал различные частичные решения, но ни одно из них не работало во всех случаях.
Идеальным решением был бы общий подход к предотвращению повторной обработки узла, но я не думаю, что это возможно. На самом деле, я подозреваю, что всю эту проблему невозможно решить.
Если это поможет, у меня есть большая часть EXSLT (включая функции). При необходимости я также могу предварительно обработать ввод любым количеством других сценариев XSLT, хотя предпочтительнее не выполнять чрезмерную предварительную обработку ресурсов, которая не попадет в вывод.
Чего я не могу сделать, так это переключиться на обработку этого с помощью другого языка (по крайней мере, без существенного реинжиниринга). Я также не могу использовать XSLT 2.0.
Есть идеи?