~didrocks/+junk/face-detection-15.04

« back to all changes in this revision

Viewing changes to facedetection/www/bower_components/hydrolysis/src/ast-utils/behavior-finder.ts

  • Committer: Didier Roche
  • Date: 2016-05-10 23:09:11 UTC
  • Revision ID: didier.roche@canonical.com-20160510230911-c7xr490zrj3yrzxd
New version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/**
 
2
 * @license
 
3
 * Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
 
4
 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
 
5
 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
 
6
 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
 
7
 * Code distributed by Google as part of the polymer project is also
 
8
 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
 
9
 */
 
10
 
 
11
'use strict';
 
12
import * as estraverse from 'estraverse';
 
13
import * as docs from './docs';
 
14
import * as esutil from './esutil';
 
15
import * as jsdoc from './jsdoc';
 
16
import * as analyzeProperties from './analyze-properties';
 
17
import * as astValue from './ast-value';
 
18
import * as estree from 'estree';
 
19
import {Visitor} from './fluent-traverse';
 
20
import {declarationPropertyHandlers, PropertyHandlers} from './declaration-property-handlers';
 
21
import {BehaviorDescriptor, LiteralValue} from './descriptors';
 
22
 
 
23
interface KeyFunc<T> {
 
24
  (value: T): any;
 
25
}
 
26
 
 
27
function dedupe<T>(array: T[], keyFunc: KeyFunc<T>): T[] {
 
28
  var bucket = {};
 
29
  array.forEach((el) => {
 
30
    var key = keyFunc(el);
 
31
    if (key in bucket) {
 
32
      return;
 
33
    }
 
34
    bucket[key] = el;
 
35
  });
 
36
  var returned = <Array<T>>[];
 
37
  Object.keys(bucket).forEach((k) => {
 
38
    returned.push(bucket[k]);
 
39
  });
 
40
  return returned;
 
41
}
 
42
 
 
43
// TODO(rictic): turn this into a class.
 
