001    package org.apache.ojb.broker.ant;
002    
003    /* Copyright 2004-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * 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    import java.io.IOException;
019    import java.io.Reader;
020    import java.io.StringReader;
021    import java.io.Writer;
022    import java.util.Iterator;
023    import java.util.List;
024    
025    import org.apache.commons.beanutils.DynaBean;
026    import org.apache.commons.digester.Digester;
027    import org.apache.commons.digester.ExtendedBaseRules;
028    import org.apache.commons.digester.Rule;
029    import org.apache.commons.digester.RuleSetBase;
030    import org.apache.ddlutils.Platform;
031    import org.apache.ddlutils.model.Column;
032    import org.apache.ddlutils.model.Database;
033    import org.apache.ojb.broker.metadata.ClassDescriptor;
034    import org.apache.ojb.broker.metadata.DescriptorRepository;
035    import org.xml.sax.Attributes;
036    import org.xml.sax.EntityResolver;
037    import org.xml.sax.InputSource;
038    
039    /**
040     * Provides data input and output via DdlUtils.
041     * 
042     * @author Thomas Dudziak
043     */
044    public class DdlUtilsDataHandling
045    {
046        private class DynaFactoryCreateRule extends Rule
047        {
048            /**
049             * {@inheritDoc}
050             */
051            public void begin(String namespace, String name, Attributes attributes) throws Exception
052            {
053                DynaBean bean = _preparedModel.createBeanFor(name);
054    
055                if (bean == null)
056                {
057                    throw new DataTaskException("Unknown element "+name);
058                }
059    
060                for (int idx = 0; idx < attributes.getLength(); idx++)
061                {
062                    String attrName  = attributes.getLocalName(idx);
063                    String attrValue = attributes.getValue(idx);
064                    Column column    = _preparedModel.getColumnFor(name, attrName);
065    
066                    if (column == null)
067                    {
068                        throw new DataTaskException("Unknown attribute "+attrName+" of element "+name);
069                    }
070                    bean.set(column.getName(), attrValue);
071                }
072                DdlUtilsDataHandling.this._digester.push(bean);
073            }
074    
075            /**
076             * {@inheritDoc}
077             */
078            public void end(String namespace, String name) throws Exception
079            {
080                DynaBean bean = (DynaBean)DdlUtilsDataHandling.this._digester.pop();
081    
082                ((DataSet)DdlUtilsDataHandling.this._digester.peek()).add(bean);
083            }
084        }
085    
086        public class DataRuleSet extends RuleSetBase
087        {
088            /**
089             * {@inheritDoc}
090             */
091            public void addRuleInstances(Digester digester)
092            {
093                digester.addObjectCreate("dataset", DataSet.class);
094                digester.addRule("*/dataset/*", new DynaFactoryCreateRule());
095            }
096        }
097    
098        /** The database model */
099        private Database _dbModel;
100        /** The platform */
101        private Platform _platform;
102        /** The prepared model */
103        private PreparedModel _preparedModel;
104        /** The digester for parsing the XML */
105        private Digester _digester;
106    
107        /**
108         * Creates a new data handling object.
109         */
110        public DdlUtilsDataHandling()
111        {
112            _digester = new Digester();
113            _digester.setEntityResolver(new EntityResolver() {
114                public InputSource resolveEntity(String publicId, String systemId)
115                {
116                    // we don't care about the DTD for data files
117                    return new InputSource(new StringReader(""));
118                }
119    
120            });
121            _digester.setNamespaceAware(true);
122            _digester.setValidating(false);
123            _digester.setUseContextClassLoader(true);
124            _digester.setRules(new ExtendedBaseRules());
125            _digester.addRuleSet(new DataRuleSet());
126        }
127    
128        /**
129         * Sets the model that the handling works on.
130         * 
131         * @param databaseModel The database model
132         * @param objModel      The object model
133         */
134        public void setModel(Database databaseModel, DescriptorRepository objModel)
135        {
136            _dbModel       = databaseModel;
137            _preparedModel = new PreparedModel(objModel, databaseModel);
138        }
139    
140        /**
141         * Sets the (connected) database platform to work against.
142         * 
143         * @param platform The platform
144         */
145        public void setPlatform(Platform platform)
146        {
147            _platform = platform;
148        }
149    
150        /**
151         * Writes a DTD that can be used for data XML files matching the current model to the given writer. 
152         * 
153         * @param output The writer to write the DTD to
154         */
155        public void getDataDTD(Writer output) throws DataTaskException
156        {
157            try
158            {
159                output.write("<!ELEMENT dataset (\n");
160                for (Iterator it = _preparedModel.getElementNames(); it.hasNext();)
161                {
162                    String elementName = (String)it.next();
163    
164                    output.write("    ");
165                    output.write(elementName);
166                    output.write("*");
167                    output.write(it.hasNext() ? " |\n" : "\n");
168                }
169                output.write(")>\n<!ATTLIST dataset\n    name CDATA #REQUIRED\n>\n");
170                for (Iterator it = _preparedModel.getElementNames(); it.hasNext();)
171                {
172                    String elementName = (String)it.next();
173                    List   classDescs  = _preparedModel.getClassDescriptorsMappingTo(elementName);
174    
175                    if (classDescs == null)
176                    {
177                        output.write("\n<!-- Indirection table");
178                    }
179                    else
180                    {
181                        output.write("\n<!-- Mapped to : ");
182                        for (Iterator classDescIt = classDescs.iterator(); classDescIt.hasNext();)
183                        {
184                            ClassDescriptor classDesc = (ClassDescriptor)classDescIt.next();
185        
186                            output.write(classDesc.getClassNameOfObject());
187                            if (classDescIt.hasNext())
188                            {
189                                output.write("\n                 ");
190                            }
191                        }
192                    }
193                    output.write(" -->\n<!ELEMENT ");
194                    output.write(elementName);
195                    output.write(" EMPTY>\n<!ATTLIST ");
196                    output.write(elementName);
197                    output.write("\n");
198    
199                    for (Iterator attrIt = _preparedModel.getAttributeNames(elementName); attrIt.hasNext();)
200                    {
201                        String attrName = (String)attrIt.next();
202    
203                        output.write("    ");
204                        output.write(attrName);
205                        output.write(" CDATA #");
206                        output.write(_preparedModel.isRequired(elementName, attrName) ? "REQUIRED" : "IMPLIED");
207                        output.write("\n");
208                    }
209                    output.write(">\n");
210                }
211            }
212            catch (IOException ex)
213            {
214                throw new DataTaskException(ex);
215            }
216        }
217    
218        /**
219         * Returns the sql necessary to add the data XML contained in the given input stream. 
220         * Note that the data is expected to match the repository metadata (not the table schema).
221         * Also note that you should not use the reader after passing it to this method except closing
222         * it (which is not done automatically).
223         * 
224         * @param input  A reader returning the content of the data file
225         * @param output The writer to write the sql to
226         */
227        public void getInsertDataSql(Reader input, Writer output) throws DataTaskException
228        {
229            try
230            {
231                DataSet set = (DataSet)_digester.parse(input);
232    
233                set.createInsertionSql(_dbModel, _platform, output);
234            }
235            catch (Exception ex)
236            {
237                if (ex instanceof DataTaskException)
238                {
239                    // is not declared by digester, but may be thrown
240                    throw (DataTaskException)ex;
241                }
242                else
243                {
244                    throw new DataTaskException(ex);
245                }
246            }
247        }
248    
249        /**
250         * Returns the sql necessary to add the data XML contained in the given input stream. 
251         * Note that the data is expected to match the repository metadata (not the table schema).
252         * Also note that you should not use the reader after passing it to this method except closing
253         * it (which is not done automatically).
254         * 
255         * @param input     A reader returning the content of the data file
256         * @param batchSize The batch size; use 1 for not batch insertion
257         */
258        public void insertData(Reader input, int batchSize) throws DataTaskException
259        {
260            try
261            {
262                DataSet set = (DataSet)_digester.parse(input);
263                
264                set.insert(_platform, _dbModel, batchSize);
265            }
266            catch (Exception ex)
267            {
268                if (ex instanceof DataTaskException)
269                {
270                    // is not declared by digester, but may be thrown
271                    throw (DataTaskException)ex;
272                }
273                else
274                {
275                    throw new DataTaskException(ex);
276                }
277            }
278        }
279    }