1    	package acl2s.lib.session;
2    	
3    	import java.io.File;
4    	import java.io.FileInputStream;
5    	import java.io.FileWriter;
6    	import java.io.IOException;
7    	import java.util.ArrayList;
8    	
9    	import javax.xml.parsers.ParserConfigurationException;
10   	import javax.xml.parsers.SAXParserFactory;
11   	
12   	import org.peterd.util.io.StreamCopier;
13   	import org.xml.sax.Attributes;
14   	import org.xml.sax.SAXException;
15   	import org.xml.sax.helpers.DefaultHandler;
16   	
17   	import acl2s.lib.parse.IParseContext;
18   	import acl2s.lib.parse.ParseException;
19   	import acl2s.lib.parse.StringParser;
20   	import acl2s.lib.parse.obj.Cons;
21   	import acl2s.lib.parse.obj.LispObject;
22   	import acl2s.lib.parse.obj.Sym;
23   	import acl2s.lib_driver.IAcl2sLibErrorHandler;
24   	
25   	public class ModeDir {
26   		protected final Sym includeBookDirSym;
27   		protected final File home;
28   		protected final Sym[] deps;
29   		protected final File[] certOrder;
30   		protected final SessionMode[] modes;
31   		protected final String addIncludeBookDirString;
32   		
33   		ModeDir(File home, Sym includeBookDirSym, Sym[] deps, File[] certOrder, SessionMode[] modes) {
34   			if (certOrder == null) certOrder = new File[] {};
35   			this.certOrder = certOrder;
36   			if (deps == null) deps = new Sym[] {};
37   			this.deps = deps;
38   			this.home = home;
39   			this.includeBookDirSym = includeBookDirSym;
40   			if (modes == null) modes = new SessionMode[] {};
41   			this.modes = modes;
42   			this.addIncludeBookDirString =
43   				includeBookDirSym == null ? "" :
44   					"(acl2::add-include-book-dir " + includeBookDirSym.toString() +
45   					" \"" + MyBaseConfig.toAcl2PathDir(home) + "\")\n";
46   		}
47   	
48   		public Sym getIncludeBookDirSym() {
49   			return includeBookDirSym;
50   		}
51   	
52   		public File getHome() {
53   			return home;
54   		}
55   	
56   		public Sym[] getDeps() {
57   			return deps;
58   		}
59   	
60   		public File[] getCertOrder() {
61   			return certOrder;
62   		}
63   	
64   		public SessionMode[] getModes() {
65   			return modes;
66   		}
67   	
68   		public String getAddIncludeBookDirString() {
69   			return addIncludeBookDirString;
70   		}
71   		
72   		
73   		void maybeWriteDirLsp() {
74   			File dirLsp = new File(home, "dir.lsp");
75   			if (!dirLsp.exists() || dirLsp.canWrite()) {
76   				try {
Event do_not_call: "java.io.FileWriter(java.io.File arg1)" implicitly uses the environment's default character set, which might lead to unexpected behavior. Consider using an OutputStreamWriter with a FileOutputStream and an explicit character set.
77   					FileWriter w = new FileWriter(dirLsp);
78   					w.append(addIncludeBookDirString);
79   					w.close();
80   				} catch (IOException e) {
81   					// ignore
82   				}
83   			}
84   		}
85   		
86   		static final IParseContext bootstrappc = BootstrapParseContext.instance;
87   		static final IParseContext keywordpc = BootstrapParseContext.keywordParseInstance;
88   	
89   		static class ModeDirParser extends DefaultHandler {
90   			protected IAcl2sLibErrorHandler err;
91   			protected File dir;
92   			protected String msgPrefix;
93   			
94   			ModeDirParser(IAcl2sLibErrorHandler err, File dir) {
95   				this.err = err;
96   				this.dir = dir;
97   				this.msgPrefix = "While parsing plugin.xml for mode dir " + dir.getAbsolutePath() + ":\n";
98   			}
99   			
100  			protected Sym includeBookDir;
101  			protected final ArrayList<Sym> deps = new ArrayList<Sym>();
102  			protected final ArrayList<File> order = new ArrayList<File>();
103  			protected final ArrayList<SessionMode> modes = new ArrayList<SessionMode>();
104  			
105  			boolean inExtension = false;
106  	
107  			@Override
108  			public void startElement(String uri, String localName, String name, Attributes attributes)
109  			throws SAXException {
110  				if (inExtension) {
111  					if (name.equals("include-book-dir-entry")) {
112  						int len = attributes.getLength();
113  						for (int i = 0; i < len; i++) {
114  							if (attributes.getQName(i).equals("keyword")) {
115  								String val = attributes.getValue(i);
116  								try {
117  									includeBookDir = Sym.parse(new StringParser(keywordpc, val));
118  									if (!includeBookDir.isKeyword()) {
119  										err.error("include-book-dir-entry must be a keyword symbol", null);
120  										includeBookDir = null;
121  									}
122  								} catch (ParseException e) {
123  									err.error("Malformed include-book-dir-entry", e);
124  								}
125  							}
126  						}
127  						return;
128  					}
129  					if (name.equals("certify-order-element")) {
130  						int len = attributes.getLength();
131  						for (int i = 0; i < len; i++) {
132  							if (attributes.getQName(i).equals("book")) {
133  								String val = attributes.getValue(i);
134  								String cpath = MyBaseConfig.toSystemPathString(val + ".lisp");
135  								
136  								File f = new File(dir, cpath);
137  								if (f.canRead()) {
138  									order.add(f);
139  								} else {
140  									err.error(msgPrefix + "certify-order-element book does not exist: " + val + " (" + cpath + ")", null);
141  								}
142  							}
143  						}
144  						return;
145  					}
146  					if (name.equals("dependency")) {
147  						int len = attributes.getLength();
148  						for (int i = 0; i < len; i++) {
149  							if (attributes.getQName(i).equals("keyword")) {
150  								String val = attributes.getValue(i);
151  								try {
152  									Sym d = Sym.parse(new StringParser(keywordpc, val));
153  									if (d.isKeyword()) {
154  										deps.add(d);
155  									} else {
156  										err.error(msgPrefix + "include-book-dir-entry must be a keyword symbol", null);
157  									}
158  								} catch (ParseException e) {
159  									err.error(msgPrefix + "Malformed include-book-dir-entry", e);
160  								}
161  							}
162  						}
163  						return;
164  					}
165  					if (name.equals("mode")) {
166  						String mname = null;
167  						String sdesc = null;
168  						String ldesc = null;
169  						boolean bookdev = true;
170  						boolean introductory = false;
171  						LispObject ttags = Sym.nilInstance;
172  						LispObject extraImports = Sym.nilInstance;
173  						int precedence = 500;
174  						String initData = "";
175  						
176  						int len = attributes.getLength();
177  						for (int i = 0; i < len; i++) {
178  							String aname = attributes.getQName(i);
179  							String val = attributes.getValue(i);
180  							if (aname.equals("name")) {
181  								mname = val;
182  							} else if (aname.equals("short_desc")) {
183  								sdesc = val;
184  							} else if (aname.equals("long_desc")) {
185  								ldesc = val;
186  							} else if (aname.equals("book_dev")) {
187  								bookdev = Boolean.parseBoolean(val);
188  							} else if (aname.equals("ttags")) {
189  								try {
190  									ttags = LispObject.parseOne(new StringParser(bootstrappc, val));
191  								} catch (ParseException e) {
192  									err.error(msgPrefix + "Malformed ttags spec for mode", e);
193  								}
194  							} else if (aname.equals("introductory")) {
195  								introductory = Boolean.parseBoolean(val);
196  							} else if (aname.equals("extra_imports")) {
197  								try {
198  									extraImports = LispObject.parseOne(new StringParser(bootstrappc, val));
199  									extraImports = Cons.maybeDeQuote(extraImports);
200  	
201  									if (extraImports.isNotNil()) {
202  										extraImports = Cons.quote(extraImports);
203  									}
204  								} catch (ParseException e) {
205  									err.error(msgPrefix + "Malformed ttags spec for mode", e);
206  								}
207  							} else if (aname.equals("precedence")) {
208  								try {
209  									precedence = Integer.parseInt(val);
210  								} catch (NumberFormatException e) {
211  									err.error(msgPrefix + "Malformed precedence for mode", e);
212  								}
213  							} else if (aname.equals("init_file")) {
214  								val = MyBaseConfig.toSystemPathString(val);
215  								File initFile = new File(dir,val);
216  								try {
217  									FileInputStream stream = new FileInputStream(initFile);
218  									initData = new String(StreamCopier.dump(stream));
219  									stream.close();
220  								} catch (IOException e) {
221  									err.error(msgPrefix + "Error reading init file " + initFile.getAbsolutePath(), e);
222  								}
223  							}
224  						}
225  						if (mname != null) {
226  							modes.add(new SessionMode(dir, mname, sdesc, ldesc, bookdev, ttags.isNotNil(), introductory, extraImports, precedence, new String[] {initData}));
227  						}
228  						return;
229  					}
230  				} else {
231  					if (name.equals("extension")) {
232  						int len = attributes.getLength();
233  						for (int i = 0; i < len; i++) {
234  							if (attributes.getQName(i).equals("point")) {
235  								if (attributes.getValue(i).equals("acl2s.modedir")) {
236  									inExtension = true;
237  									break;
238  								}
239  							}
240  						}
241  						return;
242  					}
243  				}
244  			}
245  	
246  			@Override
247  			public void endElement(String uri, String localName, String name)
248  					throws SAXException {
249  				if (localName.equals("extension")) {
250  					inExtension = false;
251  				}
252  			}
253  			
254  			public ModeDir go() {
255  				File pluginFile = new File(dir, "plugin.xml");
256  				ModeDir ret = null;
257  				try {
258  					saxFactory.newSAXParser().parse(pluginFile, this);
259  					ret = new ModeDir(dir,includeBookDir,
260  							deps.toArray(new Sym[deps.size()]),
261  							order.toArray(new File[order.size()]),
262  							modes.toArray(new SessionMode[modes.size()]));
263  				} catch (SAXException e) {
264  					err.error(msgPrefix + "Error parsing " + pluginFile.getAbsolutePath(), e);
265  				} catch (IOException e) {
266  					err.error(msgPrefix + "Error parsing " + pluginFile.getAbsolutePath(), e);
267  				} catch (ParserConfigurationException e) {
268  					err.error(msgPrefix + "Error parsing " + pluginFile.getAbsolutePath(), e);
269  				}
270  				if (ret != null) {
271  					ret.maybeWriteDirLsp();
272  				}
273  				return ret;
274  			}
275  		}
276  		
277  		public static ModeDir maybeCreate(IAcl2sLibErrorHandler err, File home) {
278  			return new ModeDirParser(err, home).go();
279  		}
280  		
281  		static SAXParserFactory saxFactory = SAXParserFactory.newInstance();
282  	}