001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.betwixt.io.read; 019 020 import java.util.ArrayList; 021 import java.util.List; 022 023 import org.apache.commons.betwixt.ElementDescriptor; 024 import org.apache.commons.betwixt.expression.Context; 025 import org.apache.commons.betwixt.expression.Updater; 026 import org.xml.sax.Attributes; 027 028 /** 029 * <p>Acts to bind an array property. 030 * Note that this is intended to be used to map 031 * properties with a setter taking an array 032 * but which do not have an adder.</p> 033 * <p> 034 * <strong>Note</strong> this implementation has state 035 * and therefore cannot be used concurrently (in simultaneous readings). 036 * </p> 037 * @author <a href='http://commons.apache.org/'>Apache Commons Team</a> 038 * @version $Revision: 561314 $ 039 */ 040 public class ArrayBindAction extends MappingAction.Base { 041 042 043 /** 044 * Factory method creates implementations to map arrays. 045 * @param elementDescriptor <code>ElementDescriptor</code> to be mapped, 046 * not null 047 * @return <code>MappingAction</code>, not null 048 */ 049 public static final MappingAction createMappingAction(ElementDescriptor elementDescriptor) { 050 MappingAction result = new ArrayBindAction(); 051 if (elementDescriptor.getSingularPropertyType() != null && 052 !elementDescriptor.getSingularPropertyType().isArray()) { 053 result = BeanBindAction.INSTANCE; 054 } 055 return result; 056 } 057 058 private BeanMapping beanMapping = new BeanMapping(); 059 private Updater originalUpdater; 060 061 /** 062 * Mapping arrays requires the addition of a temporary object 063 * (an <code>ArrayList</code>) into the stack together with an 064 * updater for that object. 065 * 066 */ 067 public MappingAction begin( 068 String namespace, 069 String name, 070 Attributes attributes, 071 ReadContext context) 072 throws Exception { 073 // push an array onto the object stack 074 context.pushBean(new ArrayList()); 075 return this; 076 } 077 078 /** 079 * Pops the <code>ArrayList</code> and the updater from 080 * their stacks. The original updater is called with the 081 * result of the convertion. 082 */ 083 public void end(ReadContext context) throws Exception { 084 if (originalUpdater != null) { 085 // create an array of appropriate type 086 List values = (List) context.popBean(); 087 originalUpdater.update(context, values); 088 } 089 } 090 091 /** Construct a delegating implmentation that wraps the real bean creator */ 092 public MappingAction next( 093 String namespace, 094 String name, 095 Attributes attributes, 096 ReadContext context) 097 throws Exception { 098 originalUpdater = context.getCurrentUpdater(); 099 MappingAction nextBindAction = BeanBindAction.INSTANCE; 100 beanMapping.setDelegate(nextBindAction); 101 return beanMapping; 102 } 103 104 105 106 /** Updates a list by adding the new value */ 107 private static class ListUpdater implements Updater { 108 /** Singleton */ 109 private static final ListUpdater INSTANCE = new ListUpdater(); 110 111 /** Update by adding the new value to the list */ 112 public void update(Context context, Object newValue) { 113 List values = (List) context.getBean(); 114 values.add(newValue); 115 } 116 117 } 118 119 private static class BeanMapping extends MappingAction.Base { 120 private MappingAction delegate; 121 122 BeanMapping() {} 123 124 125 /** 126 * Gets the action to which the bean binding is delegated. 127 * @return <code>MappingAction</code> delegate, not null 128 */ 129 MappingAction getDelegate() { 130 return delegate; 131 } 132 133 /** 134 * Sets the action to which the bean binding is delegated. 135 * @param action< code>MappingAction</code> delegate, not null 136 */ 137 void setDelegate(MappingAction action) { 138 delegate = action; 139 } 140 141 /** Push updater and then delegate */ 142 public MappingAction begin( 143 String namespace, 144 String name, 145 Attributes attributes, 146 ReadContext context) 147 throws Exception { 148 context.pushUpdater(ListUpdater.INSTANCE); 149 delegate = delegate.begin(namespace, name, attributes, context); 150 return this; 151 } 152 153 /** Delegate to delegate (Doh!) */ 154 public void body(String text, ReadContext context) throws Exception { 155 delegate.body(text, context); 156 } 157 158 /** Call delegate then pop <code>Updater</code> */ 159 public void end(ReadContext context) throws Exception { 160 delegate.end(context); 161 Updater updater = context.popUpdater(); 162 } 163 164 /** Use delegate to create next action */ 165 public MappingAction next( 166 String namespace, 167 String name, 168 Attributes attributes, 169 ReadContext context) 170 throws Exception { 171 return delegate.next(namespace, name, attributes, context); 172 } 173 174 175 } 176 }