1. /*
  2. * Copyright 2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.betwixt.io.read;
  17. import java.util.ArrayList;
  18. import java.util.List;
  19. import org.apache.commons.betwixt.ElementDescriptor;
  20. import org.apache.commons.betwixt.expression.Context;
  21. import org.apache.commons.betwixt.expression.Updater;
  22. import org.xml.sax.Attributes;
  23. /**
  24. * <p>Acts to bind an array property.
  25. * Note that this is intended to be used to map
  26. * properties with a setter taking an array
  27. * but which do not have an adder.</p>
  28. * <p>
  29. * <strong>Note</strong> this implementation has state
  30. * and therefore cannot be used concurrently (in simultaneous readings).
  31. * </p>
  32. * @author <a href='http://jakarta.apache.org/'>Jakarta Commons Team</a>
  33. * @version $Revision: 1.2 $
  34. */
  35. public class ArrayBindAction extends MappingAction.Base {
  36. /**
  37. * Factory method creates implementations to map arrays.
  38. * @param elementDescriptor <code>ElementDescriptor</code> to be mapped,
  39. * not null
  40. * @return <code>MappingAction</code>, not null
  41. */
  42. public static final MappingAction createMappingAction(ElementDescriptor elementDescriptor) {
  43. MappingAction result = new ArrayBindAction();
  44. if (elementDescriptor.getSingularPropertyType() != null &&
  45. !elementDescriptor.getSingularPropertyType().isArray()) {
  46. result = BeanBindAction.INSTANCE;
  47. }
  48. return result;
  49. }
  50. private BeanMapping beanMapping = new BeanMapping();
  51. private Updater originalUpdater;
  52. /**
  53. * Mapping arrays requires the addition of a temporary object
  54. * (an <code>ArrayList</code>) into the stack together with an
  55. * updater for that object.
  56. *
  57. */
  58. public MappingAction begin(
  59. String namespace,
  60. String name,
  61. Attributes attributes,
  62. ReadContext context)
  63. throws Exception {
  64. // push an array onto the object stack
  65. context.pushBean(new ArrayList());
  66. return this;
  67. }
  68. /**
  69. * Pops the <code>ArrayList</code> and the updater from
  70. * their stacks. The original updater is called with the
  71. * result of the convertion.
  72. */
  73. public void end(ReadContext context) throws Exception {
  74. if (originalUpdater != null) {
  75. // create an array of appropriate type
  76. List values = (List) context.popBean();
  77. originalUpdater.update(context, values);
  78. }
  79. }
  80. /** Construct a delegating implmentation that wraps the real bean creator */
  81. public MappingAction next(
  82. String namespace,
  83. String name,
  84. Attributes attributes,
  85. ReadContext context)
  86. throws Exception {
  87. originalUpdater = context.getCurrentUpdater();
  88. MappingAction nextBindAction = BeanBindAction.INSTANCE;
  89. beanMapping.setDelegate(nextBindAction);
  90. return beanMapping;
  91. }
  92. /** Updates a list by adding the new value */
  93. private static class ListUpdater implements Updater {
  94. /** Singleton */
  95. private static final ListUpdater INSTANCE = new ListUpdater();
  96. /** Update by adding the new value to the list */
  97. public void update(Context context, Object newValue) {
  98. List values = (List) context.getBean();
  99. values.add(newValue);
  100. }
  101. }
  102. private static class BeanMapping extends MappingAction.Base {
  103. private MappingAction delegate;
  104. BeanMapping() {}
  105. /**
  106. * Gets the action to which the bean binding is delegated.
  107. * @return <code>MappingAction</code> delegate, not null
  108. */
  109. MappingAction getDelegate() {
  110. return delegate;
  111. }
  112. /**
  113. * Sets the action to which the bean binding is delegated.
  114. * @param action< code>MappingAction</code> delegate, not null
  115. */
  116. void setDelegate(MappingAction action) {
  117. delegate = action;
  118. }
  119. /** Push updater and then delegate */
  120. public MappingAction begin(
  121. String namespace,
  122. String name,
  123. Attributes attributes,
  124. ReadContext context)
  125. throws Exception {
  126. context.pushUpdater(ListUpdater.INSTANCE);
  127. delegate = delegate.begin(namespace, name, attributes, context);
  128. return this;
  129. }
  130. /** Delegate to delegate (Doh!) */
  131. public void body(String text, ReadContext context) throws Exception {
  132. delegate.body(text, context);
  133. }
  134. /** Call delegate then pop <code>Updater</code> */
  135. public void end(ReadContext context) throws Exception {
  136. delegate.end(context);
  137. Updater updater = context.popUpdater();
  138. }
  139. /** Use delegate to create next action */
  140. public MappingAction next(
  141. String namespace,
  142. String name,
  143. Attributes attributes,
  144. ReadContext context)
  145. throws Exception {
  146. return delegate.next(namespace, name, attributes, context);
  147. }
  148. }
  149. }