OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library analysis.server; |
| 6 |
| 7 import 'dart:async'; |
| 8 |
| 9 import 'package:analyzer/src/generated/engine.dart'; |
| 10 |
| 11 import 'analysis_logger.dart'; |
| 12 import 'channel.dart'; |
| 13 import 'protocol.dart'; |
| 14 |
| 15 /** |
| 16 * Instances of the class [AnalysisServer] implement a server that listens on a |
| 17 * [CommunicationChannel] for analysis requests and process them. |
| 18 */ |
| 19 class AnalysisServer { |
| 20 /** |
| 21 * The name of the notification of new errors associated with a source. |
| 22 */ |
| 23 static const String ERROR_NOTIFICATION_NAME = 'context.errors'; |
| 24 |
| 25 /** |
| 26 * The name of the parameter whose value is a list of errors. |
| 27 */ |
| 28 static const String ERRORS_PARAM = 'errors'; |
| 29 |
| 30 /** |
| 31 * The name of the parameter whose value is a source. |
| 32 */ |
| 33 static const String SOURCE_PARAM = 'source'; |
| 34 |
| 35 /** |
| 36 * The channel from which requests are received and to which responses should |
| 37 * be sent. |
| 38 */ |
| 39 CommunicationChannel channel; |
| 40 |
| 41 /** |
| 42 * A flag indicating whether the server is running. |
| 43 */ |
| 44 bool running; |
| 45 |
| 46 /** |
| 47 * A list of the request handlers used to handle the requests sent to this |
| 48 * server. |
| 49 */ |
| 50 List<RequestHandler> handlers; |
| 51 |
| 52 /** |
| 53 * A table mapping context id's to the analysis contexts associated with them. |
| 54 */ |
| 55 final Map<String, AnalysisContext> contextMap = new Map<String, AnalysisContex
t>(); |
| 56 |
| 57 /** |
| 58 * A list of the analysis contexts for which analysis work needs to be |
| 59 * performed. |
| 60 */ |
| 61 final List<AnalysisContext> contextWorkQueue = new List<AnalysisContext>(); |
| 62 |
| 63 /** |
| 64 * Initialize a newly created server to receive requests from and send |
| 65 * responses to the given [channel]. |
| 66 */ |
| 67 AnalysisServer(CommunicationChannel channel) { |
| 68 AnalysisEngine.instance.logger = new AnalysisLogger(); |
| 69 running = true; |
| 70 this.channel = channel; |
| 71 this.channel.listen(handleRequest, onError: error, onDone: done); |
| 72 } |
| 73 |
| 74 /** |
| 75 * Add the given [context] to the list of analysis contexts for which analysis |
| 76 * work needs to be performed. Ensure that the work will be performed. |
| 77 */ |
| 78 void addContextToWorkQueue(AnalysisContext context) { |
| 79 if (!contextWorkQueue.contains(context)) { |
| 80 contextWorkQueue.add(context); |
| 81 run(); |
| 82 } |
| 83 } |
| 84 |
| 85 /** |
| 86 * The socket from which requests are being read has been closed. |
| 87 */ |
| 88 void done() { |
| 89 running = false; |
| 90 } |
| 91 |
| 92 /** |
| 93 * There was an error related to the socket from which requests are being |
| 94 * read. |
| 95 */ |
| 96 void error() { |
| 97 running = false; |
| 98 } |
| 99 |
| 100 /** |
| 101 * Handle a [request] that was read from the communication channel. |
| 102 */ |
| 103 void handleRequest(Request request) { |
| 104 int count = handlers.length; |
| 105 for (int i = 0; i < count; i++) { |
| 106 Response response = handlers[i].handleRequest(request); |
| 107 if (response != null) { |
| 108 channel.sendResponse(response); |
| 109 return; |
| 110 } |
| 111 } |
| 112 channel.sendResponse(new Response.unknownRequest(request)); |
| 113 } |
| 114 |
| 115 /** |
| 116 * Perform the next available task. If a request was received that has not yet |
| 117 * been performed, perform it next. Otherwise, look for some analysis that |
| 118 * needs to be done and do that. Otherwise, do nothing. |
| 119 */ |
| 120 void performTask() { |
| 121 // |
| 122 // Look for a context that has work to be done and then perform one task. |
| 123 // |
| 124 if (!contextWorkQueue.isEmpty) { |
| 125 AnalysisContext context = contextWorkQueue[0]; |
| 126 AnalysisResult result = context.performAnalysisTask(); |
| 127 List<ChangeNotice> notices = result.changeNotices; |
| 128 if (notices == null) { |
| 129 contextWorkQueue.removeAt(0); |
| 130 } else { //if (context.analysisOptions.provideErrors) { |
| 131 sendNotices(notices); |
| 132 } |
| 133 } |
| 134 // |
| 135 // Schedule this method to be run again if there is any more work to be done
. |
| 136 // |
| 137 if (contextWorkQueue.isEmpty) { |
| 138 running = false; |
| 139 } else { |
| 140 Timer.run(() { |
| 141 performTask(); |
| 142 }); |
| 143 } |
| 144 } |
| 145 |
| 146 /** |
| 147 * Send the information in the given list of notices back to the client. |
| 148 */ |
| 149 void sendNotices(List<ChangeNotice> notices) { |
| 150 for (int i = 0; i < notices.length; i++) { |
| 151 ChangeNotice notice = notices[i]; |
| 152 Notification notification = new Notification(ERROR_NOTIFICATION_NAME); |
| 153 notification.setParameter(SOURCE_PARAM, notice.source.encoding); |
| 154 notification.setParameter(ERRORS_PARAM, notice.errors); |
| 155 sendNotification(notification); |
| 156 } |
| 157 } |
| 158 |
| 159 /** |
| 160 * Perform the tasks that are waiting for execution until the server is shut |
| 161 * down. |
| 162 */ |
| 163 void run() { |
| 164 if (!running) { |
| 165 running = true; |
| 166 Timer.run(() { |
| 167 performTask(); |
| 168 }); |
| 169 } |
| 170 } |
| 171 |
| 172 /** |
| 173 * Send the given [notification] to the client. |
| 174 */ |
| 175 void sendNotification(Notification notification) { |
| 176 channel.sendNotification(notification); |
| 177 } |
| 178 } |
OLD | NEW |