001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: StackGuard.java 468645 2006-10-28 06:57:24Z minchau $
020 */
021 package org.apache.xalan.transformer;
022
023 import javax.xml.transform.TransformerException;
024
025 import org.apache.xalan.res.XSLMessages;
026 import org.apache.xalan.templates.Constants;
027 import org.apache.xalan.templates.ElemTemplate;
028 import org.apache.xalan.templates.ElemTemplateElement;
029 import org.apache.xml.utils.ObjectStack;
030
031 /**
032 * Class to guard against recursion getting too deep.
033 */
034 public class StackGuard
035 {
036
037 /**
038 * Used for infinite loop check. If the value is -1, do not
039 * check for infinite loops. Anyone who wants to enable that
040 * check should change the value of this variable to be the
041 * level of recursion that they want to check. Be careful setting
042 * this variable, if the number is too low, it may report an
043 * infinite loop situation, when there is none.
044 * Post version 1.0.0, we'll make this a runtime feature.
045 */
046 private int m_recursionLimit = -1;
047
048 TransformerImpl m_transformer;
049
050 /**
051 * Get the recursion limit.
052 * Used for infinite loop check. If the value is -1, do not
053 * check for infinite loops. Anyone who wants to enable that
054 * check should change the value of this variable to be the
055 * level of recursion that they want to check. Be careful setting
056 * this variable, if the number is too low, it may report an
057 * infinite loop situation, when there is none.
058 * Post version 1.0.0, we'll make this a runtime feature.
059 *
060 * @return The recursion limit.
061 */
062 public int getRecursionLimit()
063 {
064 return m_recursionLimit;
065 }
066
067 /**
068 * Set the recursion limit.
069 * Used for infinite loop check. If the value is -1, do not
070 * check for infinite loops. Anyone who wants to enable that
071 * check should change the value of this variable to be the
072 * level of recursion that they want to check. Be careful setting
073 * this variable, if the number is too low, it may report an
074 * infinite loop situation, when there is none.
075 * Post version 1.0.0, we'll make this a runtime feature.
076 *
077 * @param limit The recursion limit.
078 */
079 public void setRecursionLimit(int limit)
080 {
081 m_recursionLimit = limit;
082 }
083
084 /**
085 * Constructor StackGuard
086 *
087 */
088 public StackGuard(TransformerImpl transformerImpl)
089 {
090 m_transformer = transformerImpl;
091 }
092
093 /**
094 * Overide equal method for StackGuard objects
095 *
096 */
097 public int countLikeTemplates(ElemTemplate templ, int pos)
098 {
099 ObjectStack elems = m_transformer.getCurrentTemplateElements();
100 int count = 1;
101 for (int i = pos-1; i >= 0; i--)
102 {
103 if((ElemTemplateElement)elems.elementAt(i) == templ)
104 count++;
105 }
106
107 return count;
108 }
109
110
111 /**
112 * Get the next named or match template down from and including
113 * the given position.
114 * @param pos the current index position in the stack.
115 * @return null if no matched or named template found, otherwise
116 * the next named or matched template at or below the position.
117 */
118 private ElemTemplate getNextMatchOrNamedTemplate(int pos)
119 {
120 ObjectStack elems = m_transformer.getCurrentTemplateElements();
121 for (int i = pos; i >= 0; i--)
122 {
123 ElemTemplateElement elem = (ElemTemplateElement) elems.elementAt(i);
124 if(null != elem)
125 {
126 if(elem.getXSLToken() == Constants.ELEMNAME_TEMPLATE)
127 {
128 return (ElemTemplate)elem;
129 }
130 }
131 }
132 return null;
133 }
134
135 /**
136 * Check if we are in an infinite loop
137 *
138 * @throws TransformerException
139 */
140 public void checkForInfinateLoop() throws TransformerException
141 {
142 int nTemplates = m_transformer.getCurrentTemplateElementsCount();
143 if(nTemplates < m_recursionLimit)
144 return;
145
146 if(m_recursionLimit <= 0)
147 return; // Safety check.
148
149 // loop from the top index down to the recursion limit (I don't think
150 // there's any need to go below that).
151 for (int i = (nTemplates - 1); i >= m_recursionLimit; i--)
152 {
153 ElemTemplate template = getNextMatchOrNamedTemplate(i);
154
155 if(null == template)
156 break;
157
158 int loopCount = countLikeTemplates(template, i);
159
160 if (loopCount >= m_recursionLimit)
161 {
162 // throw new TransformerException("Template nesting too deep. nesting = "+loopCount+
163 // ", template "+((null == template.getName()) ? "name = " : "match = ")+
164 // ((null != template.getName()) ? template.getName().toString()
165 // : template.getMatch().getPatternString()));
166
167 String idIs = XSLMessages.createMessage(((null != template.getName()) ? "nameIs" : "matchPatternIs"), null);
168 Object[] msgArgs = new Object[]{ new Integer(loopCount), idIs,
169 ((null != template.getName()) ? template.getName().toString()
170 : template.getMatch().getPatternString()) };
171 String msg = XSLMessages.createMessage("recursionTooDeep", msgArgs);
172
173 throw new TransformerException(msg);
174 }
175 }
176 }
177
178 }