001    package railo.runtime.text.feed;
002    
003    import java.io.IOException;
004    import java.io.InputStream;
005    import java.util.HashMap;
006    import java.util.Iterator;
007    import java.util.Map;
008    import java.util.Map.Entry;
009    import java.util.Stack;
010    
011    import org.xml.sax.Attributes;
012    import org.xml.sax.InputSource;
013    import org.xml.sax.Locator;
014    import org.xml.sax.SAXException;
015    import org.xml.sax.XMLReader;
016    import org.xml.sax.ext.Locator2;
017    import org.xml.sax.helpers.DefaultHandler;
018    import org.xml.sax.helpers.XMLReaderFactory;
019    
020    import railo.commons.io.IOUtil;
021    import railo.commons.io.SystemUtil;
022    import railo.commons.io.res.Resource;
023    import railo.commons.lang.StringUtil;
024    import railo.runtime.op.Caster;
025    import railo.runtime.type.Array;
026    import railo.runtime.type.ArrayImpl;
027    import railo.runtime.type.CastableArray;
028    import railo.runtime.type.Collection;
029    import railo.runtime.type.Collection.Key;
030    import railo.runtime.type.KeyImpl;
031    import railo.runtime.type.Struct;
032    import railo.runtime.type.StructImpl;
033    
034    public final class FeedHandler extends DefaultHandler {
035    
036            public final static String DEFAULT_SAX_PARSER="org.apache.xerces.parsers.SAXParser";
037    
038            
039            private XMLReader xmlReader;
040    
041            //private StringBuffer content=new StringBuffer();
042    
043            private int deep=0;
044            private FeedStruct data;
045            private String path=""; 
046            private Collection.Key inside;
047            private Stack<FeedStruct> parents=new Stack<FeedStruct>();
048            private FeedDeclaration decl;
049            private Map<String,String> root=new HashMap<String,String>();
050            private boolean hasDC;
051            private boolean isAtom;
052    
053            private boolean inAuthor;
054    
055            private boolean inEntry;
056            
057            
058            /**
059             * Constructor of the class
060             * @param res
061             * @throws IOException
062             * @throws SAXException 
063             */
064            public FeedHandler(Resource res) throws IOException, SAXException {
065                    InputStream is=null;
066                    try {
067                            InputSource source=new InputSource(is=res.getInputStream());
068                            source.setSystemId(res.getPath());
069                            
070                            init(DEFAULT_SAX_PARSER,source);
071                    } 
072                    finally {
073                            IOUtil.closeEL(is);
074                    }
075            }
076            public FeedHandler(InputSource is) throws IOException, SAXException {
077                    init(DEFAULT_SAX_PARSER,is);
078                    
079            }
080    
081            /**
082             * Constructor of the class
083             * @param stream
084             * @throws IOException
085             * @throws SAXException 
086             */
087            public FeedHandler(InputStream stream) throws IOException, SAXException {
088                    InputSource is=new InputSource(IOUtil.getReader(stream, SystemUtil.getCharset()));
089                    init(DEFAULT_SAX_PARSER,is);
090            }
091            
092            private void init(String saxParser,InputSource is) throws SAXException, IOException     {
093                    //print.out("is:"+is);
094                    hasDC=false;
095                    data=new FeedStruct();
096                    xmlReader=XMLReaderFactory.createXMLReader(saxParser);
097                    xmlReader.setContentHandler(this);
098                    xmlReader.setErrorHandler(this);
099                    xmlReader.setDTDHandler(new DummyDTDHandler());
100                    xmlReader.parse(is);
101        }
102            
103            /**
104             * @return the hasDC
105             */
106            public boolean hasDC() {
107                    return hasDC;
108            }
109            public void setDocumentLocator(Locator locator) { 
110                    if (locator instanceof Locator2) {
111                            Locator2 locator2 = (Locator2) locator;
112                            root.put("encoding", locator2.getEncoding());
113                    } 
114            }
115    
116            /**
117             * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
118             */
119            public void startElement(String uri, String name, String qName, Attributes atts) {
120                    deep++;
121                    
122                    
123                    if("entry".equals(name))inEntry=true;
124                    else if("author".equals(name))inAuthor=true;
125                    
126                    if(qName.startsWith("dc:")){
127                            name="dc_"+name;
128                            hasDC=true;
129                    }
130                    //print.o("iniside("+deep+"):"+name+"->"+uri);
131                    
132                    
133                    inside = KeyImpl.getInstance(name);
134                    if(StringUtil.isEmpty(path))path=name;
135                    else {
136                            path+="."+name;
137                    }
138                    if(decl==null){
139                            String decName=name;
140                            String version = atts.getValue("version");
141                            if("feed".equals(decName)) {
142                                    if(!StringUtil.isEmpty(version))decName="atom_"+version;
143                                    else decName="atom_1.0";
144                            }
145                            else {
146                                    if(!StringUtil.isEmpty(version))decName+="_"+version;
147                            }
148                            decl=FeedDeclaration.getInstance(decName);
149                            root.put("version",decName);
150                            isAtom=decl.getType().equals("atom");
151                            
152                    }
153                    
154                    
155                    FeedStruct sct=new FeedStruct(path,inside,uri);
156                    
157                    // attributes
158                    Map<String,String> attrs = getAttributes(atts, path);
159                    if(attrs!=null){
160                            Entry<String, String>  entry;
161                            Iterator<Entry<String, String>> it = attrs.entrySet().iterator();
162                            sct.setHasAttribute(true);
163                            while(it.hasNext()){
164                                    entry = it.next();
165                                    sct.setEL(entry.getKey(), entry.getValue());
166                            }
167                    }
168                    
169                    // assign
170                    if(!isAtom || deep<4) {
171                            Object obj = data.get(inside, null);
172                            if(obj instanceof Array)        {
173                                    ((Array)obj).appendEL(sct);
174                            }
175                            else if(obj instanceof FeedStruct){
176                                    Array arr = new ArrayImpl();
177                                    arr.appendEL(obj);
178                                    arr.appendEL(sct);
179                                    data.setEL(inside, arr);
180                            }
181                            else if(obj instanceof String){
182                                    // wenn wert schon existiert wird castableArray in setContent erstellt
183                            }
184                            else {
185                                    El el=(El) decl.getDeclaration().get(path);
186                                    if(el!=null && (el.getQuantity()==El.QUANTITY_0_N || el.getQuantity()==El.QUANTITY_1_N)){
187                                            Array arr = new ArrayImpl();
188                                            arr.appendEL(sct);
189                                            data.setEL(inside, arr);
190                                    }
191                                    else data.setEL(inside, sct);
192                                    
193                            }
194                    }
195                    parents.add(data);
196                    data=sct;
197                    
198                    //<enclosure url="http://www.scripting.com/mp3s/weatherReportDicksPicsVol7.mp3" length="6182912" type="audio/mpeg"/>
199            }
200            
201    
202            public void endElement(String uri, String name, String qName) {
203                    if("entry".equals(name))inEntry=false;
204                    else if("author".equals(name))inAuthor=false;
205                    deep--;
206                    if(isAtom && deep>=(inEntry && inAuthor?4:3)) {
207                            String content = data.getString();
208                            Key[] keys = data.keys();
209                            StringBuilder sb=new StringBuilder();
210                            sb.append("<");
211                            sb.append(qName);
212                            
213                            // xmlns
214                            if(!parents.peek().getUri().equals(uri)) {
215                                    sb.append(" xmlns=\"");
216                                    sb.append(uri);
217                                    sb.append("\"");
218                            }
219                            
220                            for(int i=0;i<keys.length;i++){
221                                    sb.append(" ");
222                                    sb.append(keys[i].getString());
223                                    sb.append("=\"");
224                                    sb.append(Caster.toString(data.get(keys[i],""),""));
225                                    sb.append("\"");
226                                    
227                            }
228                            
229                            
230                            if(!StringUtil.isEmpty(content)) {
231                                    sb.append(">");
232                                    sb.append(content);
233                                    sb.append("</"+qName+">");
234                            }
235                            else sb.append("/>");
236                            
237                            
238                            
239                            
240                            
241                            
242                            data= parents.pop();
243                            data.append(sb.toString().trim());
244                            //setContent(sb.toString().trim());
245                            
246                            path=data.getPath();
247                            inside=data.getInside();
248                            return;
249                    }
250                    
251                    
252                    
253                    //setContent(content.toString().trim());
254                    setContent(data.getString().trim());
255                    
256                    
257                    
258                    data=(FeedStruct) parents.pop();
259                    path=data.getPath();
260                    inside=data.getInside();        
261            }
262            
263            public void characters (char ch[], int start, int length)       {
264                    data.append(new String(ch,start,length));
265                    //content.append(new String(ch,start,length));
266            }
267            
268            private void setContent(String value)   {
269                    //print.out(path+":"+inside);
270                    if(StringUtil.isEmpty(inside)) return;
271                            
272                            if(data.hasAttribute()) {
273                                    if(!StringUtil.isEmpty(value))setEl(data,KeyImpl.VALUE,value);
274                            }
275                            else {
276                                    FeedStruct parent=(FeedStruct) parents.peek();
277                                    setEl(parent,inside,value);
278                            }
279                                    
280        }
281    
282            private void setEl(Struct sct, Collection.Key key, String value) {
283                    Object existing = sct.get(key,null);
284                    
285                    if(existing instanceof CastableArray){
286                            ((CastableArray)existing).appendEL(value);
287                    }
288                    else if(existing instanceof String){
289                            CastableArray ca=new CastableArray(existing);
290                            ca.appendEL(existing);
291                            ca.appendEL(value);
292                            sct.setEL(key,ca);
293                    }
294                    else 
295                            sct.setEL(key,Caster.toString(value));
296                    
297                    
298                    /*if(existing instanceof Struct)sct.setEL(key,value);
299                    else if(existing instanceof Array)((Array)existing).appendEL(value);
300                    else if(existing!=null){
301                            CastableArray ca=new CastableArray(existing);
302                            ca.appendEL(existing);
303                            ca.appendEL(value);
304                            sct.setEL(key,ca);
305                    }
306                    else*/
307            }
308            
309            private Map<String,String> getAttributes(Attributes attrs, String path) {
310                    El el = (El) decl.getDeclaration().get(path);
311                    
312                    int len=attrs.getLength();
313                    if((el==null || el.getAttrs()==null) && len==0) return null;
314                    
315                    Map<String,String> map=new HashMap<String,String>();
316                    if(el!=null) {
317                            Attr[] defaults = el.getAttrs();
318                            if(defaults!=null) {
319                                    for(int i=0;i<defaults.length;i++) {
320                                            if(defaults[i].hasDefaultValue())
321                                                    map.put(defaults[i].getName(), defaults[i].getDefaultValue());
322                                    }
323                            }
324                    }
325                    for(int i=0;i<len;i++) {
326                            map.put(attrs.getQName(i), attrs.getValue(i));
327                    }
328                    return map;
329            }
330            
331    
332            /**
333             * @return the properties
334             */
335            public Struct getData() {
336                    return data;
337            }
338            
339            
340            
341    
342            /**
343             * @see org.xml.sax.helpers.DefaultHandler#endDocument()
344             */
345            public void endDocument() throws SAXException {
346                    super.endDocument();
347                    Struct def=new StructImpl();
348                    Key[] entryLevel = decl.getEntryLevel();
349                    for(int i=0;i<entryLevel.length;i++) {
350                            data=(FeedStruct) data.get(entryLevel[i],def);
351                    }
352                    data.putAll(root);
353            }
354            
355    
356    }