diff --git a/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java b/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
index 49308d263..2ac045d45 100644
--- a/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
+++ b/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
@@ -21,26 +21,16 @@ package com.opensymphony.xwork2.config.providers;
import com.opensymphony.xwork2.ActionProxyFactory;
import com.opensymphony.xwork2.DefaultActionProxyFactory;
import com.opensymphony.xwork2.DefaultLocaleProviderFactory;
-import com.opensymphony.xwork2.LocaleProviderFactory;
-import com.opensymphony.xwork2.StrutsTextProviderFactory;
-import com.opensymphony.xwork2.TextProviderFactory;
-import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
-import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
-import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
-import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
-import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
-import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
-import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
-import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
-import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
import com.opensymphony.xwork2.DefaultTextProvider;
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
-import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
-import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
+import com.opensymphony.xwork2.LocaleProviderFactory;
+import com.opensymphony.xwork2.LocalizedTextProvider;
import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.StrutsTextProviderFactory;
import com.opensymphony.xwork2.TextProvider;
+import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.UnknownHandlerManager;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
@@ -57,14 +47,7 @@ import com.opensymphony.xwork2.conversion.impl.CollectionConverter;
import com.opensymphony.xwork2.conversion.impl.DateConverter;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
-import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
-import org.apache.struts2.components.date.DateFormatter;
-import org.apache.struts2.components.date.DateTimeFormatterAdapter;
-import org.apache.struts2.components.date.SimpleDateFormatAdapter;
-import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
-import org.apache.struts2.conversion.StrutsTypeConverterCreator;
-import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
import com.opensymphony.xwork2.conversion.impl.StringConverter;
@@ -73,34 +56,45 @@ import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.factory.ActionFactory;
import com.opensymphony.xwork2.factory.ConverterFactory;
import com.opensymphony.xwork2.factory.DefaultActionFactory;
-import com.opensymphony.xwork2.factory.StrutsConverterFactory;
import com.opensymphony.xwork2.factory.DefaultInterceptorFactory;
import com.opensymphony.xwork2.factory.DefaultResultFactory;
+import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
import com.opensymphony.xwork2.factory.InterceptorFactory;
import com.opensymphony.xwork2.factory.ResultFactory;
+import com.opensymphony.xwork2.factory.StrutsConverterFactory;
+import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Scope;
+import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
+import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
+import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
+import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.ObjectProxy;
import com.opensymphony.xwork2.ognl.OgnlReflectionContextFactory;
import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
+import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.ObjectAccessor;
import com.opensymphony.xwork2.ognl.accessor.ObjectProxyPropertyAccessor;
+import com.opensymphony.xwork2.ognl.accessor.ParameterPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkEnumerationAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkIteratorPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.XWorkMethodAccessor;
+import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
+import com.opensymphony.xwork2.security.DefaultAcceptedPatternsChecker;
+import com.opensymphony.xwork2.security.DefaultExcludedPatternsChecker;
+import com.opensymphony.xwork2.security.DefaultNotExcludedAcceptedPatternsChecker;
+import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
+import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.util.CompoundRoot;
-import com.opensymphony.xwork2.LocalizedTextProvider;
-import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
-import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
-import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
import com.opensymphony.xwork2.util.OgnlTextParser;
import com.opensymphony.xwork2.util.PatternMatcher;
+import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
import com.opensymphony.xwork2.util.TextParser;
import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.WildcardHelper;
@@ -119,8 +113,15 @@ import com.opensymphony.xwork2.validator.ValidatorFileParser;
import ognl.MethodAccessor;
import ognl.PropertyAccessor;
import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
+import org.apache.struts2.conversion.StrutsTypeConverterCreator;
+import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
+import org.apache.struts2.url.StrutsUrlDecoder;
+import org.apache.struts2.url.StrutsUrlEncoder;
+import org.apache.struts2.url.UrlDecoder;
+import org.apache.struts2.url.UrlEncoder;
import java.util.ArrayList;
import java.util.Collection;
@@ -153,88 +154,88 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider
@Override
public void register(ContainerBuilder builder, LocatableProperties props)
- throws ConfigurationException {
+ throws ConfigurationException {
builder
- .factory(ObjectFactory.class)
- .factory(ActionFactory.class, DefaultActionFactory.class)
- .factory(ResultFactory.class, DefaultResultFactory.class)
- .factory(InterceptorFactory.class, DefaultInterceptorFactory.class)
- .factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class)
- .factory(ConverterFactory.class, StrutsConverterFactory.class)
- .factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class)
+ .factory(ObjectFactory.class)
+ .factory(ActionFactory.class, DefaultActionFactory.class)
+ .factory(ResultFactory.class, DefaultResultFactory.class)
+ .factory(InterceptorFactory.class, DefaultInterceptorFactory.class)
+ .factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class)
+ .factory(ConverterFactory.class, StrutsConverterFactory.class)
+ .factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class)
- .factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
- .factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
+ .factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
+ .factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
- .factory(XWorkConverter.class, Scope.SINGLETON)
- .factory(XWorkBasicConverter.class, Scope.SINGLETON)
- .factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
- .factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
- .factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
- .factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
- .factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
+ .factory(XWorkConverter.class, Scope.SINGLETON)
+ .factory(XWorkBasicConverter.class, Scope.SINGLETON)
+ .factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
+ .factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
+ .factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
+ .factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
+ .factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
- .factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
- .factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
- .factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
- .factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
- .factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
- .factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
- .factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
- .factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
+ .factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
+ .factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
+ .factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
+ .factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
+ .factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
+ .factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
+ .factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
+ .factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
- .factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
+ .factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
- // silly workarounds for ognl since there is no way to flush its caches
- .factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
+ // silly workarounds for ognl since there is no way to flush its caches
+ .factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
- .factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
- .factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
+ .factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
+ .factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
- .factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
+ .factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
- .factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
- .factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
- .factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
+ .factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
+ .factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
+ .factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
- .factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
- .factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
- .factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
- .factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
+ .factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
+ .factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
+ .factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
+ .factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
- .factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
- .factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
- .factory(OgnlUtil.class, Scope.SINGLETON)
- .factory(CollectionConverter.class, Scope.SINGLETON)
- .factory(ArrayConverter.class, Scope.SINGLETON)
- .factory(DateConverter.class, Scope.SINGLETON)
- .factory(NumberConverter.class, Scope.SINGLETON)
- .factory(StringConverter.class, Scope.SINGLETON)
+ .factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
+ .factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
+ .factory(OgnlUtil.class, Scope.SINGLETON)
+ .factory(CollectionConverter.class, Scope.SINGLETON)
+ .factory(ArrayConverter.class, Scope.SINGLETON)
+ .factory(DateConverter.class, Scope.SINGLETON)
+ .factory(NumberConverter.class, Scope.SINGLETON)
+ .factory(StringConverter.class, Scope.SINGLETON)
- .factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
- .factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
- .factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
- , Scope.SINGLETON)
+ .factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
+ .factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
+ .factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
+ , Scope.SINGLETON)
- .factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
+ .factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
- .factory(DateFormatter.class, "simpleDateFormatter", SimpleDateFormatAdapter.class, Scope.SINGLETON)
- .factory(DateFormatter.class, "dateTimeFormatter", DateTimeFormatterAdapter.class, Scope.SINGLETON)
+ .factory(UrlEncoder.class, StrutsUrlEncoder.class, Scope.SINGLETON)
+ .factory(UrlDecoder.class, StrutsUrlDecoder.class, Scope.SINGLETON)
;
props.setProperty(StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, Boolean.FALSE.toString());
diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java
index f37a85078..5dff4724c 100644
--- a/core/src/main/java/org/apache/struts2/StrutsConstants.java
+++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java
@@ -457,4 +457,7 @@ public final class StrutsConstants {
/** See {@link org.apache.struts2.components.Date#setDateFormatter(DateFormatter)} */
public static final String STRUTS_DATE_FORMATTER = "struts.date.formatter";
+
+ public static final String STRUTS_URL_ENCODER = "struts.url.encoder";
+ public static final String STRUTS_URL_DECODER = "struts.url.decoder";
}
diff --git a/core/src/main/java/org/apache/struts2/config/AbstractBeanSelectionProvider.java b/core/src/main/java/org/apache/struts2/config/AbstractBeanSelectionProvider.java
index 34db87d3a..f672ae041 100644
--- a/core/src/main/java/org/apache/struts2/config/AbstractBeanSelectionProvider.java
+++ b/core/src/main/java/org/apache/struts2/config/AbstractBeanSelectionProvider.java
@@ -24,7 +24,6 @@ import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.inject.*;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
-import com.opensymphony.xwork2.util.location.LocatableProperties;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
diff --git a/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java b/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java
index f47bbc354..2e5a9315c 100644
--- a/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java
+++ b/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java
@@ -66,6 +66,8 @@ import org.apache.struts2.dispatcher.DispatcherErrorHandler;
import org.apache.struts2.dispatcher.StaticContentLoader;
import org.apache.struts2.dispatcher.mapper.ActionMapper;
import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
+import org.apache.struts2.url.UrlDecoder;
+import org.apache.struts2.url.UrlEncoder;
import org.apache.struts2.util.ContentTypeMatcher;
import org.apache.struts2.views.freemarker.FreemarkerManager;
import org.apache.struts2.views.util.UrlHelper;
@@ -429,6 +431,9 @@ public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider {
alias(ExpressionCacheFactory.class, StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, builder, props, Scope.SINGLETON);
alias(BeanInfoCacheFactory.class, StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, builder, props, Scope.SINGLETON);
+ alias(UrlEncoder.class, StrutsConstants.STRUTS_URL_ENCODER, builder, props, Scope.SINGLETON);
+ alias(UrlDecoder.class, StrutsConstants.STRUTS_URL_DECODER, builder, props, Scope.SINGLETON);
+
switchDevMode(props);
}
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/mapper/Restful2ActionMapper.java b/core/src/main/java/org/apache/struts2/dispatcher/mapper/Restful2ActionMapper.java
index 2dc418241..1a11b06bf 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/mapper/Restful2ActionMapper.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/mapper/Restful2ActionMapper.java
@@ -24,7 +24,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
-import org.apache.struts2.util.URLDecoderUtil;
+import org.apache.struts2.url.UrlDecoder;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
@@ -32,18 +32,26 @@ import java.util.StringTokenizer;
/**
* Extended version of {@link RestfulActionMapper}, see documentation for more details
- * https://struts.apache.org/core-developers/restful-action-mapper.html
+ * Restful2ActionMapper
*/
public class Restful2ActionMapper extends DefaultActionMapper {
- protected static final Logger LOG = LogManager.getLogger(Restful2ActionMapper.class);
+ private static final Logger LOG = LogManager.getLogger(Restful2ActionMapper.class);
+
public static final String HTTP_METHOD_PARAM = "__http_method";
+
private String idParameterName = null;
-
+ private UrlDecoder decoder;
+
public Restful2ActionMapper() {
setSlashesInActionNames("true");
}
+ @Inject
+ public void setDecoder(UrlDecoder decoder) {
+ this.decoder = decoder;
+ }
+
/*
* (non-Javadoc)
*
@@ -54,7 +62,7 @@ public class Restful2ActionMapper extends DefaultActionMapper {
throw new IllegalStateException("This action mapper requires the setting 'slashesInActionNames' to be set to 'true'");
}
ActionMapping mapping = super.getMapping(request, configManager);
-
+
if (mapping == null) {
return null;
}
@@ -78,7 +86,7 @@ public class Restful2ActionMapper extends DefaultActionMapper {
// Index e.g. foo/
if (isGet(request)) {
mapping.setMethod("index");
-
+
// Creating a new entry on POST e.g. foo/
} else if (isPost(request)) {
mapping.setMethod("create");
@@ -96,14 +104,14 @@ public class Restful2ActionMapper extends DefaultActionMapper {
// Removing an item e.g. foo/1
} else if (isDelete(request)) {
mapping.setMethod("remove");
-
- // Updating an item e.g. foo/1
+
+ // Updating an item e.g. foo/1
} else if (isPut(request)) {
mapping.setMethod("update");
}
-
+
}
-
+
if (idParameterName != null && lastSlashPos > -1) {
actionName = actionName.substring(0, lastSlashPos);
}
@@ -129,10 +137,10 @@ public class Restful2ActionMapper extends DefaultActionMapper {
while (st.hasMoreTokens()) {
if (isNameTok) {
- paramName = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
+ paramName = decoder.decode(st.nextToken(), "UTF-8", false);
isNameTok = false;
} else {
- paramValue = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
+ paramValue = decoder.decode(st.nextToken(), "UTF-8", false);
if ((paramName != null) && (paramName.length() > 0)) {
parameters.put(paramName, paramValue);
@@ -143,7 +151,7 @@ public class Restful2ActionMapper extends DefaultActionMapper {
}
if (parameters.size() > 0) {
if (mapping.getParams() == null) {
- mapping.setParams(new HashMap());
+ mapping.setParams(new HashMap<>());
}
mapping.getParams().putAll(parameters);
}
diff --git a/core/src/main/java/org/apache/struts2/dispatcher/mapper/RestfulActionMapper.java b/core/src/main/java/org/apache/struts2/dispatcher/mapper/RestfulActionMapper.java
index 888bb97a4..1709834a1 100644
--- a/core/src/main/java/org/apache/struts2/dispatcher/mapper/RestfulActionMapper.java
+++ b/core/src/main/java/org/apache/struts2/dispatcher/mapper/RestfulActionMapper.java
@@ -19,10 +19,11 @@
package org.apache.struts2.dispatcher.mapper;
import com.opensymphony.xwork2.config.ConfigurationManager;
+import com.opensymphony.xwork2.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.RequestUtils;
-import org.apache.struts2.util.URLDecoderUtil;
+import org.apache.struts2.url.UrlDecoder;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
@@ -32,11 +33,19 @@ import java.util.StringTokenizer;
/**
* Simple Restfull Action Mapper to support REST application
* See docs for more information
- * https://struts.apache.org/core-developers/restful-action-mapper.html
+ * RestfulActionMapper
*/
public class RestfulActionMapper implements ActionMapper {
+
protected static final Logger LOG = LogManager.getLogger(RestfulActionMapper.class);
+ private UrlDecoder decoder;
+
+ @Inject
+ public void setDecoder(UrlDecoder decoder) {
+ this.decoder = decoder;
+ }
+
/* (non-Javadoc)
* @see org.apache.struts2.dispatcher.mapper.ActionMapper#getMapping(javax.servlet.http.HttpServletRequest)
*/
@@ -64,10 +73,10 @@ public class RestfulActionMapper implements ActionMapper {
while (st.hasMoreTokens()) {
if (isNameTok) {
- paramName = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
+ paramName = decoder.decode(st.nextToken(), "UTF-8", false);
isNameTok = false;
} else {
- paramValue = URLDecoderUtil.decode(st.nextToken(), "UTF-8");
+ paramValue = decoder.decode(st.nextToken(), "UTF-8", false);
if ((paramName != null) && (paramName.length() > 0)) {
parameters.put(paramName, paramValue);
@@ -98,7 +107,7 @@ public class RestfulActionMapper implements ActionMapper {
if (value != null) {
retVal.append("/");
retVal.append(value);
- }
+ }
return retVal.toString();
}
diff --git a/core/src/main/java/org/apache/struts2/url/StrutsUrlDecoder.java b/core/src/main/java/org/apache/struts2/url/StrutsUrlDecoder.java
new file mode 100644
index 000000000..3a480d87f
--- /dev/null
+++ b/core/src/main/java/org/apache/struts2/url/StrutsUrlDecoder.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.url;
+
+import com.opensymphony.xwork2.inject.Inject;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.message.ParameterizedMessage;
+import org.apache.struts2.StrutsConstants;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+
+public class StrutsUrlDecoder implements UrlDecoder {
+
+ private static final Logger LOG = LogManager.getLogger(StrutsUrlDecoder.class);
+
+ private String encoding = "UTF-8";
+
+ @Inject(value = StrutsConstants.STRUTS_I18N_ENCODING, required = false)
+ public void setEncoding(String encoding) {
+ LOG.debug("Using default encoding: {}", encoding);
+ if (StringUtils.isNotEmpty(encoding)) {
+ this.encoding = encoding;
+ }
+ }
+
+ @Override
+ public String decode(String input, String encoding, boolean isQueryString) {
+ if (input == null) {
+ return (null);
+ }
+ byte[] bytes = null;
+ try {
+ bytes = input.getBytes(getCharset(encoding));
+ } catch (UnsupportedEncodingException uee) {
+ LOG.debug(new ParameterizedMessage("Unable to URL decode the specified input since the encoding: {} is not supported.", encoding), uee);
+ }
+
+ return internalDecode(bytes, encoding, isQueryString);
+ }
+
+ @Override
+ public String decode(String input, boolean isQueryString) {
+ return this.decode(input, this.encoding, isQueryString);
+ }
+
+ @Override
+ public String decode(String input) {
+ return decode(input, false);
+ }
+
+ private String internalDecode(byte[] bytes, String encoding, boolean isQuery) {
+ if (bytes == null) {
+ return null;
+ }
+
+ int len = bytes.length;
+ int ix = 0;
+ int ox = 0;
+ while (ix < len) {
+ byte b = bytes[ix++]; // Get byte to test
+ if (b == '+' && isQuery) {
+ b = (byte) ' ';
+ } else if (b == '%') {
+ if (ix + 2 > len) {
+ throw new IllegalArgumentException("The % character must be followed by two hexadecimal digits");
+ }
+ b = (byte) ((((convertHexDigit(bytes[ix++]) << 4) & 0xff) + convertHexDigit(bytes[ix++]) & 0xff) & 0xff);
+ }
+ bytes[ox++] = b;
+ }
+ if (encoding != null) {
+ try {
+ return new String(bytes, 0, ox, getCharset(encoding));
+ } catch (UnsupportedEncodingException uee) {
+ LOG.debug(new ParameterizedMessage("Unable to URL decode the specified input since the encoding: {} is not supported.", encoding), uee);
+ return null;
+ }
+ }
+ return new String(bytes, 0, ox);
+
+ }
+
+ private byte convertHexDigit(byte b) {
+ if ((b >= '0') && (b <= '9')) return (byte) (b - '0');
+ if ((b >= 'a') && (b <= 'f')) return (byte) (b - 'a' + 10);
+ if ((b >= 'A') && (b <= 'F')) return (byte) (b - 'A' + 10);
+ throw new IllegalArgumentException(((char) b) + " is not a hexadecimal digit");
+ }
+
+ private Charset getCharset(String encoding) throws UnsupportedEncodingException {
+ for (Charset charset : Charset.availableCharsets().values()) {
+ if (encoding.equalsIgnoreCase(charset.name())) {
+ return charset;
+ }
+ }
+ throw new UnsupportedEncodingException("The character encoding " + encoding + " is not supported");
+ }
+
+}
diff --git a/core/src/main/java/org/apache/struts2/url/StrutsUrlEncoder.java b/core/src/main/java/org/apache/struts2/url/StrutsUrlEncoder.java
new file mode 100644
index 000000000..12b5a0104
--- /dev/null
+++ b/core/src/main/java/org/apache/struts2/url/StrutsUrlEncoder.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.url;
+
+import com.opensymphony.xwork2.inject.Inject;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.struts2.StrutsConstants;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+public class StrutsUrlEncoder implements UrlEncoder {
+
+ private static final Logger LOG = LogManager.getLogger(StrutsUrlEncoder.class);
+
+ private String encoding = "UTF-8";
+
+ @Inject(value = StrutsConstants.STRUTS_I18N_ENCODING, required = false)
+ public void setEncoding(String encoding) {
+ LOG.debug("Using default encoding: {}", encoding);
+ if (StringUtils.isNotEmpty(encoding)) {
+ this.encoding = encoding;
+ }
+ }
+
+ @Override
+ public String encode(String input, String encoding) {
+ try {
+ return URLEncoder.encode(input, encoding);
+ } catch (UnsupportedEncodingException e) {
+ LOG.warn("Could not encode URL parameter '{}', returning value un-encoded", input);
+ return input;
+ }
+ }
+
+ @Override
+ public String encode(String input) {
+ return encode(input, encoding);
+ }
+}
diff --git a/core/src/main/java/org/apache/struts2/url/UrlDecoder.java b/core/src/main/java/org/apache/struts2/url/UrlDecoder.java
new file mode 100644
index 000000000..b54c563f5
--- /dev/null
+++ b/core/src/main/java/org/apache/struts2/url/UrlDecoder.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.url;
+
+public interface UrlDecoder {
+
+ /**
+ * Decodes the input using default encoding, e.g.: struts.i18n.encoding
+ *
+ * @param input String to decode
+ * @param encoding encoding used in decoding
+ * @param isQueryString indicates if input is a query string
+ * @return the decoded string
+ */
+ String decode(String input, String encoding, boolean isQueryString);
+
+ /**
+ * Decodes the input using default encoding, e.g.: struts.i18n.encoding
+ *
+ * @param input String to decode
+ * @param isQueryString indicates if input is a query string
+ * @return the decoded string
+ */
+ String decode(String input, boolean isQueryString);
+
+ /**
+ * Decodes the input using default encoding, e.g.: struts.i18n.encoding
+ *
+ * @param input String to decode
+ * @return the decoded string
+ */
+ String decode(String input);
+
+}
diff --git a/core/src/main/java/org/apache/struts2/url/UrlEncoder.java b/core/src/main/java/org/apache/struts2/url/UrlEncoder.java
new file mode 100644
index 000000000..976645a53
--- /dev/null
+++ b/core/src/main/java/org/apache/struts2/url/UrlEncoder.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.struts2.url;
+
+public interface UrlEncoder {
+
+ /**
+ * Encodes the input tb be used with URL using the provided encoding
+ *
+ * @param input String to encode
+ * @param encoding encoding to use
+ * @return encoded string
+ */
+ String encode(String input, String encoding);
+
+ /**
+ * Encodes the input to be used with URL using default encoding, e.g.: struts.i18n.encoding
+ *
+ * @param input String to encode
+ * @return encoded string
+ */
+ String encode(String input);
+
+}
diff --git a/core/src/main/java/org/apache/struts2/util/URLDecoderUtil.java b/core/src/main/java/org/apache/struts2/util/URLDecoderUtil.java
deleted file mode 100644
index 04f58e157..000000000
--- a/core/src/main/java/org/apache/struts2/util/URLDecoderUtil.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.struts2.util;
-
-import org.apache.struts2.util.tomcat.buf.UDecoder;
-
-/**
- * URLDecoderUtil serves as a facade for a correct URL decoding implementation.
- * As of Struts 2.3.25 it uses Tomcat URLDecoder functionality rather than the one found in java.io.
- */
-public class URLDecoderUtil {
-
- /**
- * Decodes a x-www-form-urlencoded string.
- * @param sequence the String to decode
- * @param charset The name of a supported character encoding.
- * @return the newly decoded String
- * @exception IllegalArgumentException If the encoding is not valid
- */
- public static String decode(String sequence, String charset) {
- return UDecoder.URLDecode(sequence, charset);
- }
-
- /**
- * Decodes a x-www-form-urlencoded string.
- * @param sequence the String to decode
- * @param charset The name of a supported character encoding.
- * @param isQueryString whether input is a query string. If true other decoding rules apply.
- * @return the newly decoded String
- * @exception IllegalArgumentException If the encoding is not valid
- */
- public static String decode(String sequence, String charset, boolean isQueryString) {
- return UDecoder.URLDecode(sequence, charset, isQueryString);
- }
-
-}
diff --git a/core/src/main/java/org/apache/struts2/util/tomcat/buf/Ascii.java b/core/src/main/java/org/apache/struts2/util/tomcat/buf/Ascii.java
deleted file mode 100644
index 13be1ef6d..000000000
--- a/core/src/main/java/org/apache/struts2/util/tomcat/buf/Ascii.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.struts2.util.tomcat.buf;
-
-/**
- * This class implements some basic ASCII character handling functions.
- *
- * @author dac@eng.sun.com
- * @author James Todd [gonzo@eng.sun.com]
- */
-public final class Ascii {
- /*
- * Character translation tables.
- */
-
- private static final byte[] toUpper = new byte[256];
- private static final byte[] toLower = new byte[256];
-
- /*
- * Character type tables.
- */
-
- private static final boolean[] isAlpha = new boolean[256];
- private static final boolean[] isUpper = new boolean[256];
- private static final boolean[] isLower = new boolean[256];
- private static final boolean[] isWhite = new boolean[256];
- private static final boolean[] isDigit = new boolean[256];
-
- private static final long OVERFLOW_LIMIT = Long.MAX_VALUE / 10;
-
- /*
- * Initialize character translation and type tables.
- */
- static {
- for (int i = 0; i < 256; i++) {
- toUpper[i] = (byte)i;
- toLower[i] = (byte)i;
- }
-
- for (int lc = 'a'; lc <= 'z'; lc++) {
- int uc = lc + 'A' - 'a';
-
- toUpper[lc] = (byte)uc;
- toLower[uc] = (byte)lc;
- isAlpha[lc] = true;
- isAlpha[uc] = true;
- isLower[lc] = true;
- isUpper[uc] = true;
- }
-
- isWhite[ ' '] = true;
- isWhite['\t'] = true;
- isWhite['\r'] = true;
- isWhite['\n'] = true;
- isWhite['\f'] = true;
- isWhite['\b'] = true;
-
- for (int d = '0'; d <= '9'; d++) {
- isDigit[d] = true;
- }
- }
-
- /**
- * Returns the upper case equivalent of the specified ASCII character.
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public static int toUpper(int c) {
- return toUpper[c & 0xff] & 0xff;
- }
-
- /**
- * Returns the lower case equivalent of the specified ASCII character.
- */
-
- public static int toLower(int c) {
- return toLower[c & 0xff] & 0xff;
- }
-
- /**
- * Returns true if the specified ASCII character is upper or lower case.
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public static boolean isAlpha(int c) {
- return isAlpha[c & 0xff];
- }
-
- /**
- * Returns true if the specified ASCII character is upper case.
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public static boolean isUpper(int c) {
- return isUpper[c & 0xff];
- }
-
- /**
- * Returns true if the specified ASCII character is lower case.
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public static boolean isLower(int c) {
- return isLower[c & 0xff];
- }
-
- /**
- * Returns true if the specified ASCII character is white space.
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public static boolean isWhite(int c) {
- return isWhite[c & 0xff];
- }
-
- /**
- * Returns true if the specified ASCII character is a digit.
- */
-
- public static boolean isDigit(int c) {
- return isDigit[c & 0xff];
- }
-
- /**
- * Parses an unsigned integer from the specified subarray of bytes.
- * @param b the bytes to parse
- * @param off the start offset of the bytes
- * @param len the length of the bytes
- * @exception NumberFormatException if the integer format was invalid
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public static int parseInt(byte[] b, int off, int len)
- throws NumberFormatException
- {
- int c;
-
- if (b == null || len <= 0 || !isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
-
- int n = c - '0';
-
- while (--len > 0) {
- if (!isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
- n = n * 10 + c - '0';
- }
-
- return n;
- }
-
- /**
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public static int parseInt(char[] b, int off, int len)
- throws NumberFormatException
- {
- int c;
-
- if (b == null || len <= 0 || !isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
-
- int n = c - '0';
-
- while (--len > 0) {
- if (!isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
- n = n * 10 + c - '0';
- }
-
- return n;
- }
-
- /**
- * Parses an unsigned long from the specified subarray of bytes.
- * @param b the bytes to parse
- * @param off the start offset of the bytes
- * @param len the length of the bytes
- * @exception NumberFormatException if the long format was invalid
- */
- public static long parseLong(byte[] b, int off, int len)
- throws NumberFormatException
- {
- int c;
-
- if (b == null || len <= 0 || !isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
-
- long n = c - '0';
- while (--len > 0) {
- if (isDigit(c = b[off++]) &&
- (n < OVERFLOW_LIMIT || (n == OVERFLOW_LIMIT && (c - '0') < 8))) {
- n = n * 10 + c - '0';
- } else {
- throw new NumberFormatException();
- }
- }
-
- return n;
- }
-
- /**
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public static long parseLong(char[] b, int off, int len)
- throws NumberFormatException
- {
- int c;
-
- if (b == null || len <= 0 || !isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
-
- long n = c - '0';
- long m;
-
- while (--len > 0) {
- if (!isDigit(c = b[off++])) {
- throw new NumberFormatException();
- }
- m = n * 10 + c - '0';
-
- if (m < n) {
- // Overflow
- throw new NumberFormatException();
- } else {
- n = m;
- }
- }
-
- return n;
- }
-
-}
\ No newline at end of file
diff --git a/core/src/main/java/org/apache/struts2/util/tomcat/buf/B2CConverter.java b/core/src/main/java/org/apache/struts2/util/tomcat/buf/B2CConverter.java
deleted file mode 100644
index 00581cb5c..000000000
--- a/core/src/main/java/org/apache/struts2/util/tomcat/buf/B2CConverter.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.struts2.util.tomcat.buf;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CoderResult;
-import java.nio.charset.CodingErrorAction;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-/**
- * NIO based character decoder.
- */
-public class B2CConverter {
-
- private static final Map encodingToCharsetCache =
- new HashMap();
-
- public static final Charset ISO_8859_1;
- public static final Charset UTF_8;
-
- // Protected so unit tests can use it
- protected static final int LEFTOVER_SIZE = 9;
-
- static {
- for (Charset charset: Charset.availableCharsets().values()) {
- encodingToCharsetCache.put(
- charset.name().toLowerCase(Locale.ENGLISH), charset);
- for (String alias : charset.aliases()) {
- encodingToCharsetCache.put(
- alias.toLowerCase(Locale.ENGLISH), charset);
- }
- }
- Charset iso88591 = null;
- Charset utf8 = null;
- try {
- iso88591 = getCharset("ISO-8859-1");
- utf8 = getCharset("UTF-8");
- } catch (UnsupportedEncodingException e) {
- // Impossible. All JVMs must support these.
- e.printStackTrace();
- }
- ISO_8859_1 = iso88591;
- UTF_8 = utf8;
- }
-
- public static Charset getCharset(String enc)
- throws UnsupportedEncodingException {
-
- // Encoding names should all be ASCII
- String lowerCaseEnc = enc.toLowerCase(Locale.ENGLISH);
-
- return getCharsetLower(lowerCaseEnc);
- }
-
- /**
- * Only to be used when it is known that the encoding name is in lower case.
- */
- public static Charset getCharsetLower(String lowerCaseEnc)
- throws UnsupportedEncodingException {
-
- Charset charset = encodingToCharsetCache.get(lowerCaseEnc);
-
- if (charset == null) {
- // Pre-population of the cache means this must be invalid
- throw new UnsupportedEncodingException("The character encoding " + lowerCaseEnc + " is not supported");
- }
- return charset;
- }
-
- private final CharsetDecoder decoder;
- private ByteBuffer bb = null;
- private CharBuffer cb = null;
-
- /**
- * Leftover buffer used for incomplete characters.
- */
- private final ByteBuffer leftovers;
-
- public B2CConverter(String encoding) throws IOException {
- this(encoding, false);
- }
-
- public B2CConverter(String encoding, boolean replaceOnError)
- throws IOException {
- byte[] left = new byte[LEFTOVER_SIZE];
- leftovers = ByteBuffer.wrap(left);
- CodingErrorAction action;
- if (replaceOnError) {
- action = CodingErrorAction.REPLACE;
- } else {
- action = CodingErrorAction.REPORT;
- }
- Charset charset = getCharset(encoding);
- // Special case. Use the Apache Harmony based UTF-8 decoder because it
- // - a) rejects invalid sequences that the JVM decoder does not
- // - b) fails faster for some invalid sequences
- if (charset.equals(UTF_8)) {
- decoder = new Utf8Decoder();
- } else {
- decoder = charset.newDecoder();
- }
- decoder.onMalformedInput(action);
- decoder.onUnmappableCharacter(action);
- }
-
- /**
- * Reset the decoder state.
- */
- public void recycle() {
- decoder.reset();
- leftovers.position(0);
- }
-
- /**
- * Convert the given bytes to characters.
- *
- * @param bc byte input
- * @param cc char output
- * @param endOfInput Is this all of the available data
- */
- public void convert(ByteChunk bc, CharChunk cc, boolean endOfInput)
- throws IOException {
- if ((bb == null) || (bb.array() != bc.getBuffer())) {
- // Create a new byte buffer if anything changed
- bb = ByteBuffer.wrap(bc.getBuffer(), bc.getStart(), bc.getLength());
- } else {
- // Initialize the byte buffer
- bb.limit(bc.getEnd());
- bb.position(bc.getStart());
- }
- if ((cb == null) || (cb.array() != cc.getBuffer())) {
- // Create a new char buffer if anything changed
- cb = CharBuffer.wrap(cc.getBuffer(), cc.getEnd(),
- cc.getBuffer().length - cc.getEnd());
- } else {
- // Initialize the char buffer
- cb.limit(cc.getBuffer().length);
- cb.position(cc.getEnd());
- }
- CoderResult result = null;
- // Parse leftover if any are present
- if (leftovers.position() > 0) {
- int pos = cb.position();
- // Loop until one char is decoded or there is a decoder error
- do {
- leftovers.put(bc.substractB());
- leftovers.flip();
- result = decoder.decode(leftovers, cb, endOfInput);
- leftovers.position(leftovers.limit());
- leftovers.limit(leftovers.array().length);
- } while (result.isUnderflow() && (cb.position() == pos));
- if (result.isError() || result.isMalformed()) {
- result.throwException();
- }
- bb.position(bc.getStart());
- leftovers.position(0);
- }
- // Do the decoding and get the results into the byte chunk and the char
- // chunk
- result = decoder.decode(bb, cb, endOfInput);
- if (result.isError() || result.isMalformed()) {
- result.throwException();
- } else if (result.isOverflow()) {
- // Propagate current positions to the byte chunk and char chunk, if
- // this continues the char buffer will get resized
- bc.setOffset(bb.position());
- cc.setEnd(cb.position());
- } else if (result.isUnderflow()) {
- // Propagate current positions to the byte chunk and char chunk
- bc.setOffset(bb.position());
- cc.setEnd(cb.position());
- // Put leftovers in the leftovers byte buffer
- if (bc.getLength() > 0) {
- leftovers.limit(leftovers.array().length);
- leftovers.position(bc.getLength());
- bc.substract(leftovers.array(), 0, bc.getLength());
- }
- }
- }
-}
\ No newline at end of file
diff --git a/core/src/main/java/org/apache/struts2/util/tomcat/buf/ByteChunk.java b/core/src/main/java/org/apache/struts2/util/tomcat/buf/ByteChunk.java
deleted file mode 100644
index 49cb3cde4..000000000
--- a/core/src/main/java/org/apache/struts2/util/tomcat/buf/ByteChunk.java
+++ /dev/null
@@ -1,937 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.struts2.util.tomcat.buf;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-
-/*
- * In a server it is very important to be able to operate on
- * the original byte[] without converting everything to chars.
- * Some protocols are ASCII only, and some allow different
- * non-UNICODE encodings. The encoding is not known beforehand,
- * and can even change during the execution of the protocol.
- * ( for example a multipart message may have parts with different
- * encoding )
- *
- * For HTTP it is not very clear how the encoding of RequestURI
- * and mime values can be determined, but it is a great advantage
- * to be able to parse the request without converting to string.
- */
-
-// TODO: This class could either extend ByteBuffer, or better a ByteBuffer
-// inside this way it could provide the search/etc on ByteBuffer, as a helper.
-
-/**
- * This class is used to represent a chunk of bytes, and
- * utilities to manipulate byte[].
- *
- * The buffer can be modified and used for both input and output.
- *
- * There are 2 modes: The chunk can be associated with a sink - ByteInputChannel
- * or ByteOutputChannel, which will be used when the buffer is empty (on input)
- * or filled (on output).
- * For output, it can also grow. This operating mode is selected by calling
- * setLimit() or allocate(initial, limit) with limit != -1.
- *
- * Various search and append method are defined - similar with String and
- * StringBuffer, but operating on bytes.
- *
- * This is important because it allows processing the http headers directly on
- * the received bytes, without converting to chars and Strings until the strings
- * are needed. In addition, the charset is determined later, from headers or
- * user code.
- *
- * @author dac@sun.com
- * @author James Todd [gonzo@sun.com]
- * @author Costin Manolache
- * @author Remy Maucherat
- */
-public final class ByteChunk implements Cloneable, Serializable {
-
- private static final long serialVersionUID = 1L;
-
- /** Input interface, used when the buffer is empty
- *
- * Same as java.nio.channel.ReadableByteChannel
- */
- public static interface ByteInputChannel {
- /**
- * Read new bytes ( usually the internal conversion buffer ).
- * The implementation is allowed to ignore the parameters,
- * and mutate the chunk if it wishes to implement its own buffering.
- */
- public int realReadBytes(byte cbuf[], int off, int len)
- throws IOException;
- }
-
- /** Same as java.nio.channel.WrittableByteChannel.
- */
- public static interface ByteOutputChannel {
- /**
- * Send the bytes ( usually the internal conversion buffer ).
- * Expect 8k output if the buffer is full.
- */
- public void realWriteBytes(byte cbuf[], int off, int len)
- throws IOException;
- }
-
- // --------------------
-
- /** Default encoding used to convert to strings. It should be UTF8,
- as most standards seem to converge, but the servlet API requires
- 8859_1, and this object is used mostly for servlets.
- */
- public static final Charset DEFAULT_CHARSET = B2CConverter.ISO_8859_1;
-
- // byte[]
- private byte[] buff;
-
- private int start=0;
- private int end;
-
- private Charset charset;
-
- private boolean isSet=false; // XXX
-
- // How much can it grow, when data is added
- private int limit=-1;
-
- private ByteInputChannel in = null;
- private ByteOutputChannel out = null;
-
- private boolean optimizedWrite=true;
-
- /**
- * Creates a new, uninitialized ByteChunk object.
- */
- public ByteChunk() {
- // NO-OP
- }
-
- public ByteChunk( int initial ) {
- allocate( initial, -1 );
- }
-
- /**
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public ByteChunk getClone() {
- try {
- return (ByteChunk)this.clone();
- } catch( Exception ex) {
- return null;
- }
- }
-
- public boolean isNull() {
- return ! isSet; // buff==null;
- }
-
- /**
- * Resets the message buff to an uninitialized state.
- */
- public void recycle() {
- // buff = null;
- charset=null;
- start=0;
- end=0;
- isSet=false;
- }
-
- public void reset() {
- buff=null;
- }
-
- // -------------------- Setup --------------------
-
- public void allocate( int initial, int limit ) {
- if( buff==null || buff.length < initial ) {
- buff=new byte[initial];
- }
- this.limit=limit;
- start=0;
- end=0;
- isSet=true;
- }
-
- /**
- * Sets the message bytes to the specified subarray of bytes.
- *
- * @param b the ascii bytes
- * @param off the start offset of the bytes
- * @param len the length of the bytes
- */
- public void setBytes(byte[] b, int off, int len) {
- buff = b;
- start = off;
- end = start+ len;
- isSet=true;
- }
-
- /**
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public void setOptimizedWrite(boolean optimizedWrite) {
- this.optimizedWrite = optimizedWrite;
- }
-
- public void setCharset(Charset charset) {
- this.charset = charset;
- }
-
- public Charset getCharset() {
- if (charset == null) {
- charset = DEFAULT_CHARSET;
- }
- return charset;
- }
-
- /**
- * Returns the message bytes.
- */
- public byte[] getBytes() {
- return getBuffer();
- }
-
- /**
- * Returns the message bytes.
- */
- public byte[] getBuffer() {
- return buff;
- }
-
- /**
- * Returns the start offset of the bytes.
- * For output this is the end of the buffer.
- */
- public int getStart() {
- return start;
- }
-
- public int getOffset() {
- return start;
- }
-
- public void setOffset(int off) {
- if (end < off ) {
- end=off;
- }
- start=off;
- }
-
- /**
- * Returns the length of the bytes.
- * XXX need to clean this up
- */
- public int getLength() {
- return end-start;
- }
-
- /** Maximum amount of data in this buffer.
- *
- * If -1 or not set, the buffer will grow indefinitely.
- * Can be smaller than the current buffer size ( which will not shrink ).
- * When the limit is reached, the buffer will be flushed ( if out is set )
- * or throw exception.
- */
- public void setLimit(int limit) {
- this.limit=limit;
- }
-
- public int getLimit() {
- return limit;
- }
-
- /**
- * When the buffer is empty, read the data from the input channel.
- */
- public void setByteInputChannel(ByteInputChannel in) {
- this.in = in;
- }
-
- /** When the buffer is full, write the data to the output channel.
- * Also used when large amount of data is appended.
- *
- * If not set, the buffer will grow to the limit.
- */
- public void setByteOutputChannel(ByteOutputChannel out) {
- this.out=out;
- }
-
- public int getEnd() {
- return end;
- }
-
- public void setEnd( int i ) {
- end=i;
- }
-
- // -------------------- Adding data to the buffer --------------------
- /** Append a char, by casting it to byte. This IS NOT intended for unicode.
- *
- * @param c
- * @throws IOException
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public void append( char c )
- throws IOException
- {
- append( (byte)c);
- }
-
- public void append( byte b )
- throws IOException
- {
- makeSpace( 1 );
-
- // couldn't make space
- if( limit >0 && end >= limit ) {
- flushBuffer();
- }
- buff[end++]=b;
- }
-
- public void append( ByteChunk src )
- throws IOException
- {
- append( src.getBytes(), src.getStart(), src.getLength());
- }
-
- /** Add data to the buffer
- */
- public void append( byte src[], int off, int len )
- throws IOException
- {
- // will grow, up to limit
- makeSpace( len );
-
- // if we don't have limit: makeSpace can grow as it wants
- if( limit < 0 ) {
- // assert: makeSpace made enough space
- System.arraycopy( src, off, buff, end, len );
- end+=len;
- return;
- }
-
- // Optimize on a common case.
- // If the buffer is empty and the source is going to fill up all the
- // space in buffer, may as well write it directly to the output,
- // and avoid an extra copy
- if ( optimizedWrite && len == limit && end == start && out != null ) {
- out.realWriteBytes( src, off, len );
- return;
- }
- // if we have limit and we're below
- if( len <= limit - end ) {
- // makeSpace will grow the buffer to the limit,
- // so we have space
- System.arraycopy( src, off, buff, end, len );
- end+=len;
- return;
- }
-
- // need more space than we can afford, need to flush
- // buffer
-
- // the buffer is already at ( or bigger than ) limit
-
- // We chunk the data into slices fitting in the buffer limit, although
- // if the data is written directly if it doesn't fit
-
- int avail=limit-end;
- System.arraycopy(src, off, buff, end, avail);
- end += avail;
-
- flushBuffer();
-
- int remain = len - avail;
-
- while (remain > (limit - end)) {
- out.realWriteBytes( src, (off + len) - remain, limit - end );
- remain = remain - (limit - end);
- }
-
- System.arraycopy(src, (off + len) - remain, buff, end, remain);
- end += remain;
-
- }
-
-
- // -------------------- Removing data from the buffer --------------------
-
- public int substract()
- throws IOException {
-
- if ((end - start) == 0) {
- if (in == null) {
- return -1;
- }
- int n = in.realReadBytes( buff, 0, buff.length );
- if (n < 0) {
- return -1;
- }
- }
-
- return (buff[start++] & 0xFF);
-
- }
-
-
- /**
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public int substract(ByteChunk src)
- throws IOException {
-
- if ((end - start) == 0) {
- if (in == null) {
- return -1;
- }
- int n = in.realReadBytes( buff, 0, buff.length );
- if (n < 0) {
- return -1;
- }
- }
-
- int len = getLength();
- src.append(buff, start, len);
- start = end;
- return len;
-
- }
-
-
- public byte substractB()
- throws IOException {
-
- if ((end - start) == 0) {
- if (in == null)
- return -1;
- int n = in.realReadBytes( buff, 0, buff.length );
- if (n < 0)
- return -1;
- }
-
- return (buff[start++]);
-
- }
-
-
- public int substract( byte src[], int off, int len )
- throws IOException {
-
- if ((end - start) == 0) {
- if (in == null) {
- return -1;
- }
- int n = in.realReadBytes( buff, 0, buff.length );
- if (n < 0) {
- return -1;
- }
- }
-
- int n = len;
- if (len > getLength()) {
- n = getLength();
- }
- System.arraycopy(buff, start, src, off, n);
- start += n;
- return n;
-
- }
-
-
- /**
- * Send the buffer to the sink. Called by append() when the limit is
- * reached. You can also call it explicitly to force the data to be written.
- *
- * @throws IOException
- */
- public void flushBuffer()
- throws IOException
- {
- //assert out!=null
- if( out==null ) {
- throw new IOException( "Buffer overflow, no sink " + limit + " " +
- buff.length );
- }
- out.realWriteBytes( buff, start, end-start );
- end=start;
- }
-
- /**
- * Make space for len chars. If len is small, allocate a reserve space too.
- * Never grow bigger than limit.
- */
- public void makeSpace(int count) {
- byte[] tmp = null;
-
- int newSize;
- int desiredSize=end + count;
-
- // Can't grow above the limit
- if( limit > 0 &&
- desiredSize > limit) {
- desiredSize=limit;
- }
-
- if( buff==null ) {
- if( desiredSize < 256 )
- {
- desiredSize=256; // take a minimum
- }
- buff=new byte[desiredSize];
- }
-
- // limit < buf.length ( the buffer is already big )
- // or we already have space XXX
- if( desiredSize <= buff.length ) {
- return;
- }
- // grow in larger chunks
- if( desiredSize < 2 * buff.length ) {
- newSize= buff.length * 2;
- if( limit >0 &&
- newSize > limit ) {
- newSize=limit;
- }
- tmp=new byte[newSize];
- } else {
- newSize= buff.length * 2 + count ;
- if( limit > 0 &&
- newSize > limit ) {
- newSize=limit;
- }
- tmp=new byte[newSize];
- }
-
- System.arraycopy(buff, start, tmp, 0, end-start);
- buff = tmp;
- tmp = null;
- end=end-start;
- start=0;
- }
-
- // -------------------- Conversion and getters --------------------
-
- @Override
- public String toString() {
- if (null == buff) {
- return null;
- } else if (end-start == 0) {
- return "";
- }
- return StringCache.toString(this);
- }
-
- public String toStringInternal() {
- if (charset == null) {
- charset = DEFAULT_CHARSET;
- }
- // new String(byte[], int, int, Charset) takes a defensive copy of the
- // entire byte array. This is expensive if only a small subset of the
- // bytes will be used. The code below is from Apache Harmony.
- CharBuffer cb;
- cb = charset.decode(ByteBuffer.wrap(buff, start, end-start));
- return new String(cb.array(), cb.arrayOffset(), cb.length());
- }
-
- /**
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public int getInt()
- {
- return Ascii.parseInt(buff, start,end-start);
- }
-
- public long getLong() {
- return Ascii.parseLong(buff, start,end-start);
- }
-
-
- // -------------------- equals --------------------
-
- /**
- * Compares the message bytes to the specified String object.
- * @param s the String to compare
- * @return true if the comparison succeeded, false otherwise
- */
- public boolean equals(String s) {
- // XXX ENCODING - this only works if encoding is UTF8-compat
- // ( ok for tomcat, where we compare ascii - header names, etc )!!!
-
- byte[] b = buff;
- int blen = end-start;
- if (b == null || blen != s.length()) {
- return false;
- }
- int boff = start;
- for (int i = 0; i < blen; i++) {
- if (b[boff++] != s.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Compares the message bytes to the specified String object.
- * @param s the String to compare
- * @return true if the comparison succeeded, false otherwise
- */
- public boolean equalsIgnoreCase(String s) {
- byte[] b = buff;
- int blen = end-start;
- if (b == null || blen != s.length()) {
- return false;
- }
- int boff = start;
- for (int i = 0; i < blen; i++) {
- if (Ascii.toLower(b[boff++]) != Ascii.toLower(s.charAt(i))) {
- return false;
- }
- }
- return true;
- }
-
- public boolean equals( ByteChunk bb ) {
- return equals( bb.getBytes(), bb.getStart(), bb.getLength());
- }
-
- public boolean equals( byte b2[], int off2, int len2) {
- byte b1[]=buff;
- if( b1==null && b2==null ) {
- return true;
- }
-
- int len=end-start;
- if ( len2 != len || b1==null || b2==null ) {
- return false;
- }
-
- int off1 = start;
-
- while ( len-- > 0) {
- if (b1[off1++] != b2[off2++]) {
- return false;
- }
- }
- return true;
- }
-
- public boolean equals( CharChunk cc ) {
- return equals( cc.getChars(), cc.getStart(), cc.getLength());
- }
-
- public boolean equals( char c2[], int off2, int len2) {
- // XXX works only for enc compatible with ASCII/UTF !!!
- byte b1[]=buff;
- if( c2==null && b1==null ) {
- return true;
- }
-
- if (b1== null || c2==null || end-start != len2 ) {
- return false;
- }
- int off1 = start;
- int len=end-start;
-
- while ( len-- > 0) {
- if ( (char)b1[off1++] != c2[off2++]) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Returns true if the message bytes starts with the specified string.
- * @param s the string
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public boolean startsWith(String s) {
- // Works only if enc==UTF
- byte[] b = buff;
- int blen = s.length();
- if (b == null || blen > end-start) {
- return false;
- }
- int boff = start;
- for (int i = 0; i < blen; i++) {
- if (b[boff++] != s.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Returns true if the message bytes start with the specified byte array.
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public boolean startsWith(byte[] b2) {
- byte[] b1 = buff;
- if (b1 == null && b2 == null) {
- return true;
- }
-
- int len = end - start;
- if (b1 == null || b2 == null || b2.length > len) {
- return false;
- }
- for (int i = start, j = 0; i < end && j < b2.length;) {
- if (b1[i++] != b2[j++]) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Returns true if the message bytes starts with the specified string.
- * @param s the string
- * @param pos The position
- */
- public boolean startsWithIgnoreCase(String s, int pos) {
- byte[] b = buff;
- int len = s.length();
- if (b == null || len+pos > end-start) {
- return false;
- }
- int off = start+pos;
- for (int i = 0; i < len; i++) {
- if (Ascii.toLower( b[off++] ) != Ascii.toLower( s.charAt(i))) {
- return false;
- }
- }
- return true;
- }
-
- public int indexOf( String src, int srcOff, int srcLen, int myOff ) {
- char first=src.charAt( srcOff );
-
- // Look for first char
- int srcEnd = srcOff + srcLen;
-
- mainLoop:
- for( int i=myOff+start; i <= (end - srcLen); i++ ) {
- if( buff[i] != first ) {
- continue;
- }
- // found first char, now look for a match
- int myPos=i+1;
- for( int srcPos=srcOff + 1; srcPos< srcEnd;) {
- if( buff[myPos++] != src.charAt( srcPos++ )) {
- continue mainLoop;
- }
- }
- return i-start; // found it
- }
- return -1;
- }
-
- // -------------------- Hash code --------------------
-
- // normal hash.
- public int hash() {
- return hashBytes( buff, start, end-start);
- }
-
- /**
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public int hashIgnoreCase() {
- return hashBytesIC( buff, start, end-start );
- }
-
- private static int hashBytes( byte buff[], int start, int bytesLen ) {
- int max=start+bytesLen;
- byte bb[]=buff;
- int code=0;
- for (int i = start; i < max ; i++) {
- code = code * 37 + bb[i];
- }
- return code;
- }
-
- private static int hashBytesIC( byte bytes[], int start,
- int bytesLen )
- {
- int max=start+bytesLen;
- byte bb[]=bytes;
- int code=0;
- for (int i = start; i < max ; i++) {
- code = code * 37 + Ascii.toLower(bb[i]);
- }
- return code;
- }
-
- /**
- * Returns the first instance of the given character in this ByteChunk
- * starting at the specified byte. If the character is not found, -1 is
- * returned.
- *
- * NOTE: This only works for characters in the range 0-127.
- *
- * @param c The character
- * @param starting The start position
- * @return The position of the first instance of the character or
- * -1 if the character is not found.
- */
- public int indexOf(char c, int starting) {
- int ret = indexOf(buff, start + starting, end, c);
- return (ret >= start) ? ret - start : -1;
- }
-
- /**
- * Returns the first instance of the given character in the given byte array
- * between the specified start and end.
- *
- * NOTE: This only works for characters in the range 0-127.
- *
- * @param bytes The byte array to search
- * @param start The point to start searching from in the byte array
- * @param end The point to stop searching in the byte array
- * @param c The character to search for
- * @return The position of the first instance of the character or -1
- * if the character is not found.
- */
- public static int indexOf(byte bytes[], int start, int end, char c) {
- int offset = start;
-
- while (offset < end) {
- byte b=bytes[offset];
- if (b == c) {
- return offset;
- }
- offset++;
- }
- return -1;
- }
-
- /**
- * Returns the first instance of the given byte in the byte array between
- * the specified start and end.
- *
- * @param bytes The byte array to search
- * @param start The point to start searching from in the byte array
- * @param end The point to stop searching in the byte array
- * @param b The byte to search for
- * @return The position of the first instance of the byte or -1 if the
- * byte is not found.
- */
- public static int findByte(byte bytes[], int start, int end, byte b) {
- int offset = start;
- while (offset < end) {
- if (bytes[offset] == b) {
- return offset;
- }
- offset++;
- }
- return -1;
- }
-
- /**
- * Returns the first instance of any of the given bytes in the byte array
- * between the specified start and end.
- *
- * @param bytes The byte array to search
- * @param start The point to start searching from in the byte array
- * @param end The point to stop searching in the byte array
- * @param b The array of bytes to search for
- * @return The position of the first instance of the byte or -1 if the
- * byte is not found.
- */
- public static int findBytes(byte bytes[], int start, int end, byte b[]) {
- int blen = b.length;
- int offset = start;
- while (offset < end) {
- for (int i = 0; i < blen; i++) {
- if (bytes[offset] == b[i]) {
- return offset;
- }
- }
- offset++;
- }
- return -1;
- }
-
- /**
- * Returns the first instance of any byte that is not one of the given bytes
- * in the byte array between the specified start and end.
- *
- * @param bytes The byte array to search
- * @param start The point to start searching from in the byte array
- * @param end The point to stop searching in the byte array
- * @param b The list of bytes to search for
- * @return The position of the first instance a byte that is not
- * in the list of bytes to search for or -1 if no such byte
- * is found.
- * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
- */
- @Deprecated
- public static int findNotBytes(byte bytes[], int start, int end, byte b[]) {
- int blen = b.length;
- int offset = start;
- boolean found;
-
- while (offset < end) {
- found = true;
- for (int i = 0; i < blen; i++) {
- if (bytes[offset] == b[i]) {
- found=false;
- break;
- }
- }
- if (found) {
- return offset;
- }
- offset++;
- }
- return -1;
- }
-
-
- /**
- * Convert specified String to a byte array. This ONLY WORKS for ascii, UTF
- * chars will be truncated.
- *
- * @param value to convert to byte array
- * @return the byte array value
- */
- public static final byte[] convertToBytes(String value) {
- byte[] result = new byte[value.length()];
- for (int i = 0; i < value.length(); i++) {
- result[i] = (byte) value.charAt(i);
- }
- return result;
- }
-}
\ No newline at end of file
diff --git a/core/src/main/java/org/apache/struts2/util/tomcat/buf/CharChunk.java b/core/src/main/java/org/apache/struts2/util/tomcat/buf/CharChunk.java
deleted file mode 100644
index c612abe49..000000000
--- a/core/src/main/java/org/apache/struts2/util/tomcat/buf/CharChunk.java
+++ /dev/null
@@ -1,702 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.struts2.util.tomcat.buf;
-
-import java.io.IOException;
-import java.io.Serializable;
-
-/**
- * Utilities to manipulate char chunks. While String is
- * the easiest way to manipulate chars ( search, substrings, etc),
- * it is known to not be the most efficient solution - Strings are
- * designed as immutable and secure objects.
- *
- * @author dac@sun.com
- * @author James Todd [gonzo@sun.com]
- * @author Costin Manolache
- * @author Remy Maucherat
- */
-public final class CharChunk implements Cloneable, Serializable, CharSequence {
-
- private static final long serialVersionUID = 1L;
-
- // Input interface, used when the buffer is emptied.
- public static interface CharInputChannel {
- /**
- * Read new bytes ( usually the internal conversion buffer ).
- * The implementation is allowed to ignore the parameters,
- * and mutate the chunk if it wishes to implement its own buffering.
- */
- public int realReadChars(char cbuf[], int off, int len)
- throws IOException;
- }
- /**
- * When we need more space we'll either
- * grow the buffer ( up to the limit ) or send it to a channel.
- */
- public static interface CharOutputChannel {
- /** Send the bytes ( usually the internal conversion buffer ).
- * Expect 8k output if the buffer is full.
- */
- public void realWriteChars(char cbuf[], int off, int len)
- throws IOException;
- }
-
- // --------------------
-
- private int hashCode = 0;
- // did we compute the hashcode ?
- private boolean hasHashCode = false;
-
- // char[]
- private char buff[];
-
- private int start;
- private int end;
-
- private boolean isSet=false; // XXX
-
- // -1: grow indefinitely
- // maximum amount to be cached
- private int limit=-1;
-
- private CharInputChannel in = null;
- private CharOutputChannel out = null;
-
- private boolean optimizedWrite=true;
-
- /**
- * Creates a new, uninitialized CharChunk object.
- */
- public CharChunk() {
- }
-
- public CharChunk(int size) {
- allocate( size, -1 );
- }
-
- // --------------------
-
- public boolean isNull() {
- if( end > 0 ) {
- return false;
- }
- return !isSet; //XXX
- }
-
- /**
- * Resets the message bytes to an uninitialized state.
- */
- public void recycle() {
- // buff=null;
- isSet=false; // XXX
- hasHashCode = false;
- start=0;
- end=0;
- }
-
- // -------------------- Setup --------------------
-
- public void allocate( int initial, int limit ) {
- if( buff==null || buff.length < initial ) {
- buff=new char[initial];
- }
- this.limit=limit;
- start=0;
- end=0;
- isSet=true;
- hasHashCode = false;
- }
-
-
- public void setOptimizedWrite(boolean optimizedWrite) {
- this.optimizedWrite = optimizedWrite;
- }
-
- public void setChars( char[] c, int off, int len ) {
- buff=c;
- start=off;
- end=start + len;
- isSet=true;
- hasHashCode = false;
- }
-
- /** Maximum amount of data in this buffer.
- *
- * If -1 or not set, the buffer will grow indefinitely.
- * Can be smaller than the current buffer size ( which will not shrink ).
- * When the limit is reached, the buffer will be flushed ( if out is set )
- * or throw exception.
- */
- public void setLimit(int limit) {
- this.limit=limit;
- }
-
- public int getLimit() {
- return limit;
- }
-
- /**
- * When the buffer is empty, read the data from the input channel.
- */
- public void setCharInputChannel(CharInputChannel in) {
- this.in = in;
- }
-
- /** When the buffer is full, write the data to the output channel.
- * Also used when large amount of data is appended.
- *
- * If not set, the buffer will grow to the limit.
- */
- public void setCharOutputChannel(CharOutputChannel out) {
- this.out=out;
- }
-
- // compat
- public char[] getChars()
- {
- return getBuffer();
- }
-
- public char[] getBuffer()
- {
- return buff;
- }
-
- /**
- * Returns the start offset of the bytes.
- * For output this is the end of the buffer.
- */
- public int getStart() {
- return start;
- }
-
- public int getOffset() {
- return start;
- }
-
- /**
- * Returns the start offset of the bytes.
- */
- public void setOffset(int off) {
- start=off;
- }
-
- /**
- * Returns the length of the bytes.
- */
- public int getLength() {
- return end-start;
- }
-
-
- public int getEnd() {
- return end;
- }
-
- public void setEnd( int i ) {
- end=i;
- }
-
- // -------------------- Adding data --------------------
-
- public void append( char b )
- throws IOException
- {
- makeSpace( 1 );
-
- // couldn't make space
- if( limit >0 && end >= limit ) {
- flushBuffer();
- }
- buff[end++]=b;
- }
-
- public void append( CharChunk src )
- throws IOException
- {
- append( src.getBuffer(), src.getOffset(), src.getLength());
- }
-
- /** Add data to the buffer
- */
- public void append( char src[], int off, int len )
- throws IOException
- {
- // will grow, up to limit
- makeSpace( len );
-
- // if we don't have limit: makeSpace can grow as it wants
- if( limit < 0 ) {
- // assert: makeSpace made enough space
- System.arraycopy( src, off, buff, end, len );
- end+=len;
- return;
- }
-
- // Optimize on a common case.
- // If the source is going to fill up all the space in buffer, may
- // as well write it directly to the output, and avoid an extra copy
- if ( optimizedWrite && len == limit && end == start && out != null ) {
- out.realWriteChars( src, off, len );
- return;
- }
-
- // if we have limit and we're below
- if( len <= limit - end ) {
- // makeSpace will grow the buffer to the limit,
- // so we have space
- System.arraycopy( src, off, buff, end, len );
-
- end+=len;
- return;
- }
-
- // need more space than we can afford, need to flush
- // buffer
-
- // the buffer is already at ( or bigger than ) limit
-
- // Optimization:
- // If len-avail < length ( i.e. after we fill the buffer with
- // what we can, the remaining will fit in the buffer ) we'll just
- // copy the first part, flush, then copy the second part - 1 write
- // and still have some space for more. We'll still have 2 writes, but
- // we write more on the first.
-
- if( len + end < 2 * limit ) {
- /* If the request length exceeds the size of the output buffer,
- flush the output buffer and then write the data directly.
- We can't avoid 2 writes, but we can write more on the second
- */
- int avail=limit-end;
- System.arraycopy(src, off, buff, end, avail);
- end += avail;
-
- flushBuffer();
-
- System.arraycopy(src, off+avail, buff, end, len - avail);
- end+= len - avail;
-
- } else { // len > buf.length + avail
- // long write - flush the buffer and write the rest
- // directly from source
- flushBuffer();
-
- out.realWriteChars( src, off, len );
- }
- }
-
-
- /** Append a string to the buffer
- */
- public void append(String s) throws IOException {
- append(s, 0, s.length());
- }
-
- /** Append a string to the buffer
- */
- public void append(String s, int off, int len) throws IOException {
- if (s==null) {
- return;
- }
-
- // will grow, up to limit
- makeSpace( len );
-
- // if we don't have limit: makeSpace can grow as it wants
- if( limit < 0 ) {
- // assert: makeSpace made enough space
- s.getChars(off, off+len, buff, end );
- end+=len;
- return;
- }
-
- int sOff = off;
- int sEnd = off + len;
- while (sOff < sEnd) {
- int d = min(limit - end, sEnd - sOff);
- s.getChars( sOff, sOff+d, buff, end);
- sOff += d;
- end += d;
- if (end >= limit) {
- flushBuffer();
- }
- }
- }
-
- // -------------------- Removing data from the buffer --------------------
-
- public int substract()
- throws IOException {
-
- if ((end - start) == 0) {
- if (in == null) {
- return -1;
- }
- int n = in.realReadChars(buff, end, buff.length - end);
- if (n < 0) {
- return -1;
- }
- }
-
- return (buff[start++]);
-
- }
-
- public int substract( char src[], int off, int len )
- throws IOException {
-
- if ((end - start) == 0) {
- if (in == null) {
- return -1;
- }
- int n = in.realReadChars( buff, end, buff.length - end);
- if (n < 0) {
- return -1;
- }
- }
-
- int n = len;
- if (len > getLength()) {
- n = getLength();
- }
- System.arraycopy(buff, start, src, off, n);
- start += n;
- return n;
-
- }
-
-
- public void flushBuffer()
- throws IOException
- {
- //assert out!=null
- if( out==null ) {
- throw new IOException( "Buffer overflow, no sink " + limit + " " +
- buff.length );
- }
- out.realWriteChars( buff, start, end - start );
- end=start;
- }
-
- /** Make space for len chars. If len is small, allocate
- * a reserve space too. Never grow bigger than limit.
- */
- public void makeSpace(int count)
- {
- char[] tmp = null;
-
- int newSize;
- int desiredSize=end + count;
-
- // Can't grow above the limit
- if( limit > 0 &&
- desiredSize > limit) {
- desiredSize=limit;
- }
-
- if( buff==null ) {
- if( desiredSize < 256 )
- {
- desiredSize=256; // take a minimum
- }
- buff=new char[desiredSize];
- }
-
- // limit < buf.length ( the buffer is already big )
- // or we already have space XXX
- if( desiredSize <= buff.length) {
- return;
- }
- // grow in larger chunks
- if( desiredSize < 2 * buff.length ) {
- newSize= buff.length * 2;
- if( limit >0 &&
- newSize > limit ) {
- newSize=limit;
- }
- tmp=new char[newSize];
- } else {
- newSize= buff.length * 2 + count ;
- if( limit > 0 &&
- newSize > limit ) {
- newSize=limit;
- }
- tmp=new char[newSize];
- }
-
- System.arraycopy(buff, 0, tmp, 0, end);
- buff = tmp;
- tmp = null;
- }
-
- // -------------------- Conversion and getters --------------------
-
- @Override
- public String toString() {
- if (null == buff) {
- return null;
- } else if (end-start == 0) {
- return "";
- }
- return StringCache.toString(this);
- }
-
- public String toStringInternal() {
- return new String(buff, start, end-start);
- }
-
- // -------------------- equals --------------------
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof CharChunk) {
- return equals((CharChunk) obj);
- }
- return false;
- }
-
- /**
- * Compares the message bytes to the specified String object.
- * @param s the String to compare
- * @return true if the comparison succeeded, false otherwise
- */
- public boolean equals(String s) {
- char[] c = buff;
- int len = end-start;
- if (c == null || len != s.length()) {
- return false;
- }
- int off = start;
- for (int i = 0; i < len; i++) {
- if (c[off++] != s.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Compares the message bytes to the specified String object.
- * @param s the String to compare
- * @return true if the comparison succeeded, false otherwise
- */
- public boolean equalsIgnoreCase(String s) {
- char[] c = buff;
- int len = end-start;
- if (c == null || len != s.length()) {
- return false;
- }
- int off = start;
- for (int i = 0; i < len; i++) {
- if (Ascii.toLower( c[off++] ) != Ascii.toLower( s.charAt(i))) {
- return false;
- }
- }
- return true;
- }
-
- public boolean equals(CharChunk cc) {
- return equals( cc.getChars(), cc.getOffset(), cc.getLength());
- }
-
- public boolean equals(char b2[], int off2, int len2) {
- char b1[]=buff;
- if( b1==null && b2==null ) {
- return true;
- }
-
- if (b1== null || b2==null || end-start != len2) {
- return false;
- }
- int off1 = start;
- int len=end-start;
- while ( len-- > 0) {
- if (b1[off1++] != b2[off2++]) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Returns true if the message bytes starts with the specified string.
- * @param s the string
- */
- public boolean startsWith(String s) {
- char[] c = buff;
- int len = s.length();
- if (c == null || len > end-start) {
- return false;
- }
- int off = start;
- for (int i = 0; i < len; i++) {
- if (c[off++] != s.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Returns true if the message bytes starts with the specified string.
- * @param s the string
- */
- public boolean startsWithIgnoreCase(String s, int pos) {
- char[] c = buff;
- int len = s.length();
- if (c == null || len+pos > end-start) {
- return false;
- }
- int off = start+pos;
- for (int i = 0; i < len; i++) {
- if (Ascii.toLower( c[off++] ) != Ascii.toLower( s.charAt(i))) {
- return false;
- }
- }
- return true;
- }
-
-
- /**
- * Returns true if the message bytes end with the specified string.
- * @param s the string
- */
- public boolean endsWith(String s) {
- char[] c = buff;
- int len = s.length();
- if (c == null || len > end-start) {
- return false;
- }
- int off = end - len;
- for (int i = 0; i < len; i++) {
- if (c[off++] != s.charAt(i)) {
- return false;
- }
- }
- return true;
- }
-
- // -------------------- Hash code --------------------
-
- @Override
- public int hashCode() {
- if (hasHashCode) {
- return hashCode;
- }
- int code = 0;
-
- code = hash();
- hashCode = code;
- hasHashCode = true;
- return code;
- }
-
- // normal hash.
- public int hash() {
- int code=0;
- for (int i = start; i < start + end-start; i++) {
- code = code * 37 + buff[i];
- }
- return code;
- }
-
- public int indexOf(char c) {
- return indexOf( c, start);
- }
-
- /**
- * Returns true if the message bytes starts with the specified string.
- * @param c the character
- */
- public int indexOf(char c, int starting) {
- int ret = indexOf( buff, start+starting, end, c );
- return (ret >= start) ? ret - start : -1;
- }
-
- public static int indexOf( char chars[], int off, int cend, char qq )
- {
- while( off < cend ) {
- char b=chars[off];
- if( b==qq ) {
- return off;
- }
- off++;
- }
- return -1;
- }
-
-
- public int indexOf(String src, int srcOff, int srcLen, int myOff ) {
- char first=src.charAt( srcOff );
-
- // Look for first char
- int srcEnd = srcOff + srcLen;
-
- for( int i=myOff+start; i <= (end - srcLen); i++ ) {
- if( buff[i] != first ) {
- continue;
- }
- // found first char, now look for a match
- int myPos=i+1;
- for( int srcPos=srcOff + 1; srcPos< srcEnd;) {
- if( buff[myPos++] != src.charAt( srcPos++ )) {
- break;
- }
- if( srcPos==srcEnd )
- {
- return i-start; // found it
- }
- }
- }
- return -1;
- }
-
- // -------------------- utils
- private int min(int a, int b) {
- if (a < b) {
- return a;
- }
- return b;
- }
-
- // Char sequence impl
-
- public char charAt(int index) {
- return buff[index + start];
- }
-
- public CharSequence subSequence(int start, int end) {
- try {
- CharChunk result = (CharChunk) this.clone();
- result.setOffset(this.start + start);
- result.setEnd(this.start + end);
- return result;
- } catch (CloneNotSupportedException e) {
- // Cannot happen
- return null;
- }
- }
-
- public int length() {
- return end - start;
- }
-
-}
diff --git a/core/src/main/java/org/apache/struts2/util/tomcat/buf/HexUtils.java b/core/src/main/java/org/apache/struts2/util/tomcat/buf/HexUtils.java
deleted file mode 100644
index 461675276..000000000
--- a/core/src/main/java/org/apache/struts2/util/tomcat/buf/HexUtils.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.struts2.util.tomcat.buf;
-
-/**
- * Tables useful when converting byte arrays to and from strings of hexadecimal
- * digits.
- * Code from Ajp11, from Apache's JServ.
- *
- * @author Craig R. McClanahan
- */
-public final class HexUtils {
-
- // -------------------------------------------------------------- Constants
-
- /**
- * Table for HEX to DEC byte translation.
- */
- private static final int[] DEC = {
- 00, 01, 02, 03, 04, 05, 06, 07, 8, 9, -1, -1, -1, -1, -1, -1,
- -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 10, 11, 12, 13, 14, 15,
- };
-
-
- /**
- * Table for DEC to HEX byte translation.
- */
- private static final byte[] HEX =
- { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
- (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b',
- (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' };
-
-
- /**
- * Table for byte to hex string translation.
- */
- private static final char[] hex = "0123456789abcdef".toCharArray();
-
-
- // --------------------------------------------------------- Static Methods
-
- public static int getDec(int index) {
- // Fast for correct values, slower for incorrect ones
- try {
- return DEC[index - '0'];
- } catch (ArrayIndexOutOfBoundsException ex) {
- return -1;
- }
- }
-
-
- public static byte getHex(int index) {
- return HEX[index];
- }
-
-
- public static String toHexString(byte[] bytes) {
- if (null == bytes) {
- return null;
- }
-
- StringBuilder sb = new StringBuilder(bytes.length << 1);
-
- for(int i = 0; i < bytes.length; ++i) {
- sb.append(hex[(bytes[i] & 0xf0) >> 4])
- .append(hex[(bytes[i] & 0x0f)])
- ;
- }
-
- return sb.toString();
- }
-
-
- public static byte[] fromHexString(String input) {
- if (input == null) {
- return null;
- }
-
- if ((input.length() & 1) == 1) {
- // Odd number of characters
- throw new IllegalArgumentException("The input must consist of an even number of hex digits");
- }
-
- char[] inputChars = input.toCharArray();
- byte[] result = new byte[input.length() >> 1];
- for (int i = 0; i < result.length; i++) {
- int upperNibble = getDec(inputChars[2*i]);
- int lowerNibble = getDec(inputChars[2*i + 1]);
- if (upperNibble < 0 || lowerNibble < 0) {
- // Non hex character
- throw new IllegalArgumentException("The input must consist only of hex digits");
- }
- result[i] = (byte) ((upperNibble << 4) + lowerNibble);
- }
- return result;
- }
-}
diff --git a/core/src/main/java/org/apache/struts2/util/tomcat/buf/MessageBytes.java b/core/src/main/java/org/apache/struts2/util/tomcat/buf/MessageBytes.java
deleted file mode 100644
index db7ab8a71..000000000
--- a/core/src/main/java/org/apache/struts2/util/tomcat/buf/MessageBytes.java
+++ /dev/null
@@ -1,548 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.struts2.util.tomcat.buf;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.nio.ByteBuffer;
-import java.nio.charset.Charset;
-import java.util.Locale;
-
-/**
- * This class is used to represent a subarray of bytes in an HTTP message.
- * It represents all request/response elements. The byte/char conversions are
- * delayed and cached. Everything is recyclable.
- *
- * The object can represent a byte[], a char[], or a (sub) String. All
- * operations can be made in case sensitive mode or not.
- *
- * @author dac@eng.sun.com
- * @author James Todd [gonzo@eng.sun.com]
- * @author Costin Manolache
- */
-public final class MessageBytes implements Cloneable, Serializable {
- private static final long serialVersionUID = 1L;
-
- // primary type ( whatever is set as original value )
- private int type = T_NULL;
-
- public static final int T_NULL = 0;
- /** getType() is T_STR if the the object used to create the MessageBytes
- was a String */
- public static final int T_STR = 1;
- /** getType() is T_STR if the the object used to create the MessageBytes
- was a byte[] */
- public static final int T_BYTES = 2;
- /** getType() is T_STR if the the object used to create the MessageBytes
- was a char[] */
- public static final int T_CHARS = 3;
-
- private int hashCode=0;
- // did we compute the hashcode ?
- private boolean hasHashCode=false;
-
- // Internal objects to represent array + offset, and specific methods
- private final ByteChunk byteC=new ByteChunk();
- private final CharChunk charC=new CharChunk();
-
- // String
- private String strValue;
- // true if a String value was computed. Probably not needed,
- // strValue!=null is the same
- private boolean hasStrValue=false;
-
- /**
- * Creates a new, uninitialized MessageBytes object.
- * Use static newInstance() in order to allow
- * future hooks.
- */
- private MessageBytes() {
- }
-
- /** Construct a new MessageBytes instance
- */
- public static MessageBytes newInstance() {
- return factory.newInstance();
- }
-
- public boolean isNull() {
- // should we check also hasStrValue ???
- return byteC.isNull() && charC.isNull() && ! hasStrValue;
- // bytes==null && strValue==null;
- }
-
- /**
- * Resets the message bytes to an uninitialized (NULL) state.
- */
- public void recycle() {
- type=T_NULL;
- byteC.recycle();
- charC.recycle();
-
- strValue=null;
-
- hasStrValue=false;
- hasHashCode=false;
- hasLongValue=false;
- }
-
-
- /**
- * Sets the content to the specified subarray of bytes.
- *
- * @param b the bytes
- * @param off the start offset of the bytes
- * @param len the length of the bytes
- */
- public void setBytes(byte[] b, int off, int len) {
- byteC.setBytes( b, off, len );
- type=T_BYTES;
- hasStrValue=false;
- hasHashCode=false;
- hasLongValue=false;
- }
-
- /**
- * Sets the content to be a char[]
- *
- * @param c the bytes
- * @param off the start offset of the bytes
- * @param len the length of the bytes
- */
- public void setChars( char[] c, int off, int len ) {
- charC.setChars( c, off, len );
- type=T_CHARS;
- hasStrValue=false;
- hasHashCode=false;
- hasLongValue=false;
- }
-
- /**
- * Set the content to be a string
- */
- public void setString( String s ) {
- strValue=s;
- hasHashCode=false;
- hasLongValue=false;
- if (s == null) {
- hasStrValue=false;
- type=T_NULL;
- } else {
- hasStrValue=true;
- type=T_STR;
- }
- }
-
- // -------------------- Conversion and getters --------------------
-
- /** Compute the string value
- */
- @Override
- public String toString() {
- if( hasStrValue ) {
- return strValue;
- }
-
- switch (type) {
- case T_CHARS:
- strValue=charC.toString();
- hasStrValue=true;
- return strValue;
- case T_BYTES:
- strValue=byteC.toString();
- hasStrValue=true;
- return strValue;
- }
- return null;
- }
-
- //----------------------------------------
- /** Return the type of the original content. Can be
- * T_STR, T_BYTES, T_CHARS or T_NULL
- */
- public int getType() {
- return type;
- }
-
- /**
- * Returns the byte chunk, representing the byte[] and offset/length.
- * Valid only if T_BYTES or after a conversion was made.
- */
- public ByteChunk getByteChunk() {
- return byteC;
- }
-
- /**
- * Returns the char chunk, representing the char[] and offset/length.
- * Valid only if T_CHARS or after a conversion was made.
- */
- public CharChunk getCharChunk() {
- return charC;
- }
-
- /**
- * Returns the string value.
- * Valid only if T_STR or after a conversion was made.
- */
- public String getString() {
- return strValue;
- }
-
- /**
- * Get the Charset used for string<->byte conversions.
- */
- public Charset getCharset() {
- return byteC.getCharset();
- }
-
- /**
- * Set the Charset used for string<->byte conversions.
- */
- public void setCharset(Charset charset) {
- byteC.setCharset(charset);
- }
-
- /** Do a char->byte conversion.
- */
- public void toBytes() {
- if (!byteC.isNull()) {
- type=T_BYTES;
- return;
- }
- toString();
- type=T_BYTES;
- Charset charset = byteC.getCharset();
- ByteBuffer result = charset.encode(strValue);
- byteC.setBytes(result.array(), result.arrayOffset(), result.limit());
- }
-
- /** Convert to char[] and fill the CharChunk.
- * XXX Not optimized - it converts to String first.
- */
- public void toChars() {
- if( ! charC.isNull() ) {
- type=T_CHARS;
- return;
- }
- // inefficient
- toString();
- type=T_CHARS;
- char cc[]=strValue.toCharArray();
- charC.setChars(cc, 0, cc.length);
- }
-
-
- /**
- * Returns the length of the original buffer.
- * Note that the length in bytes may be different from the length
- * in chars.
- */
- public int getLength() {
- if(type==T_BYTES) {
- return byteC.getLength();
- }
- if(type==T_CHARS) {
- return charC.getLength();
- }
- if(type==T_STR) {
- return strValue.length();
- }
- toString();
- if( strValue==null ) {
- return 0;
- }
- return strValue.length();
- }
-
- // -------------------- equals --------------------
-
- /**
- * Compares the message bytes to the specified String object.
- * @param s the String to compare
- * @return true if the comparison succeeded, false otherwise
- */
- public boolean equals(String s) {
- switch (type) {
- case T_STR:
- if (strValue == null) {
- return s == null;
- }
- return strValue.equals( s );
- case T_CHARS:
- return charC.equals( s );
- case T_BYTES:
- return byteC.equals( s );
- default:
- return false;
- }
- }
-
- /**
- * Compares the message bytes to the specified String object.
- * @param s the String to compare
- * @return true if the comparison succeeded, false otherwise
- */
- public boolean equalsIgnoreCase(String s) {
- switch (type) {
- case T_STR:
- if (strValue == null) {
- return s == null;
- }
- return strValue.equalsIgnoreCase( s );
- case T_CHARS:
- return charC.equalsIgnoreCase( s );
- case T_BYTES:
- return byteC.equalsIgnoreCase( s );
- default:
- return false;
- }
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof MessageBytes) {
- return equals((MessageBytes) obj);
- }
- return false;
- }
-
- public boolean equals(MessageBytes mb) {
- switch (type) {
- case T_STR:
- return mb.equals( strValue );
- }
-
- if( mb.type != T_CHARS &&
- mb.type!= T_BYTES ) {
- // it's a string or int/date string value
- return equals( mb.toString() );
- }
-
- // mb is either CHARS or BYTES.
- // this is either CHARS or BYTES
- // Deal with the 4 cases ( in fact 3, one is symmetric)
-
- if( mb.type == T_CHARS && type==T_CHARS ) {
- return charC.equals( mb.charC );
- }
- if( mb.type==T_BYTES && type== T_BYTES ) {
- return byteC.equals( mb.byteC );
- }
- if( mb.type== T_CHARS && type== T_BYTES ) {
- return byteC.equals( mb.charC );
- }
- if( mb.type== T_BYTES && type== T_CHARS ) {
- return mb.byteC.equals( charC );
- }
- // can't happen
- return true;
- }
-
-
- /**
- * Returns true if the message bytes starts with the specified string.
- * @param s the string
- * @param pos The start position
- */
- public boolean startsWithIgnoreCase(String s, int pos) {
- switch (type) {
- case T_STR:
- if( strValue==null ) {
- return false;
- }
- if( strValue.length() < pos + s.length() ) {
- return false;
- }
-
- for( int i=0; i 0) {
- int digit = (int) (current % 10);
- current = current / 10;
- buf[end++] = HexUtils.getHex(digit);
- }
- byteC.setOffset(0);
- byteC.setEnd(end);
- // Inverting buffer
- end--;
- if (l < 0) {
- start++;
- }
- while (end > start) {
- byte temp = buf[start];
- buf[start] = buf[end];
- buf[end] = temp;
- start++;
- end--;
- }
- longValue=l;
- hasStrValue=false;
- hasHashCode=false;
- hasLongValue=true;
- type=T_BYTES;
- }
-
- // Used for headers conversion
- /** Convert the buffer to an long, cache the value
- */
- public long getLong() {
- if( hasLongValue ) {
- return longValue;
- }
-
- switch (type) {
- case T_BYTES:
- longValue=byteC.getLong();
- break;
- default:
- longValue= Long.parseLong(toString());
- }
-
- hasLongValue=true;
- return longValue;
-
- }
-
- // -------------------- Future may be different --------------------
-
- private static final MessageBytesFactory factory=new MessageBytesFactory();
-
- private static class MessageBytesFactory {
- protected MessageBytesFactory() {
- }
- public MessageBytes newInstance() {
- return new MessageBytes();
- }
- }
-}
diff --git a/core/src/main/java/org/apache/struts2/util/tomcat/buf/StringCache.java b/core/src/main/java/org/apache/struts2/util/tomcat/buf/StringCache.java
deleted file mode 100644
index d568ea5fa..000000000
--- a/core/src/main/java/org/apache/struts2/util/tomcat/buf/StringCache.java
+++ /dev/null
@@ -1,697 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.struts2.util.tomcat.buf;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map.Entry;
-import java.util.TreeMap;
-
-/**
- * This class implements a String cache for ByteChunk and CharChunk.
- *
- * @author Remy Maucherat
- */
-public class StringCache {
-
-
- private static final Logger log = LogManager.getLogger(StringCache.class);
-
-
- // ------------------------------------------------------- Static Variables
-
-
- /**
- * Enabled ?
- */
- protected static boolean byteEnabled = ("true".equals(System.getProperty(
- "tomcat.util.buf.StringCache.byte.enabled", "false")));
-
-
- protected static boolean charEnabled = ("true".equals(System.getProperty(
- "tomcat.util.buf.StringCache.char.enabled", "false")));
-
-
- protected static int trainThreshold = Integer.parseInt(System.getProperty(
- "tomcat.util.buf.StringCache.trainThreshold", "20000"));
-
-
- protected static int cacheSize = Integer.parseInt(System.getProperty(
- "tomcat.util.buf.StringCache.cacheSize", "200"));
-
-
- protected static final int maxStringSize =
- Integer.parseInt(System.getProperty(
- "tomcat.util.buf.StringCache.maxStringSize", "128"));
-
-
- /**
- * Statistics hash map for byte chunk.
- */
- protected static final HashMap bcStats =
- new HashMap(cacheSize);
-
-
- /**
- * toString count for byte chunk.
- */
- protected static int bcCount = 0;
-
-
- /**
- * Cache for byte chunk.
- */
- protected static ByteEntry[] bcCache = null;
-
-
- /**
- * Statistics hash map for char chunk.
- */
- protected static final HashMap ccStats =
- new HashMap(cacheSize);
-
-
- /**
- * toString count for char chunk.
- */
- protected static int ccCount = 0;
-
-
- /**
- * Cache for char chunk.
- */
- protected static CharEntry[] ccCache = null;
-
-
- /**
- * Access count.
- */
- protected static int accessCount = 0;
-
-
- /**
- * Hit count.
- */
- protected static int hitCount = 0;
-
-
- // ------------------------------------------------------------ Properties
-
-
- /**
- * @return Returns the cacheSize.
- */
- public int getCacheSize() {
- return cacheSize;
- }
-
-
- /**
- * @param cacheSize The cacheSize to set.
- */
- public void setCacheSize(int cacheSize) {
- StringCache.cacheSize = cacheSize;
- }
-
-
- /**
- * @return Returns the enabled.
- */
- public boolean getByteEnabled() {
- return byteEnabled;
- }
-
-
- /**
- * @param byteEnabled The enabled to set.
- */
- public void setByteEnabled(boolean byteEnabled) {
- StringCache.byteEnabled = byteEnabled;
- }
-
-
- /**
- * @return Returns the enabled.
- */
- public boolean getCharEnabled() {
- return charEnabled;
- }
-
-
- /**
- * @param charEnabled The enabled to set.
- */
- public void setCharEnabled(boolean charEnabled) {
- StringCache.charEnabled = charEnabled;
- }
-
-
- /**
- * @return Returns the trainThreshold.
- */
- public int getTrainThreshold() {
- return trainThreshold;
- }
-
-
- /**
- * @param trainThreshold The trainThreshold to set.
- */
- public void setTrainThreshold(int trainThreshold) {
- StringCache.trainThreshold = trainThreshold;
- }
-
-
- /**
- * @return Returns the accessCount.
- */
- public int getAccessCount() {
- return accessCount;
- }
-
-
- /**
- * @return Returns the hitCount.
- */
- public int getHitCount() {
- return hitCount;
- }
-
-
- // -------------------------------------------------- Public Static Methods
-
-
- public void reset() {
- hitCount = 0;
- accessCount = 0;
- synchronized (bcStats) {
- bcCache = null;
- bcCount = 0;
- }
- synchronized (ccStats) {
- ccCache = null;
- ccCount = 0;
- }
- }
-
-
- public static String toString(ByteChunk bc) {
-
- // If the cache is null, then either caching is disabled, or we're
- // still training
- if (bcCache == null) {
- String value = bc.toStringInternal();
- if (byteEnabled && (value.length() < maxStringSize)) {
- // If training, everything is synced
- synchronized (bcStats) {
- // If the cache has been generated on a previous invocation
- // while waiting for the lock, just return the toString
- // value we just calculated
- if (bcCache != null) {
- return value;
- }
- // Two cases: either we just exceeded the train count, in
- // which case the cache must be created, or we just update
- // the count for the string
- if (bcCount > trainThreshold) {
- long t1 = System.currentTimeMillis();
- // Sort the entries according to occurrence
- TreeMap> tempMap =
- new TreeMap>();
- for (Entry item : bcStats.entrySet()) {
- ByteEntry entry = item.getKey();
- int[] countA = item.getValue();
- Integer count = Integer.valueOf(countA[0]);
- // Add to the list for that count
- ArrayList list = tempMap.get(count);
- if (list == null) {
- // Create list
- list = new ArrayList();
- tempMap.put(count, list);
- }
- list.add(entry);
- }
- // Allocate array of the right size
- int size = bcStats.size();
- if (size > cacheSize) {
- size = cacheSize;
- }
- ByteEntry[] tempbcCache = new ByteEntry[size];
- // Fill it up using an alphabetical order
- // and a dumb insert sort
- ByteChunk tempChunk = new ByteChunk();
- int n = 0;
- while (n < size) {
- Object key = tempMap.lastKey();
- ArrayList list = tempMap.get(key);
- for (int i = 0; i < list.size() && n < size; i++) {
- ByteEntry entry = list.get(i);
- tempChunk.setBytes(entry.name, 0,
- entry.name.length);
- int insertPos = findClosest(tempChunk,
- tempbcCache, n);
- if (insertPos == n) {
- tempbcCache[n + 1] = entry;
- } else {
- System.arraycopy(tempbcCache, insertPos + 1,
- tempbcCache, insertPos + 2,
- n - insertPos - 1);
- tempbcCache[insertPos + 1] = entry;
- }
- n++;
- }
- tempMap.remove(key);
- }
- bcCount = 0;
- bcStats.clear();
- bcCache = tempbcCache;
- if (log.isDebugEnabled()) {
- long t2 = System.currentTimeMillis();
- log.debug("ByteCache generation time: " +
- (t2 - t1) + "ms");
- }
- } else {
- bcCount++;
- // Allocate new ByteEntry for the lookup
- ByteEntry entry = new ByteEntry();
- entry.value = value;
- int[] count = bcStats.get(entry);
- if (count == null) {
- int end = bc.getEnd();
- int start = bc.getStart();
- // Create byte array and copy bytes
- entry.name = new byte[bc.getLength()];
- System.arraycopy(bc.getBuffer(), start, entry.name,
- 0, end - start);
- // Set encoding
- entry.charset = bc.getCharset();
- // Initialize occurrence count to one
- count = new int[1];
- count[0] = 1;
- // Set in the stats hash map
- bcStats.put(entry, count);
- } else {
- count[0] = count[0] + 1;
- }
- }
- }
- }
- return value;
- } else {
- accessCount++;
- // Find the corresponding String
- String result = find(bc);
- if (result == null) {
- return bc.toStringInternal();
- }
- // Note: We don't care about safety for the stats
- hitCount++;
- return result;
- }
-
- }
-
-
- public static String toString(CharChunk cc) {
-
- // If the cache is null, then either caching is disabled, or we're
- // still training
- if (ccCache == null) {
- String value = cc.toStringInternal();
- if (charEnabled && (value.length() < maxStringSize)) {
- // If training, everything is synced
- synchronized (ccStats) {
- // If the cache has been generated on a previous invocation
- // while waiting for the lock, just return the toString
- // value we just calculated
- if (ccCache != null) {
- return value;
- }
- // Two cases: either we just exceeded the train count, in
- // which case the cache must be created, or we just update
- // the count for the string
- if (ccCount > trainThreshold) {
- long t1 = System.currentTimeMillis();
- // Sort the entries according to occurrence
- TreeMap> tempMap =
- new TreeMap>();
- for (Entry item : ccStats.entrySet()) {
- CharEntry entry = item.getKey();
- int[] countA = item.getValue();
- Integer count = Integer.valueOf(countA[0]);
- // Add to the list for that count
- ArrayList list = tempMap.get(count);
- if (list == null) {
- // Create list
- list = new ArrayList();
- tempMap.put(count, list);
- }
- list.add(entry);
- }
- // Allocate array of the right size
- int size = ccStats.size();
- if (size > cacheSize) {
- size = cacheSize;
- }
- CharEntry[] tempccCache = new CharEntry[size];
- // Fill it up using an alphabetical order
- // and a dumb insert sort
- CharChunk tempChunk = new CharChunk();
- int n = 0;
- while (n < size) {
- Object key = tempMap.lastKey();
- ArrayList list = tempMap.get(key);
- for (int i = 0; i < list.size() && n < size; i++) {
- CharEntry entry = list.get(i);
- tempChunk.setChars(entry.name, 0,
- entry.name.length);
- int insertPos = findClosest(tempChunk,
- tempccCache, n);
- if (insertPos == n) {
- tempccCache[n + 1] = entry;
- } else {
- System.arraycopy(tempccCache, insertPos + 1,
- tempccCache, insertPos + 2,
- n - insertPos - 1);
- tempccCache[insertPos + 1] = entry;
- }
- n++;
- }
- tempMap.remove(key);
- }
- ccCount = 0;
- ccStats.clear();
- ccCache = tempccCache;
- if (log.isDebugEnabled()) {
- long t2 = System.currentTimeMillis();
- log.debug("CharCache generation time: " +
- (t2 - t1) + "ms");
- }
- } else {
- ccCount++;
- // Allocate new CharEntry for the lookup
- CharEntry entry = new CharEntry();
- entry.value = value;
- int[] count = ccStats.get(entry);
- if (count == null) {
- int end = cc.getEnd();
- int start = cc.getStart();
- // Create char array and copy chars
- entry.name = new char[cc.getLength()];
- System.arraycopy(cc.getBuffer(), start, entry.name,
- 0, end - start);
- // Initialize occurrence count to one
- count = new int[1];
- count[0] = 1;
- // Set in the stats hash map
- ccStats.put(entry, count);
- } else {
- count[0] = count[0] + 1;
- }
- }
- }
- }
- return value;
- } else {
- accessCount++;
- // Find the corresponding String
- String result = find(cc);
- if (result == null) {
- return cc.toStringInternal();
- }
- // Note: We don't care about safety for the stats
- hitCount++;
- return result;
- }
-
- }
-
-
- // ----------------------------------------------------- Protected Methods
-
-
- /**
- * Compare given byte chunk with byte array.
- * Return -1, 0 or +1 if inferior, equal, or superior to the String.
- */
- protected static final int compare(ByteChunk name, byte[] compareTo) {
- int result = 0;
-
- byte[] b = name.getBuffer();
- int start = name.getStart();
- int end = name.getEnd();
- int len = compareTo.length;
-
- if ((end - start) < len) {
- len = end - start;
- }
- for (int i = 0; (i < len) && (result == 0); i++) {
- if (b[i + start] > compareTo[i]) {
- result = 1;
- } else if (b[i + start] < compareTo[i]) {
- result = -1;
- }
- }
- if (result == 0) {
- if (compareTo.length > (end - start)) {
- result = -1;
- } else if (compareTo.length < (end - start)) {
- result = 1;
- }
- }
- return result;
- }
-
-
- /**
- * Find an entry given its name in the cache and return the associated
- * String.
- */
- protected static final String find(ByteChunk name) {
- int pos = findClosest(name, bcCache, bcCache.length);
- if ((pos < 0) || (compare(name, bcCache[pos].name) != 0)
- || !(name.getCharset().equals(bcCache[pos].charset))) {
- return null;
- } else {
- return bcCache[pos].value;
- }
- }
-
-
- /**
- * Find an entry given its name in a sorted array of map elements.
- * This will return the index for the closest inferior or equal item in the
- * given array.
- */
- protected static final int findClosest(ByteChunk name, ByteEntry[] array,
- int len) {
-
- int a = 0;
- int b = len - 1;
-
- // Special cases: -1 and 0
- if (b == -1) {
- return -1;
- }
-
- if (compare(name, array[0].name) < 0) {
- return -1;
- }
- if (b == 0) {
- return 0;
- }
-
- int i = 0;
- while (true) {
- i = (b + a) >>> 1;
- int result = compare(name, array[i].name);
- if (result == 1) {
- a = i;
- } else if (result == 0) {
- return i;
- } else {
- b = i;
- }
- if ((b - a) == 1) {
- int result2 = compare(name, array[b].name);
- if (result2 < 0) {
- return a;
- } else {
- return b;
- }
- }
- }
-
- }
-
-
- /**
- * Compare given char chunk with char array.
- * Return -1, 0 or +1 if inferior, equal, or superior to the String.
- */
- protected static final int compare(CharChunk name, char[] compareTo) {
- int result = 0;
-
- char[] c = name.getBuffer();
- int start = name.getStart();
- int end = name.getEnd();
- int len = compareTo.length;
-
- if ((end - start) < len) {
- len = end - start;
- }
- for (int i = 0; (i < len) && (result == 0); i++) {
- if (c[i + start] > compareTo[i]) {
- result = 1;
- } else if (c[i + start] < compareTo[i]) {
- result = -1;
- }
- }
- if (result == 0) {
- if (compareTo.length > (end - start)) {
- result = -1;
- } else if (compareTo.length < (end - start)) {
- result = 1;
- }
- }
- return result;
- }
-
-
- /**
- * Find an entry given its name in the cache and return the associated
- * String.
- */
- protected static final String find(CharChunk name) {
- int pos = findClosest(name, ccCache, ccCache.length);
- if ((pos < 0) || (compare(name, ccCache[pos].name) != 0)) {
- return null;
- } else {
- return ccCache[pos].value;
- }
- }
-
-
- /**
- * Find an entry given its name in a sorted array of map elements.
- * This will return the index for the closest inferior or equal item in the
- * given array.
- */
- protected static final int findClosest(CharChunk name, CharEntry[] array,
- int len) {
-
- int a = 0;
- int b = len - 1;
-
- // Special cases: -1 and 0
- if (b == -1) {
- return -1;
- }
-
- if (compare(name, array[0].name) < 0 ) {
- return -1;
- }
- if (b == 0) {
- return 0;
- }
-
- int i = 0;
- while (true) {
- i = (b + a) >>> 1;
- int result = compare(name, array[i].name);
- if (result == 1) {
- a = i;
- } else if (result == 0) {
- return i;
- } else {
- b = i;
- }
- if ((b - a) == 1) {
- int result2 = compare(name, array[b].name);
- if (result2 < 0) {
- return a;
- } else {
- return b;
- }
- }
- }
-
- }
-
-
- // -------------------------------------------------- ByteEntry Inner Class
-
-
- private static class ByteEntry {
-
- private byte[] name = null;
- private Charset charset = null;
- private String value = null;
-
- @Override
- public String toString() {
- return value;
- }
- @Override
- public int hashCode() {
- return value.hashCode();
- }
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof ByteEntry) {
- return value.equals(((ByteEntry) obj).value);
- }
- return false;
- }
-
- }
-
-
- // -------------------------------------------------- CharEntry Inner Class
-
-
- private static class CharEntry {
-
- private char[] name = null;
- private String value = null;
-
- @Override
- public String toString() {
- return value;
- }
- @Override
- public int hashCode() {
- return value.hashCode();
- }
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof CharEntry) {
- return value.equals(((CharEntry) obj).value);
- }
- return false;
- }
-
- }
-
-
-}
diff --git a/core/src/main/java/org/apache/struts2/util/tomcat/buf/UDecoder.java b/core/src/main/java/org/apache/struts2/util/tomcat/buf/UDecoder.java
deleted file mode 100644
index 1719986b2..000000000
--- a/core/src/main/java/org/apache/struts2/util/tomcat/buf/UDecoder.java
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.struts2.util.tomcat.buf;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-
-import java.io.CharConversionException;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-
-/**
- * All URL decoding happens here. This way we can reuse, review, optimize
- * without adding complexity to the buffers.
- *
- * The conversion will modify the original buffer.
- *
- * @author Costin Manolache
- */
-public final class UDecoder {
-
- private static final Logger log = LogManager.getLogger(UDecoder.class);
-
- public static final boolean ALLOW_ENCODED_SLASH =
- Boolean.parseBoolean(System.getProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "false"));
-
- private static class DecodeException extends CharConversionException {
- private static final long serialVersionUID = 1L;
- public DecodeException(String s) {
- super(s);
- }
-
- @Override
- public synchronized Throwable fillInStackTrace() {
- // This class does not provide a stack trace
- return this;
- }
- }
-
- /** Unexpected end of data. */
- private static final IOException EXCEPTION_EOF = new DecodeException("EOF");
-
- /** %xx with not-hex digit */
- private static final IOException EXCEPTION_NOT_HEX_DIGIT = new DecodeException(
- "isHexDigit");
-
- /** %-encoded slash is forbidden in resource path */
- private static final IOException EXCEPTION_SLASH = new DecodeException(
- "noSlash");
-
- public UDecoder()
- {
- }
-
- /** URLDecode, will modify the source.
- */
- public void convert( ByteChunk mb, boolean query )
- throws IOException
- {
- int start=mb.getOffset();
- byte buff[]=mb.getBytes();
- int end=mb.getEnd();
-
- int idx= ByteChunk.findByte( buff, start, end, (byte) '%' );
- int idx2=-1;
- if( query ) {
- idx2= ByteChunk.findByte( buff, start, (idx >= 0 ? idx : end), (byte) '+' );
- }
- if( idx<0 && idx2<0 ) {
- return;
- }
-
- // idx will be the smallest positive index ( first % or + )
- if( (idx2 >= 0 && idx2 < idx) || idx < 0 ) {
- idx=idx2;
- }
-
- final boolean noSlash = !(ALLOW_ENCODED_SLASH || query);
-
- for( int j=idx; j= end ) {
- throw EXCEPTION_EOF;
- }
- byte b1= buff[j+1];
- byte b2=buff[j+2];
- if( !isHexDigit( b1 ) || ! isHexDigit(b2 )) {
- throw EXCEPTION_NOT_HEX_DIGIT;
- }
-
- j+=2;
- int res=x2c( b1, b2 );
- if (noSlash && (res == '/')) {
- throw EXCEPTION_SLASH;
- }
- buff[idx]=(byte)res;
- }
- }
-
- mb.setEnd( idx );
-
- return;
- }
-
- // -------------------- Additional methods --------------------
- // XXX What do we do about charset ????
-
- /** In-buffer processing - the buffer will be modified
- */
- public void convert( CharChunk mb, boolean query )
- throws IOException
- {
- // log( "Converting a char chunk ");
- int start=mb.getOffset();
- char buff[]=mb.getBuffer();
- int cend=mb.getEnd();
-
- int idx= CharChunk.indexOf( buff, start, cend, '%' );
- int idx2=-1;
- if( query ) {
- idx2= CharChunk.indexOf( buff, start, (idx >= 0 ? idx : cend), '+' );
- }
- if( idx<0 && idx2<0 ) {
- return;
- }
-
- // idx will be the smallest positive index ( first % or + )
- if( (idx2 >= 0 && idx2 < idx) || idx < 0 ) {
- idx=idx2;
- }
-
- final boolean noSlash = !(ALLOW_ENCODED_SLASH || query);
-
- for( int j=idx; j= cend ) {
- // invalid
- throw EXCEPTION_EOF;
- }
- char b1= buff[j+1];
- char b2=buff[j+2];
- if( !isHexDigit( b1 ) || ! isHexDigit(b2 )) {
- throw EXCEPTION_NOT_HEX_DIGIT;
- }
-
- j+=2;
- int res=x2c( b1, b2 );
- if (noSlash && (res == '/')) {
- throw EXCEPTION_SLASH;
- }
- buff[idx]=(char)res;
- }
- }
- mb.setEnd( idx );
- }
-
- /** URLDecode, will modify the source
- */
- public void convert(MessageBytes mb, boolean query)
- throws IOException
- {
-
- switch (mb.getType()) {
- case MessageBytes.T_STR:
- String strValue=mb.toString();
- if( strValue==null ) {
- return;
- }
- try {
- mb.setString( convert( strValue, query ));
- } catch (RuntimeException ex) {
- throw new DecodeException(ex.getMessage());
- }
- break;
- case MessageBytes.T_CHARS:
- CharChunk charC=mb.getCharChunk();
- convert( charC, query );
- break;
- case MessageBytes.T_BYTES:
- ByteChunk bytesC=mb.getByteChunk();
- convert( bytesC, query );
- break;
- }
- }
-
- // XXX Old code, needs to be replaced !!!!
- //
- public final String convert(String str, boolean query)
- {
- if (str == null) {
- return null;
- }
-
- if( (!query || str.indexOf( '+' ) < 0) && str.indexOf( '%' ) < 0 ) {
- return str;
- }
-
- final boolean noSlash = !(ALLOW_ENCODED_SLASH || query);
-
- StringBuilder dec = new StringBuilder(); // decoded string output
- int strPos = 0;
- int strLen = str.length();
-
- dec.ensureCapacity(str.length());
- while (strPos < strLen) {
- int laPos; // lookahead position
-
- // look ahead to next URLencoded metacharacter, if any
- for (laPos = strPos; laPos < strLen; laPos++) {
- char laChar = str.charAt(laPos);
- if ((laChar == '+' && query) || (laChar == '%')) {
- break;
- }
- }
-
- // if there were non-metacharacters, copy them all as a block
- if (laPos > strPos) {
- dec.append(str.substring(strPos,laPos));
- strPos = laPos;
- }
-
- // shortcut out of here if we're at the end of the string
- if (strPos >= strLen) {
- break;
- }
-
- // process next metacharacter
- char metaChar = str.charAt(strPos);
- if (metaChar == '+') {
- dec.append(' ');
- strPos++;
- continue;
- } else if (metaChar == '%') {
- // We throw the original exception - the super will deal with
- // it
- // try {
- char res = (char) Integer.parseInt(
- str.substring(strPos + 1, strPos + 3), 16);
- if (noSlash && (res == '/')) {
- throw new IllegalArgumentException("noSlash");
- }
- dec.append(res);
- strPos += 3;
- }
- }
-
- return dec.toString();
- }
-
-
- /**
- * Decode and return the specified URL-encoded String.
- * When the byte array is converted to a string, the system default
- * character encoding is used... This may be different than some other
- * servers. It is assumed the string is not a query string.
- *
- * @param str The url-encoded string
- *
- * @exception IllegalArgumentException if a '%' character is not followed
- * by a valid 2-digit hexadecimal number
- */
- public static String URLDecode(String str) {
- return URLDecode(str, null);
- }
-
-
- /**
- * Decode and return the specified URL-encoded String. It is assumed the
- * string is not a query string.
- *
- * @param str The url-encoded string
- * @param enc The encoding to use; if null, the default encoding is used. If
- * an unsupported encoding is specified null will be returned
- * @exception IllegalArgumentException if a '%' character is not followed
- * by a valid 2-digit hexadecimal number
- */
- public static String URLDecode(String str, String enc) {
- return URLDecode(str, enc, false);
- }
-
-
- /**
- * Decode and return the specified URL-encoded String.
- *
- * @param str The url-encoded string
- * @param enc The encoding to use; if null, the default encoding is used. If
- * an unsupported encoding is specified null will be returned
- * @param isQuery Is this a query string being processed
- * @exception IllegalArgumentException if a '%' character is not followed
- * by a valid 2-digit hexadecimal number
- */
- public static String URLDecode(String str, String enc, boolean isQuery) {
- if (str == null)
- return (null);
-
- // use the specified encoding to extract bytes out of the
- // given string so that the encoding is not lost. If an
- // encoding is not specified, use ISO-8859-1
- byte[] bytes = null;
- try {
- if (enc == null) {
- bytes = str.getBytes("ISO-8859-1");
- } else {
- bytes = str.getBytes(B2CConverter.getCharset(enc));
- }
- } catch (UnsupportedEncodingException uee) {
- if (log.isDebugEnabled()) {
- log.debug("Unable to URL decode the specified input since the encoding "+ enc + " is not supported.", uee);
- }
- }
-
- return URLDecode(bytes, enc, isQuery);
-
- }
-
-
- /**
- * Decode and return the specified URL-encoded byte array.
- *
- * @param bytes The url-encoded byte array
- * @param enc The encoding to use; if null, the default encoding is used. If
- * an unsupported encoding is specified null will be returned
- * @param isQuery Is this a query string being processed
- * @exception IllegalArgumentException if a '%' character is not followed
- * by a valid 2-digit hexadecimal number
- */
- public static String URLDecode(byte[] bytes, String enc, boolean isQuery) {
-
- if (bytes == null)
- return null;
-
- int len = bytes.length;
- int ix = 0;
- int ox = 0;
- while (ix < len) {
- byte b = bytes[ix++]; // Get byte to test
- if (b == '+' && isQuery) {
- b = (byte)' ';
- } else if (b == '%') {
- if (ix + 2 > len) {
- throw new IllegalArgumentException(
- "The % character must be followed by two hexademical digits");
- }
- b = (byte) ((convertHexDigit(bytes[ix++]) << 4)
- + convertHexDigit(bytes[ix++]));
- }
- bytes[ox++] = b;
- }
- if (enc != null) {
- try {
- return new String(bytes, 0, ox, B2CConverter.getCharset(enc));
- } catch (UnsupportedEncodingException uee) {
- if (log.isDebugEnabled()) {
- log.debug("Unable to URL decode the specified input since the encoding " + enc + " is not supported.", uee);
- }
- return null;
- }
- }
- return new String(bytes, 0, ox);
-
- }
-
-
- private static byte convertHexDigit( byte b ) {
- if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
- if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
- if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
- throw new IllegalArgumentException(((char) b) + " is not a hexadecimal digit");
- }
-
-
- private static boolean isHexDigit( int c ) {
- return ( ( c>='0' && c<='9' ) ||
- ( c>='a' && c<='f' ) ||
- ( c>='A' && c<='F' ));
- }
-
-
- private static int x2c( byte b1, byte b2 ) {
- int digit= (b1>='A') ? ( (b1 & 0xDF)-'A') + 10 :
- (b1 -'0');
- digit*=16;
- digit +=(b2>='A') ? ( (b2 & 0xDF)-'A') + 10 :
- (b2 -'0');
- return digit;
- }
-
-
- private static int x2c( char b1, char b2 ) {
- int digit= (b1>='A') ? ( (b1 & 0xDF)-'A') + 10 :
- (b1 -'0');
- digit*=16;
- digit +=(b2>='A') ? ( (b2 & 0xDF)-'A') + 10 :
- (b2 -'0');
- return digit;
- }
-}
diff --git a/core/src/main/java/org/apache/struts2/util/tomcat/buf/Utf8Decoder.java b/core/src/main/java/org/apache/struts2/util/tomcat/buf/Utf8Decoder.java
deleted file mode 100644
index bca4de8d6..000000000
--- a/core/src/main/java/org/apache/struts2/util/tomcat/buf/Utf8Decoder.java
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.struts2.util.tomcat.buf;
-
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.CharsetDecoder;
-import java.nio.charset.CoderResult;
-
-/**
- * Decodes bytes to UTF-8. Extracted from Apache Harmony and modified to reject
- * code points from U+D800 to U+DFFF as per RFC3629. The standard Java decoder
- * does not reject these. It has also been modified to reject code points
- * greater than U+10FFFF which the standard Java decoder rejects but the harmony
- * one does not.
- */
-public class Utf8Decoder extends CharsetDecoder {
-
- // The next table contains information about UTF-8 charset and
- // correspondence of 1st byte to the length of sequence
- // For information please visit http://www.ietf.org/rfc/rfc3629.txt
- //
- // Please note, o means 0, actually.
- // -------------------------------------------------------------------
- // 0 1 2 3 Value
- // -------------------------------------------------------------------
- // oxxxxxxx 00000000 00000000 0xxxxxxx
- // 11oyyyyy 1oxxxxxx 00000000 00000yyy yyxxxxxx
- // 111ozzzz 1oyyyyyy 1oxxxxxx 00000000 zzzzyyyy yyxxxxxx
- // 1111ouuu 1ouuzzzz 1oyyyyyy 1oxxxxxx 000uuuuu zzzzyyyy yyxxxxxx
- private static final int remainingBytes[] = {
- // 1owwwwww
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- // 11oyyyyy
- -1, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- // 111ozzzz
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- // 1111ouuu
- 3, 3, 3, 3, 3, -1, -1, -1,
- // > 11110111
- -1, -1, -1, -1, -1, -1, -1, -1};
- private static final int remainingNumbers[] = {0, // 0 1 2 3
- 4224, // (01o00000b << 6)+(1o000000b)
- 401536, // (011o0000b << 12)+(1o000000b << 6)+(1o000000b)
- 29892736 // (0111o000b << 18)+(1o000000b << 12)+(1o000000b <<
- // 6)+(1o000000b)
- };
- private static final int lowerEncodingLimit[] = {-1, 0x80, 0x800, 0x10000};
-
-
- public Utf8Decoder() {
- super(B2CConverter.UTF_8, 1.0f, 1.0f);
- }
-
-
- @Override
- protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
- if (in.hasArray() && out.hasArray()) {
- return decodeHasArray(in, out);
- }
- return decodeNotHasArray(in, out);
- }
-
-
- private CoderResult decodeNotHasArray(ByteBuffer in, CharBuffer out) {
- int outRemaining = out.remaining();
- int pos = in.position();
- int limit = in.limit();
- try {
- while (pos < limit) {
- if (outRemaining == 0) {
- return CoderResult.OVERFLOW;
- }
- int jchar = in.get();
- if (jchar < 0) {
- jchar = jchar & 0x7F;
- int tail = remainingBytes[jchar];
- if (tail == -1) {
- return CoderResult.malformedForLength(1);
- }
- if (limit - pos < 1 + tail) {
- // No early test for invalid sequences here as peeking
- // at the next byte is harder
- return CoderResult.UNDERFLOW;
- }
- int nextByte;
- for (int i = 0; i < tail; i++) {
- nextByte = in.get() & 0xFF;
- if ((nextByte & 0xC0) != 0x80) {
- return CoderResult.malformedForLength(1 + i);
- }
- jchar = (jchar << 6) + nextByte;
- }
- jchar -= remainingNumbers[tail];
- if (jchar < lowerEncodingLimit[tail]) {
- // Should have been encoded in a fewer octets
- return CoderResult.malformedForLength(1);
- }
- pos += tail;
- }
- // Apache Tomcat added test
- if (jchar >= 0xD800 && jchar <= 0xDFFF) {
- return CoderResult.unmappableForLength(3);
- }
- // Apache Tomcat added test
- if (jchar > 0x10FFFF) {
- return CoderResult.unmappableForLength(4);
- }
- if (jchar <= 0xffff) {
- out.put((char) jchar);
- outRemaining--;
- } else {
- if (outRemaining < 2) {
- return CoderResult.OVERFLOW;
- }
- out.put((char) ((jchar >> 0xA) + 0xD7C0));
- out.put((char) ((jchar & 0x3FF) + 0xDC00));
- outRemaining -= 2;
- }
- pos++;
- }
- return CoderResult.UNDERFLOW;
- } finally {
- in.position(pos);
- }
- }
-
-
- private CoderResult decodeHasArray(ByteBuffer in, CharBuffer out) {
- int outRemaining = out.remaining();
- int pos = in.position();
- int limit = in.limit();
- final byte[] bArr = in.array();
- final char[] cArr = out.array();
- final int inIndexLimit = limit + in.arrayOffset();
- int inIndex = pos + in.arrayOffset();
- int outIndex = out.position() + out.arrayOffset();
- // if someone would change the limit in process,
- // he would face consequences
- for (; inIndex < inIndexLimit && outRemaining > 0; inIndex++) {
- int jchar = bArr[inIndex];
- if (jchar < 0) {
- jchar = jchar & 0x7F;
- // If first byte is invalid, tail will be set to -1
- int tail = remainingBytes[jchar];
- if (tail == -1) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(1);
- }
- // Additional checks to detect invalid sequences ASAP
- // Checks derived from Unicode 6.2, Chapter 3, Table 3-7
- // Check 2nd byte
- int tailAvailable = inIndexLimit - inIndex - 1;
- if (tailAvailable > 0) {
- // First byte C2..DF, second byte 80..BF
- if (jchar > 0x41 && jchar < 0x60 &&
- (bArr[inIndex + 1] & 0xC0) != 0x80) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(1);
- }
- // First byte E0, second byte A0..BF
- if (jchar == 0x60 && (bArr[inIndex + 1] & 0xE0) != 0xA0) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(1);
- }
- // First byte E1..EC, second byte 80..BF
- if (jchar > 0x60 && jchar < 0x6D &&
- (bArr[inIndex + 1] & 0xC0) != 0x80) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(1);
- }
- // First byte ED, second byte 80..9F
- if (jchar == 0x6D && (bArr[inIndex + 1] & 0xE0) != 0x80) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(1);
- }
- // First byte EE..EF, second byte 80..BF
- if (jchar > 0x6D && jchar < 0x70 &&
- (bArr[inIndex + 1] & 0xC0) != 0x80) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(1);
- }
- // First byte F0, second byte 90..BF
- if (jchar == 0x70 &&
- ((bArr[inIndex + 1] & 0xFF) < 0x90 ||
- (bArr[inIndex + 1] & 0xFF) > 0xBF)) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(1);
- }
- // First byte F1..F3, second byte 80..BF
- if (jchar > 0x70 && jchar < 0x74 &&
- (bArr[inIndex + 1] & 0xC0) != 0x80) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(1);
- }
- // First byte F4, second byte 80..8F
- if (jchar == 0x74 &&
- (bArr[inIndex + 1] & 0xF0) != 0x80) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(1);
- }
- }
- // Check third byte if present and expected
- if (tailAvailable > 1 && tail > 1) {
- if ((bArr[inIndex + 2] & 0xC0) != 0x80) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(2);
- }
- }
- // Check fourth byte if present and expected
- if (tailAvailable > 2 && tail > 2) {
- if ((bArr[inIndex + 3] & 0xC0) != 0x80) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(3);
- }
- }
- if (tailAvailable < tail) {
- break;
- }
- for (int i = 0; i < tail; i++) {
- int nextByte = bArr[inIndex + i + 1] & 0xFF;
- if ((nextByte & 0xC0) != 0x80) {
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(1 + i);
- }
- jchar = (jchar << 6) + nextByte;
- }
- jchar -= remainingNumbers[tail];
- if (jchar < lowerEncodingLimit[tail]) {
- // Should have been encoded in fewer octets
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return CoderResult.malformedForLength(1);
- }
- inIndex += tail;
- }
- // Apache Tomcat added test
- if (jchar >= 0xD800 && jchar <= 0xDFFF) {
- return CoderResult.unmappableForLength(3);
- }
- // Apache Tomcat added test
- if (jchar > 0x10FFFF) {
- return CoderResult.unmappableForLength(4);
- }
- if (jchar <= 0xffff) {
- cArr[outIndex++] = (char) jchar;
- outRemaining--;
- } else {
- if (outRemaining < 2) {
- return CoderResult.OVERFLOW;
- }
- cArr[outIndex++] = (char) ((jchar >> 0xA) + 0xD7C0);
- cArr[outIndex++] = (char) ((jchar & 0x3FF) + 0xDC00);
- outRemaining -= 2;
- }
- }
- in.position(inIndex - in.arrayOffset());
- out.position(outIndex - out.arrayOffset());
- return (outRemaining == 0 && inIndex < inIndexLimit) ?
- CoderResult.OVERFLOW :
- CoderResult.UNDERFLOW;
- }
-}
\ No newline at end of file
diff --git a/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java b/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java
index c27361177..9f383c611 100644
--- a/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java
+++ b/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java
@@ -18,8 +18,17 @@
*/
package org.apache.struts2.views.util;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
+import com.opensymphony.xwork2.inject.Inject;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.StringEscapeUtils;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.url.UrlDecoder;
+import org.apache.struts2.url.UrlEncoder;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
@@ -27,18 +36,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.text.StringEscapeUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.apache.struts2.StrutsConstants;
-import org.apache.struts2.util.URLDecoderUtil;
-
-import com.opensymphony.xwork2.inject.Inject;
-
/**
* Default implementation of UrlHelper
*/
@@ -49,16 +46,11 @@ public class DefaultUrlHelper implements UrlHelper {
public static final String HTTP_PROTOCOL = "http";
public static final String HTTPS_PROTOCOL = "https";
- private String encoding = "UTF-8";
private int httpPort = DEFAULT_HTTP_PORT;
private int httpsPort = DEFAULT_HTTPS_PORT;
- @Inject(StrutsConstants.STRUTS_I18N_ENCODING)
- public void setEncoding(String encoding) {
- if (StringUtils.isNotEmpty(encoding)) {
- this.encoding = encoding;
- }
- }
+ private UrlEncoder encoder;
+ private UrlDecoder decoder;
@Inject(StrutsConstants.STRUTS_URL_HTTP_PORT)
public void setHttpPort(String httpPort) {
@@ -70,6 +62,16 @@ public class DefaultUrlHelper implements UrlHelper {
this.httpsPort = Integer.parseInt(httpsPort);
}
+ @Inject
+ public void setEncoder(UrlEncoder encoder) {
+ this.encoder = encoder;
+ }
+
+ @Inject
+ public void setDecoder(UrlDecoder decoder) {
+ this.decoder = decoder;
+ }
+
public String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params) {
return buildUrl(action, request, response, params, null, true, true);
}
@@ -81,7 +83,7 @@ public class DefaultUrlHelper implements UrlHelper {
public String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme,
boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort) {
- return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, true);
+ return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, true);
}
public String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String urlScheme,
@@ -108,7 +110,7 @@ public class DefaultUrlHelper implements UrlHelper {
// If switching schemes, use the configured port for the particular scheme.
if (!scheme.equals(reqScheme)) {
appendPort(link, scheme, HTTP_PROTOCOL.equals(scheme) ? httpPort : httpsPort);
- // Else use the port from the current request.
+ // Else use the port from the current request.
} else {
appendPort(link, scheme, request.getServerPort());
}
@@ -143,7 +145,7 @@ public class DefaultUrlHelper implements UrlHelper {
uri = request.getRequestURI();
}
- link.append(uri.substring(0, uri.lastIndexOf('/') + 1));
+ link.append(uri, 0, uri.lastIndexOf('/') + 1);
}
// Add page
@@ -175,7 +177,7 @@ public class DefaultUrlHelper implements UrlHelper {
String result = link.toString();
- if (StringUtils.containsIgnoreCase(result, "