44
export function behaviorFinder() {
 
45
  /** The behaviors we've found. */
 
46
  var behaviors: BehaviorDescriptor[] = [];
 
47
 
 
48
  var currentBehavior: BehaviorDescriptor = null;
 
49
  var propertyHandlers: PropertyHandlers = null;
 
50
 
 
51
  /**
 
52
   * merges behavior with preexisting behavior with the same name.
 
53
   * here to support multiple @polymerBehavior tags referring
 
54
   * to same behavior. See iron-multi-selectable for example.
 
55
   */
 
56
  function mergeBehavior(newBehavior: BehaviorDescriptor): BehaviorDescriptor {
 
57
    var isBehaviorImpl = (b:string) => {
 
58
      // filter out BehaviorImpl
 
59
      return b.indexOf(newBehavior.is) === -1;
 
60
    };
 
61
    for (var i=0; i<behaviors.length; i++) {
 
62
      if (newBehavior.is !== behaviors[i].is)
 
63
        continue;
 
64
      // merge desc, longest desc wins
 
65
      if (newBehavior.desc) {
 
66
        if (behaviors[i].desc) {
 
67
          if (newBehavior.desc.length > behaviors[i].desc.length)
 
68
            behaviors[i].desc = newBehavior.desc;
 
69
        }
 
70
        else {
 
71
          behaviors[i].desc = newBehavior.desc;
 
72
        }
 
73
      }
 
74
      // merge demos
 
75
      behaviors[i].demos = (behaviors[i].demos || []).concat(newBehavior.demos || []);
 
76
      // merge events,
 
77
      behaviors[i].events = (behaviors[i].events || []).concat(newBehavior.events || []);
 
78
      behaviors[i].events = dedupe(behaviors[i].events, (e) => {return e.name});
 
79
      // merge properties
 
80
      behaviors[i].properties = (behaviors[i].properties || []).concat(newBehavior.properties || []);
 
81
      // merge observers
 
82
      behaviors[i].observers = (behaviors[i].observers || []).concat(newBehavior.observers || []);
 
83
      // merge behaviors
 
84
      behaviors[i].behaviors =
 
85
        (behaviors[i].behaviors || []).concat(newBehavior.behaviors || [])
 
86
        .filter(isBehaviorImpl);
 
87
      return behaviors[i];
 
88
    }
 
89
    return newBehavior;
 
90
  }
 
91
 
 
92
  /**
 
93
   * gets the expression representing a behavior from a node.
 
94
   */
 
95
  function behaviorExpression(node:estree.Node): estree.Node {
 
96
    switch(node.type) {
 
97
      case 'ExpressionStatement':
 
98
        // need to cast to `any` here because ExpressionStatement is super
 
99
        // super general. this code is suspicious.
 
100
        return (<any>node).expression.right;
 
101
      case 'VariableDeclaration':
 
102
        const n = <estree.VariableDeclaration>node;
 
103
        return n.declarations.length > 0 ? n.declarations[0].init : null;
 
104
    }
 
105
  }
 
106
 
 
107
  /**
 
108
   * checks whether an expression is a simple array containing only member
 
109
   * expressions or identifiers.
 
110
   */
 
111
  function isSimpleBehaviorArray(expression: estree.Node): boolean {
 
112
    if (!expression || expression.type !== 'ArrayExpression') return false;
 
113
    const arrayExpr = <estree.ArrayExpression>expression;
 
114
    for (var i=0; i < arrayExpr.elements.length; i++) {
 
115
      if (arrayExpr.elements[i].type !== 'MemberExpression' &&
 
116
          arrayExpr.elements[i].type !== 'Identifier') {
 
117
        return false;
 
118
      }
 
119
    }
 
120
    return true;
 
121
  }
 
122
 
 
123
  var templatizer = "Polymer.Templatizer";
 
124
 
 
125
  function _parseChainedBehaviors(node: estree.Node) {
 
126
    // if current behavior is part of an array, it gets extended by other behaviors
 
127
    // inside the array. Ex:
 
128
    // Polymer.IronMultiSelectableBehavior = [ {....}, Polymer.IronSelectableBehavior]
 
129
    // We add these to behaviors array
 
130
    var expression = behaviorExpression(node);
 
131
    var chained:LiteralValue[] = [];
 
132
    if (expression && expression.type === 'ArrayExpression') {
 
133
      const arrExpr = <estree.ArrayExpression>expression;
 
134
      for (var i=0; i < arrExpr.elements.length; i++) {
 
135
        if (arrExpr.elements[i].type === 'MemberExpression' ||
 
136
            arrExpr.elements[i].type === 'Identifier') {
 
137
          chained.push(astValue.expressionToValue(arrExpr.elements[i]));
 
138
        }
 
139
      }
 
140
      if (chained.length > 0)
 
141
        currentBehavior.behaviors = chained;
 
142
    }
 
143
  }
 
144
 
 
145
  function _initBehavior(node: estree.Node, getName: ()=>string) {
 
146
    var comment = esutil.getAttachedComment(node);
 
147
    var symbol = getName();
 
148
    // Quickly filter down to potential candidates.
 
149
    if (!comment || comment.indexOf('@polymerBehavior') === -1) {
 
150
      if (symbol !== templatizer) {
 
151
        return;
 
152
      }
 
153
    }
 
154
 
 
155
 
 
156
    currentBehavior = {
 
157
      type: 'behavior',
 
158
      desc: comment,
 
159
      events: esutil.getEventComments(node).map( function(event) {
 
160
        return { desc: event};
 
161
      })
 
162
    };
 
163
    propertyHandlers = declarationPropertyHandlers(currentBehavior);
 
164
 
 
165
    docs.annotateBehavior(currentBehavior);
 
166
    // Make sure that we actually parsed a behavior tag!
 
167
    if (!jsdoc.hasTag(currentBehavior.jsdoc, 'polymerBehavior') &&
 
168
        symbol !== templatizer) {
 
169
      currentBehavior = null;
 
170
      propertyHandlers = null;
 
171
      return;
 
172
    }
 
173
 
 
174
    var name = jsdoc.getTag(currentBehavior.jsdoc, 'polymerBehavior', 'name');
 
175
    currentBehavior.symbol = symbol;
 
176
    if (!name) {
 
177
      name = currentBehavior.symbol;
 
178
    }
 
179
    if (!name) {
 
180
      console.warn('Unable to determine name for @polymerBehavior:', comment);
 
181
    }
 
182
    currentBehavior.is = name;
 
183
 
 
184
    _parseChainedBehaviors(node);
 
185
 
 
186
    currentBehavior = mergeBehavior(currentBehavior);
 
187
    propertyHandlers = declarationPropertyHandlers(currentBehavior);
 
188
 
 
189
    // Some behaviors are just lists of other behaviors. If this is one then
 
190
    // add it to behaviors right away.
 
191
    if (isSimpleBehaviorArray(behaviorExpression(node))) {
 
192
      // TODO(ajo): Add a test to confirm the presence of `properties`
 
193
      if (!currentBehavior.observers) currentBehavior.observers = [];
 
194
      if (!currentBehavior.properties) currentBehavior.properties = [];
 
195
      if (behaviors.indexOf(currentBehavior) === -1)
 
196
        behaviors.push(currentBehavior);
 
197
      currentBehavior = null;
 
198
      propertyHandlers = null;
 
199
    }
 
200
  }
 
201
 
 
202
  var visitors: Visitor = {
 
203
 
 
204
    /**
 
205
     * Look for object declarations with @behavior in the docs.
 
206
     */
 
207
    enterVariableDeclaration: function(node, parent) {
 
208
      if (node.declarations.length !== 1) return;  // Ambiguous.
 
209
      _initBehavior(node, function () {
 
210
        return esutil.objectKeyToString(node.declarations[0].id);
 
211
      });
 
212
    },
 
213
 
 
214
    /**
 
215
     * Look for object assignments with @polymerBehavior in the docs.
 
216
     */
 
217
    enterAssignmentExpression: function(node, parent) {
 
218
      _initBehavior(parent, function () {
 
219
        return esutil.objectKeyToString(node.left);
 
220
      });
 
221
    },
 
222
 
 
223
    /**
 
224
     * We assume that the object expression after such an assignment is the
 
225
     * behavior's declaration. Seems to be a decent assumption for now.
 
226
     */
 
227
    enterObjectExpression: function(node, parent) {
 
228
      if (!currentBehavior || currentBehavior.properties) return;
 
229
 
 
230
      currentBehavior.properties = currentBehavior.properties || [];
 
231
      currentBehavior.observers = currentBehavior.observers || [];
 
232
      for (var i = 0; i < node.properties.length; i++) {
 
233
        var prop = node.properties[i];
 
234
        var name = esutil.objectKeyToString(prop.key);
 
235
        if (!name) {
 
236
          throw {
 
237
            message: 'Cant determine name for property key.',
 
238
            location: node.loc.start
 
239
          };
 
240
        }
 
241
        if (name in propertyHandlers) {
 
242
          propertyHandlers[name](prop.value);
 
243
        }
 
244
        else {
 
245
          currentBehavior.properties.push(esutil.toPropertyDescriptor(prop));
 
246
        }
 
247
      }
 
248
      behaviors.push(currentBehavior);
 
249
      currentBehavior = null;
 
250
    },
 
251
 
 
252
  };
 
253
 
 
254
  return {visitors, behaviors};
 
255
};