root/trunk/ProjectFortress/src/com/sun/fortress/repository/GraphRepository.java @ 3283

Revision 3283, 33.5 KB (checked in by dr2chase, 11 months ago)

More whacking on auto generation of APIs for foreign imports

Line 
1/*******************************************************************************
2    Copyright 2009 Sun Microsystems, Inc.,
3    4150 Network Circle, Santa Clara, California 95054, U.S.A.
4    All rights reserved.
5
6    U.S. Government Rights - Commercial software.
7    Government users are subject to the Sun Microsystems, Inc. standard
8    license agreement and applicable provisions of the FAR and its supplements.
9
10    Use is subject to license terms.
11
12    This distribution may include materials developed by third parties.
13
14    Sun, Sun Microsystems, the Sun logo and Java are trademarks or registered
15    trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
16 ******************************************************************************/
17
18package com.sun.fortress.repository;
19
20import static com.sun.fortress.interpreter.glue.WellKnownNames.defaultLibrary;
21
22import java.io.File;
23import java.io.FileNotFoundException;
24import java.io.IOException;
25import java.lang.reflect.AccessibleObject;
26import java.lang.reflect.Field;
27import java.lang.reflect.Method;
28import java.util.ArrayList;
29import java.util.Arrays;
30import java.util.Collections;
31import java.util.HashMap;
32import java.util.Iterator;
33import java.util.List;
34import java.util.Map;
35import java.util.Set;
36
37import com.sun.fortress.Shell;
38import com.sun.fortress.compiler.AnalyzeResult;
39import com.sun.fortress.compiler.GlobalEnvironment;
40import com.sun.fortress.compiler.Parser;
41import com.sun.fortress.compiler.Parser.Result;
42import com.sun.fortress.compiler.index.ApiIndex;
43import com.sun.fortress.compiler.index.ComponentIndex;
44import com.sun.fortress.exceptions.MultipleStaticError;
45import com.sun.fortress.exceptions.ProgramError;
46import com.sun.fortress.exceptions.StaticError;
47import com.sun.fortress.exceptions.WrappedException;
48import com.sun.fortress.interpreter.glue.WellKnownNames;
49import com.sun.fortress.nodes.APIName;
50import com.sun.fortress.nodes.AliasedAPIName;
51import com.sun.fortress.nodes.AliasedSimpleName;
52import com.sun.fortress.nodes.Api;
53import com.sun.fortress.nodes.CompilationUnit;
54import com.sun.fortress.nodes.Component;
55import com.sun.fortress.nodes.FnDecl;
56import com.sun.fortress.nodes.Id;
57import com.sun.fortress.nodes.IdOrOp;
58import com.sun.fortress.nodes.IdOrOpOrAnonymousName;
59import com.sun.fortress.nodes.Import;
60import com.sun.fortress.nodes.ImportApi;
61import com.sun.fortress.nodes.ImportNames;
62import com.sun.fortress.nodes.ImportedNames;
63import com.sun.fortress.nodes.NodeDepthFirstVisitor_void;
64import com.sun.fortress.nodes.StaticParam;
65import com.sun.fortress.nodes.TraitObjectDecl;
66import com.sun.fortress.nodes.TraitTypeWhere;
67import com.sun.fortress.nodes_util.NodeFactory;
68import com.sun.fortress.nodes_util.NodeUtil;
69import com.sun.fortress.nodes_util.Span;
70import com.sun.fortress.repository.graph.ApiGraphNode;
71import com.sun.fortress.repository.graph.ComponentGraphNode;
72import com.sun.fortress.repository.graph.Graph;
73import com.sun.fortress.repository.graph.GraphNode;
74import com.sun.fortress.repository.graph.GraphVisitor;
75import com.sun.fortress.syntax_abstractions.parser.FortressParser;
76import com.sun.fortress.useful.Bijection;
77import com.sun.fortress.useful.Debug;
78import com.sun.fortress.useful.Fn;
79import com.sun.fortress.useful.HashBijection;
80import com.sun.fortress.useful.MultiMap;
81import com.sun.fortress.useful.Path;
82import com.sun.fortress.useful.Useful;
83import com.sun.fortress.useful.Debug.Type;
84
85import edu.rice.cs.plt.iter.IterUtil;
86import edu.rice.cs.plt.tuple.Option;
87import edu.rice.cs.plt.tuple.OptionUnwrapException;
88
89/* A graph-based repository. This repository determines the dependency structure
90 * before any components/APIs are compiled so that they can be compiled in an
91 * efficient and deterministic order.
92 *
93 * When only compiling a .fss file,
94 * the .fss depends on its importing APIs and those APIs depend
95 * on the APIs they import.
96 * When linking a .fss file, the .fss depends on its importing APIs,
97 * and components that implement those APIs are added to the graph.
98 *
99 * Elements in the graph are stored as Node depends on List<Node>
100 */
101public class GraphRepository extends StubRepository implements FortressRepository {
102
103    /* stores the nodes and their relationships */
104    private Graph<GraphNode> graph;
105    /* current source path */
106    private Path path;
107    /* underlying cache of compiled files */
108    private CacheBasedRepository cache;
109    /* true if a recompile is needed */
110    private boolean needUpdate = true;
111    /* If link is true then pull in a component for an API */
112    private boolean link = false;
113
114    ForeignJava foreignJava = new ForeignJava();
115
116    public GraphRepository(Path p, CacheBasedRepository cache) throws FileNotFoundException {
117        this.path = p;
118        this.cache = cache;
119        graph = new Graph<GraphNode>();
120        addRoots();
121    }
122
123    private static String[] roots() {
124        /* files that are dependencies of everything */
125        return defaultLibrary();
126    }
127
128    /* by default all the root APIs should be added to the graph
129     * and set as dependencies for everything else.
130     */
131    private void addRoots() throws FileNotFoundException {
132        for ( String root : roots() ){
133            APIName name = NodeFactory.makeAPIName(NodeFactory.shellSpan,root);
134            ApiGraphNode api = new ApiGraphNode(name, getApiFileDate(name));
135            try{
136                long cache_date = cache.getModifiedDateForApi(api.getName());
137                api.setApi( cache.getApi( api.getName() ), cache_date);
138            } catch ( FileNotFoundException e ){
139            } catch ( IOException e ){
140            }
141            graph.addNode( api );
142        }
143
144        for ( String root : roots() ) {
145            ApiGraphNode node =
146                (ApiGraphNode) graph.find(ApiGraphNode.key(NodeFactory.makeAPIName(NodeFactory.shellSpan,root)));
147            try{
148                for ( APIName api : dependencies(node) ){
149                    Debug.debug( Debug.Type.REPOSITORY, 2, "Add edge ", api );
150                    graph.addEdge(node, addApiGraph(api));
151                }
152            } catch ( FileNotFoundException e ){
153            } catch ( IOException e ){
154            }
155        }
156    }
157
158    @Override
159    public Map<APIName, ApiIndex> apis() {
160        return cache.apis();
161    }
162
163    @Override
164    public Map<APIName, ComponentIndex> components() {
165        return cache.components();
166    }
167
168    /* getApi and getComponent add an API/component to the graph, find all the
169     * dependencies( via addApiGraph/addComponentGraph ) and recompile everything.
170     */
171    @Override
172    public ApiIndex getApi(APIName name) throws FileNotFoundException, IOException, StaticError {
173       Debug.debug( Debug.Type.REPOSITORY, 2, "Get API for ", name);
174       
175        ApiGraphNode node = addApiGraph(name);
176        refreshGraph();
177        try{
178            return node.getApi().unwrap();
179        } catch ( OptionUnwrapException o ){
180            throw StaticError.make( "Cannot find api " + name + " in the repository. This should not happen, please contact a developer.", "" );
181        }
182    }
183
184    @Override
185    public ComponentIndex getComponent(APIName name) throws FileNotFoundException, IOException, StaticError {
186        Debug.debug( Debug.Type.REPOSITORY, 2, "Get component for ", name );
187        ComponentGraphNode node = addComponentGraph(name);
188        refreshGraph();
189        try{
190            return node.getComponent().unwrap();
191        } catch ( OptionUnwrapException o ){
192            throw StaticError.make( "Cannot find component " + name + " in the repository. " +
193                                    "This should not happen, please contact a developer.", "" );
194        }
195    }
196
197    /* add an API node to the graph and return the node. if the API exists in the
198     * cache it is loaded, otherwise it will remain empty until it gets
199     * recompiled( probably via refreshGraph )
200     */
201    private ApiGraphNode addApiGraph( APIName name ) throws FileNotFoundException, IOException {
202        Debug.debug( Debug.Type.REPOSITORY, 2, "Add API graph ", name );
203        ApiGraphNode node = (ApiGraphNode) graph.find(ApiGraphNode.key(name));
204        if ( node == null ){
205           
206            if (foreignJava.definesApi(name)) {
207                // TODO not smart about age of native API yet
208                // Make the native API be very old, so nothing is out of date;
209                needUpdate = true;
210                node = new ApiGraphNode(name, Long.MIN_VALUE);
211                graph.addNode( node );
212                return node;
213            }
214           
215            /* a new node was added, a recompile is needed */
216            needUpdate = true;
217            node = new ApiGraphNode(name, getApiFileDate(name));
218            graph.addNode( node );
219            try{
220                /* try to load the API from the cache.
221                 * if it fails then it will be reloaded later on
222                 * in refreshGraph
223                 */
224                long cache_date = getCacheDate(node);
225                if ( cache_date > getApiFileDate(node) ){
226                    Debug.debug( Debug.Type.REPOSITORY, 2 , "Found cached version of ", node );
227                    node.setApi( cache.getApi(name), cache_date);
228                }
229            } catch ( FileNotFoundException f ){
230                /* oh well */
231            } catch ( IOException e ){
232            }
233            /* make this API depend on the APIs it imports */
234            for ( APIName api : dependencies(node) ){
235                Debug.debug( Debug.Type.REPOSITORY, 2, "Add edge ", api );
236                graph.addEdge(node, addApiGraph(api));
237            }
238            /* and depend on all the root APIs */
239            for ( String root : roots() ){
240                graph.addEdge(node, addApiGraph(NodeFactory.makeAPIName(NodeFactory.shellSpan,root)));
241            }
242        }
243
244        return node;
245    }
246
247    /* same thing, but add a component */
248    private ComponentGraphNode addComponentGraph( APIName name ) throws FileNotFoundException, IOException, StaticError {
249        Debug.debug( Debug.Type.REPOSITORY, 2, "Add component graph ", name );
250        ComponentGraphNode node = (ComponentGraphNode) graph.find(ComponentGraphNode.key(name));
251        if ( node == null ){
252            /* a new node was added, a recompile is needed */
253            needUpdate = true;
254            node = new ComponentGraphNode(name, getComponentFileDate(name));
255            graph.addNode( node );
256            try{
257                /* try to load the component from the cache.
258                 * if it fails then it will be reloaded later on
259                 * in refreshGraph
260                 */
261                long cache_date = getCacheDate(node);
262                if ( cache_date > getComponentFileDate(node) ){
263                    Debug.debug( Debug.Type.REPOSITORY, 2 , "Found cached version of ", node );
264                    node.setComponent( cache.getComponent(name), cache_date);
265                }
266            } catch ( FileNotFoundException f ){
267                /* oh well */
268            } catch ( IOException e ){
269            }
270
271            /* make this component depend on the APIs it imports */
272            for ( APIName api : dependencies(node) ){
273                nodeDependsOnApi(node, api);
274            }
275            /* and depend on all the root APIs */
276            for ( String root : roots() ){
277                nodeDependsOnApi(node, NodeFactory.makeAPIName(NodeFactory.shellSpan,root));
278            }
279        }
280
281        return node;
282    }
283
284    private void nodeDependsOnApi(ComponentGraphNode node, APIName api)
285            throws FileNotFoundException, IOException {
286        Debug.debug( Debug.Type.REPOSITORY, 2, "Add edge ", api );
287        graph.addEdge(node, addApiGraph(api));
288        boolean b = foreignJava.definesApi(api);
289        // System.err.println("b="+b);
290        if ( link && ! b){
291            Debug.debug( Debug.Type.REPOSITORY, 1, "Component ", node.getName(), " depends on API ", api );
292            // Add element, but no API
293            addComponentGraph(api);
294        }
295    }
296
297    private long getCacheDate( ApiGraphNode node ){
298        try{
299            return cache.getModifiedDateForApi(node.getName());
300        } catch ( FileNotFoundException e ){
301            return Long.MIN_VALUE;
302        }
303    }
304
305    private long getCacheDate( ComponentGraphNode node ){
306        try{
307            return cache.getModifiedDateForComponent(node.getName());
308        } catch ( FileNotFoundException e ){
309            return Long.MIN_VALUE;
310        }
311    }
312
313    private long getCacheDate( GraphNode node ){
314        try{
315            return node.accept( new CacheVisitor() );
316        } catch ( FileNotFoundException e ){
317            return Long.MIN_VALUE;
318        }
319    }
320
321    private long getComponentFileDate( ComponentGraphNode node ) throws FileNotFoundException {
322        return node.getSourceDate();
323    }
324
325    private long getComponentFileDate( APIName name ) throws FileNotFoundException {
326        return findFile( name, ProjectProperties.COMP_SOURCE_SUFFIX ).lastModified();
327    }
328
329    private long getApiFileDate( ApiGraphNode node ) throws FileNotFoundException {
330        return node.getSourceDate();
331    }
332
333    private long getApiFileDate( APIName name ) throws FileNotFoundException {
334        return findFile( name, ProjectProperties.API_SOURCE_SUFFIX ).lastModified();
335    }
336
337    public File findFile(APIName name, String suffix) throws FileNotFoundException {
338        String dotted = name.toString();
339        String slashed = dotted.replaceAll("[.]", "/");
340        slashed = slashed + "." + suffix;
341        File fdot;
342
343        Debug.debug( Debug.Type.REPOSITORY, 3, "Finding file ", name);
344        try {
345            fdot = path.findFile(slashed);
346        } catch (FileNotFoundException ex2) {
347            throw new FileNotFoundException(NodeUtil.getSpan(name) +
348                                            "\n    Could not find API " + dotted +
349                                            " in file named " + slashed +
350                                            " on path\n    " + path);
351        }
352        return fdot;
353    }
354
355    private class CacheVisitor implements GraphVisitor<Long, FileNotFoundException>{
356        public Long visit(ApiGraphNode node) throws FileNotFoundException {
357            return getCacheDate(node);
358        }
359
360        public Long visit(ComponentGraphNode node) throws FileNotFoundException {
361            return getCacheDate(node);
362        }
363    }
364
365    /* what if the file has been edited to include import statements that the cached
366     * version doesn't have? that's ok because the cached version won't be loaded unless it
367     * is newer than the file on disk.
368     */
369    private List<APIName> dependencies(ApiGraphNode node) throws FileNotFoundException, StaticError {
370        CompilationUnit cu = node.getApi().isSome() ?
371                node.getApi().unwrap().ast() :
372                    readCUFor(node, ProjectProperties.API_SOURCE_SUFFIX);
373
374        return collectApiImports((Api)cu);
375
376    }
377
378    private List<APIName> dependencies(ComponentGraphNode node) throws FileNotFoundException, StaticError {
379        CompilationUnit cu = node.getComponent().isSome() ?
380                node.getComponent().unwrap().ast() :
381                    readCUFor(node, ProjectProperties.COMP_SOURCE_SUFFIX);
382
383        return collectComponentImports((Component)cu);
384    }
385
386    private boolean inApiList( APIName name, List<ApiGraphNode> nodes ){
387        for ( ApiGraphNode node : nodes ){
388            if ( node.getName().equals( name ) ){
389                return true;
390            }
391        }
392        return false;
393    }
394
395    private boolean inComponentList( APIName name, List<ComponentGraphNode> nodes ){
396        for ( ComponentGraphNode node : nodes ){
397            if ( node.getName().equals( name ) ){
398                return true;
399            }
400        }
401        return false;
402    }
403
404    /* reparse anything that is out of date */
405    private AnalyzeResult refreshGraph() throws FileNotFoundException, IOException, StaticError {
406        AnalyzeResult result =
407            new AnalyzeResult(IterUtil.<StaticError>empty());
408        if ( needUpdate ){
409            needUpdate = false;
410            OutOfDateVisitor date = new OutOfDateVisitor();
411            for ( GraphNode node : graph.nodes() ){
412                node.accept( date );
413            }
414            List<ApiGraphNode> reparseApis = date.apis();
415            List<ComponentGraphNode> reparseComponents = sortComponents(date.components());
416            Debug.debug( Debug.Type.REPOSITORY, 1, "Out of date APIs ", reparseApis );
417            Debug.debug( Debug.Type.REPOSITORY, 1, "Out of date components ", reparseComponents );
418            /* these can be parsed all at once */
419            result = parseApis(date.outOfDateApi());
420            for ( Map.Entry<APIName, ApiIndex> entry : result.apis().entrySet() ){
421                if ( inApiList(entry.getKey(), reparseApis) ){
422                    addApi( entry.getKey(), entry.getValue() );
423                }
424            }
425
426            /* but these have to be done in a specific order due to
427             * syntax expansion requiring some components, like
428             * FortressBuiltin, being parsed.
429             * If syntax expansion was unable to execute code then all
430             * the APIs and components could be parsed at the same time.
431             */
432            for ( ComponentGraphNode node : reparseComponents ){
433                /* parseComponent will call Fortress.analyze which
434                 * will call repository.add(component). That add() will
435                 * call setComponent() on this node with the same component
436                 * that parseComponent is going to return.
437                 */
438                result = parseComponent(syntaxExpand(node));
439                for ( Map.Entry<APIName, ComponentIndex> entry : result.components().entrySet() ){
440                    if ( inComponentList( entry.getKey(), reparseComponents ) ){
441                        addComponent( entry.getKey(), entry.getValue() );
442                    }
443                }
444            }
445        }
446        return result;
447    }
448
449    private class OutOfDateVisitor implements GraphVisitor<Boolean,FileNotFoundException>{
450        private Map<GraphNode, Long> youngestSourceDependedOn;
451        private Map<GraphNode, Boolean> staleOrDependsOnStale;
452
453        public OutOfDateVisitor(){
454            youngestSourceDependedOn = new HashMap<GraphNode,Long>();
455            staleOrDependsOnStale = new HashMap<GraphNode,Boolean>();
456        }
457
458        Fn<GraphNode,Boolean> outOfDateApi() {
459            return new Fn<GraphNode,Boolean>(){
460                @Override
461                public Boolean apply(GraphNode g){
462                    return g instanceof ApiGraphNode && staleOrDependsOnStale.get(g);
463                }
464            };
465        }
466
467        Fn<GraphNode,Boolean> outOfDateComponent() {
468            return new Fn<GraphNode,Boolean>(){
469                @Override
470                public Boolean apply(GraphNode g){
471                    return g instanceof ComponentGraphNode && staleOrDependsOnStale.get(g);
472                }
473            };
474        }
475
476        public List<ApiGraphNode> apis(){
477            return Useful.convertList(Useful.filter(youngestSourceDependedOn.keySet(), outOfDateApi()));
478        }
479        /* returns out of date components */
480        public List<ComponentGraphNode> components(){
481            return Useful.convertList(Useful.filter(youngestSourceDependedOn.keySet(), outOfDateComponent()));
482        }
483
484        private Long handle( GraphNode node ) throws FileNotFoundException {
485            if ( youngestSourceDependedOn.containsKey(node) ){
486                return youngestSourceDependedOn.get(node);
487            }
488            long youngest = node.getSourceDate();
489            youngestSourceDependedOn.put(node, youngest);
490
491            List<GraphNode> depends = graph.depends(node);
492            Debug.debug( Debug.Type.REPOSITORY, 2, node + " depends on " + depends );
493            for ( GraphNode next : depends ){
494                long dependent_youngest = handle(next);
495                if (dependent_youngest > youngest) {
496
497                    Debug.debug( Debug.Type.REPOSITORY, 1, next + " has younger source than " + next );
498                    youngest = dependent_youngest;
499                }
500            }
501
502            youngestSourceDependedOn.put(node, youngest);
503
504            return youngest;
505        }
506
507        private Boolean isStale( GraphNode node ) throws FileNotFoundException {
508            if ( staleOrDependsOnStale.containsKey(node) ){
509                return staleOrDependsOnStale.get(node);
510            }
511
512            boolean stale = youngestSourceDependedOn.get(node) > getCacheDate(node);
513
514            // If anything depended on has source that is younger than our compiled code,
515            // then this is stale.
516            if ( stale ){
517                Debug.debug( Debug.Type.REPOSITORY, 1, node + "or dependent is newer " + youngestSourceDependedOn.get(node) + " than the cache " + getCacheDate(node) );
518            }
519
520            staleOrDependsOnStale.put(node, stale);
521
522            List<GraphNode> depends = graph.depends(node);
523            Debug.debug( Debug.Type.REPOSITORY, 2, node + " depends on " + depends );
524            for ( GraphNode next : depends ){
525                boolean dependent_stale = isStale(next);
526
527                if ( dependent_stale  ){
528                    stale = true;
529                    Debug.debug( Debug.Type.REPOSITORY, 1, node + " is stale " + next + " is stale" );
530                    staleOrDependsOnStale.put(node, stale);
531                }
532            }
533            return stale;
534        }
535
536        public Boolean visit( ApiGraphNode node ) throws FileNotFoundException {
537            handle(node);
538            return isStale( node );
539        }
540
541        public Boolean visit( ComponentGraphNode node ) throws FileNotFoundException {
542            handle(node);
543            return isStale( node );
544        }
545    }
546
547    private List<ComponentGraphNode> sortComponents(List<ComponentGraphNode> nodes) throws FileNotFoundException {
548        Graph<GraphNode> componentGraph = new Graph<GraphNode>( graph, new Fn<GraphNode, Boolean>(){
549                @Override
550                public Boolean apply(GraphNode g){
551                    return g instanceof ComponentGraphNode;
552                }
553            });
554
555        /* force components that import things to depend on the component
556         * that implements that import. This is for syntax abstraction
557         * update 6/19/2008: this would prevent separate compilation, so
558         * don't set things up this way.
559         */
560        /*
561        for ( GraphNode node : componentGraph.nodes() ){
562            ComponentGraphNode comp = (ComponentGraphNode) node;
563            for ( GraphNode dependency : graph.dependencies(comp) ){
564                if ( dependency instanceof ApiGraphNode ){
565                    ComponentGraphNode next = (ComponentGraphNode) componentGraph.find( new ComponentGraphNode( ((ApiGraphNode) dependency).getName() ) );
566                    // ComponentGraphNode next = new ComponentGraphNode( ((ApiGraphNode) dependency).getName() );
567                    if ( ! next.equals( comp ) ){
568                        componentGraph.addEdge( comp, next );
569                    }
570                }
571            }
572        }
573        */
574
575        if ( Debug.isOnFor(2, Debug.Type.REPOSITORY) ){
576            Debug.debug( Debug.Type.REPOSITORY, 2, componentGraph.getDebugString() );
577        }
578
579        List<GraphNode> sorted = componentGraph.sorted();
580        List<ComponentGraphNode> rest = new ArrayList<ComponentGraphNode>();
581
582        for ( GraphNode node : sorted ){
583            ComponentGraphNode comp = (ComponentGraphNode) node;
584            if ( nodes.contains( comp ) ){
585                /* force root components to come in front of other things */
586                if ( Arrays.asList(roots()).contains(comp.getName().toString()) ){
587                    rest.add( 0, comp );
588                } else {
589                    rest.add( comp );
590                }
591            }
592        }
593
594        return rest;
595    }
596
597    private AnalyzeResult parseApis(final Fn<GraphNode, Boolean> these_apis){
598
599        List<Api> unparsed = Useful.applyToAll(graph.filter(these_apis),
600            new Fn<GraphNode, Api>(){
601                @Override
602                public Api apply(GraphNode g){
603                    return parseApi((ApiGraphNode) g);
604                }
605            });
606
607        if (unparsed.size() == 0)
608            return new AnalyzeResult(IterUtil.<StaticError>empty());
609
610        GlobalEnvironment knownApis = new GlobalEnvironment.FromMap(parsedApis());
611
612        // Can we exclude non-imported pieces of the api here?
613
614
615        List<Component> components = new ArrayList<Component>();
616        Shell shell = new Shell(this);
617        AnalyzeResult result =
618            Shell.analyze(shell.getRepository(),
619                          knownApis, unparsed, components, System.currentTimeMillis() );
620        if ( !result.isSuccessful() ){
621            throw new MultipleStaticError(result.errors());
622        }
623        return result;
624    }
625
626    /* return a parsed API */
627    private Api parseApi( ApiGraphNode node ){
628        try{
629            APIName api_name = node.getName();
630            File fdot = findFile(api_name, ProjectProperties.API_SOURCE_SUFFIX);
631            CompilationUnit api = Parser.parseFileConvertExn(fdot);
632            if (api instanceof Api) {
633                return (Api) api;
634            } else {
635                throw StaticError.make("Unexpected parse of API " + api_name, "");
636            }
637        } catch ( FileNotFoundException e ){
638            throw new WrappedException(e);
639        } catch ( IOException e ){
640            throw new WrappedException(e);
641        }
642    }
643
644
645    /* find all parsed APIs */
646    public Map<APIName, ApiIndex> parsedApis(){
647       
648        Map<APIName, ApiIndex> apis = new HashMap<APIName, ApiIndex>();
649       
650        for ( GraphNode g :  graph.nodes()){
651            if (g instanceof ApiGraphNode) {
652                ApiGraphNode node = (ApiGraphNode) g;
653                if (node.getApi().isSome()) {
654                    apis.put( node.getName(), node.getApi().unwrap() );
655                } else if (foreignJava.definesApi(node.getName())) {
656                    apis.put(node.getName(), foreignJava.fakeApi(node.getName()));
657                }
658            }
659        }
660        return apis;
661    }
662
663    /* parse a single component. */
664    private AnalyzeResult parseComponent( Component component ) throws StaticError {
665        GlobalEnvironment knownApis = new GlobalEnvironment.FromMap(parsedApis());
666        List<Component> components = new ArrayList<Component>();
667        components.add(component);
668        long now = System.currentTimeMillis();
669        Debug.debug( Debug.Type.REPOSITORY, 1, "Parsing ", component, " at ", now );
670
671        Shell shell = new Shell(this);
672        AnalyzeResult result =
673            Shell.analyze(shell.getRepository(),
674                          knownApis, new ArrayList<Api>(), components, now );
675        if ( !result.isSuccessful() ){
676            throw new MultipleStaticError(result.errors());
677        }
678        return result;
679    }
680
681    /* parse a component and run it through syntax expansion, may
682     * invoke code( transformer expressions )
683     */
684    private Component syntaxExpand( ComponentGraphNode node ) throws FileNotFoundException, IOException {
685        Debug.debug( Debug.Type.REPOSITORY, 1, "Expand component ", node );
686
687        APIName api_name = node.getName();
688        File file = findFile(api_name, ProjectProperties.COMP_SOURCE_SUFFIX);
689        GraphRepository g1 = new GraphRepository( this.path, this.cache );
690        /* FIXME: hack to prevent infinite recursion */
691        Shell.setCurrentInterpreterRepository( g1 );
692        Result result = FortressParser.parse(api_name, file, new GlobalEnvironment.FromRepository( g1 ), verbose());
693        // Result result = FortressParser.parse(file, new GlobalEnvironment.FromRepository(this), verbose());
694        /* FIXME: hack to prevent infinite recursion */
695        Shell.setCurrentInterpreterRepository( this );
696        if (result.isSuccessful()) {
697            Debug.debug( Debug.Type.REPOSITORY, 1, "Expanded component ", node );
698            Iterator<Component> components = result.components().iterator();
699            if (components.hasNext()) return components.next();
700            throw new ProgramError("Successful parse result was nonetheless empty, file " + file.getCanonicalPath());
701        }
702        throw new ProgramError(result.errors());
703    }
704
705    /* add an API to the repository and cache it */
706    @Override
707    public void addApi(APIName name, ApiIndex definition) {
708        ApiGraphNode node = (ApiGraphNode) graph.find(ApiGraphNode.key(name));
709        if ( node == null ){
710            throw new RuntimeException("No such API '" + name + "'");
711        } else {
712            node.setApi(definition, definition.modifiedDate());
713            cache.addApi(name, definition);
714        }
715    }
716
717    /* add a component to the repository and cache it */
718    @Override
719    public void addComponent(APIName name, ComponentIndex definition){
720        ComponentGraphNode node = (ComponentGraphNode) graph.find(ComponentGraphNode.key(name));
721        if (node == null ){
722            throw new RuntimeException("No such component " + name);
723        } else {
724            node.setComponent(definition, definition.modifiedDate());
725            cache.addComponent(name, definition);
726        }
727    }
728
729    @Override
730    public void deleteComponent(APIName name) {
731        ComponentGraphNode node = (ComponentGraphNode) graph.find(ComponentGraphNode.key(name));
732        if ( node != null ){
733            cache.deleteComponent(name);
734        }
735    }
736
737    @Override
738    public ComponentIndex getLinkedComponent(APIName name) throws FileNotFoundException, IOException {
739        link = true;
740        addRootComponents();
741        ComponentIndex node = getComponent(name);
742        link = false;
743        return node;
744    }
745
746    private void addRootComponents() throws FileNotFoundException, IOException{
747        boolean added = false;
748        for ( String root : roots() ){
749            APIName name = NodeFactory.makeAPIName(NodeFactory.shellSpan,root);
750            if (null == graph.find(ApiGraphNode.key(name))) {
751                addApiGraph(name);
752            }
753            // If the API is from import-native, treat it differently than this.
754            if (link && null == graph.find(ComponentGraphNode.key(name))) {
755                addComponentGraph(name);
756            }
757//            ApiGraphNode node = (ApiGraphNode) graph.find(new ApiGraphNode(name));
758//            ComponentGraphNode comp = new ComponentGraphNode(name);
759//            try{
760//                comp.setComponent( cache.getComponent( comp.getName() ) );
761//            } catch ( FileNotFoundException e ){
762//            } catch ( IOException e ){
763//            }
764//            graph.addNode( comp );
765//            graph.addEdge( comp, node );
766        }
767    }
768
769    @Override
770    public long getModifiedDateForApi(APIName name)
771        throws FileNotFoundException {
772        return 0;
773    }
774
775    @Override
776    public long getModifiedDateForComponent(APIName name)
777        throws FileNotFoundException {
778        return 0;
779    }
780
781    @Override
782    public void clear() {
783        cache.clear();
784    }
785
786    private List<APIName> collectExplicitImports(CompilationUnit comp) {
787        List<APIName> all = new ArrayList<APIName>();
788       
789        for (Import i : comp.getImports()){
790            Option<String> opt_fl = i.getForeignLanguage();
791            boolean isNative = opt_fl.isSome();
792            if (isNative && (i instanceof ImportNames)) {
793                String fl = opt_fl.unwrap();
794                // Conditional overlap with later clause.
795                // Handle import of foreign names here.
796                // Ought to handle this case by case.
797                ImportNames ins = (ImportNames) i;
798                if("java".equalsIgnoreCase(fl)) {
799                    /*
800                     *  Don't create the API yet; its contents depend on all
801                     *  the imports.
802                     */
803                    foreignJava.processJavaImport(i, ins);
804                   
805                    // depend on the API name;
806                    // "compilation"/"reading" will get the API
807                    all.add( ins.getApiName() );
808                    continue;
809                } else if ("fortress".equalsIgnoreCase(fl)) {
810                    // do nothing, fall into normal case
811                } else {
812                    throw StaticError.make("Foreign language "+ fl + " not yet handled ", i);
813                }
814            }
815
816            if (i instanceof ImportedNames) {
817                ImportedNames names = (ImportedNames) i;
818                all.add( names.getApiName() );
819            } else { // i instanceof ImportApi
820                ImportApi apis = (ImportApi) i;
821                for (AliasedAPIName a : apis.getApis()) {
822                    all.add(a.getApiName());
823                }
824            }
825        }
826        return all;
827    }
828
829    private  List<APIName> collectComponentImports(Component comp) {
830         List<APIName> all =  collectExplicitImports(comp);
831
832         for (APIName api : comp.getExports()) {
833             all.add(api);
834         }
835         return removeExecutableApi(all);
836     }
837
838    private  List<APIName> collectApiImports(Api api) {
839        List<APIName> all =  collectExplicitImports(api);
840
841        return removeExecutableApi(all);
842    }
843
844    private CompilationUnit readCUFor(GraphNode node, String sourceSuffix) throws FileNotFoundException {
845        APIName name = node.getName();
846        File fdot = findFile(name, sourceSuffix);
847        return Parser.preparseFileConvertExn(fdot);
848    }
849
850    private static List<APIName> removeExecutableApi(List<APIName> all){
851        List<APIName> fixed = new ArrayList<APIName>();
852        for (APIName name : all){
853            if (! WellKnownNames.exportsMain(name.getText())) {
854                fixed.add(name);
855            }
856        }
857        return fixed;
858    }
859
860}
Note: See TracBrowser for help on using the browser.