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