15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright 2011 The Closure Linter Authors. All Rights Reserved.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Licensed under the Apache License, Version 2.0 (the "License");
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# you may not use this file except in compliance with the License.
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# You may obtain a copy of the License at
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#      http://www.apache.org/licenses/LICENSE-2.0
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Unless required by applicable law or agreed to in writing, software
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# distributed under the License is distributed on an "AS-IS" BASIS,
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# See the License for the specific language governing permissions and
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# limitations under the License.
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Contains logic for sorting goog.provide and goog.require statements.
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Closurized JavaScript files use goog.provide and goog.require statements at the
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)top of the file to manage dependencies. These statements should be sorted
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)alphabetically, however, it is common for them to be accompanied by inline
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)comments or suppression annotations. In order to sort these statements without
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)disrupting their comments and annotations, the association between statements
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)and comments/annotations must be maintained while sorting.
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequireProvideSorter: Handles checking/fixing of provide/require statements.
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import javascripttokens
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)from closure_linter import tokenutil
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Shorthand
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Type = javascripttokens.JavaScriptTokenType
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class RequireProvideSorter(object):
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Checks for and fixes alphabetization of provide and require statements.
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  When alphabetizing, comments on the same line or comments directly above a
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  goog.provide or goog.require statement are associated with that statement and
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  stay with the statement as it gets sorted.
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CheckProvides(self, token):
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Checks alphabetization of goog.provide statements.
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Iterates over tokens in given token stream, identifies goog.provide tokens,
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    and checks that they occur in alphabetical order by the object being
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    provided.
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token: A token in the token stream before any goog.provide tokens.
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      The first provide token in the token stream.
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      None is returned if all goog.provide statements are already sorted.
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    provide_tokens = self._GetRequireOrProvideTokens(token, 'goog.provide')
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    provide_strings = self._GetRequireOrProvideTokenStrings(provide_tokens)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sorted_provide_strings = sorted(provide_strings)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if provide_strings != sorted_provide_strings:
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return provide_tokens[0]
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return None
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def CheckRequires(self, token):
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Checks alphabetization of goog.require statements.
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Iterates over tokens in given token stream, identifies goog.require tokens,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    and checks that they occur in alphabetical order by the dependency being
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    required.
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token: A token in the token stream before any goog.require tokens.
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      The first require token in the token stream.
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      None is returned if all goog.require statements are already sorted.
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    require_tokens = self._GetRequireOrProvideTokens(token, 'goog.require')
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    require_strings = self._GetRequireOrProvideTokenStrings(require_tokens)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sorted_require_strings = sorted(require_strings)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if require_strings != sorted_require_strings:
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      return require_tokens[0]
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return None
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def FixProvides(self, token):
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Sorts goog.provide statements in the given token stream alphabetically.
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token: The first token in the token stream.
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._FixProvidesOrRequires(
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._GetRequireOrProvideTokens(token, 'goog.provide'))
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def FixRequires(self, token):
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Sorts goog.require statements in the given token stream alphabetically.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token: The first token in the token stream.
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._FixProvidesOrRequires(
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self._GetRequireOrProvideTokens(token, 'goog.require'))
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _FixProvidesOrRequires(self, tokens):
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Sorts goog.provide or goog.require statements.
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tokens: A list of goog.provide or goog.require tokens in the order they
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              appear in the token stream. i.e. the first token in this list must
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              be the first goog.provide or goog.require token.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    strings = self._GetRequireOrProvideTokenStrings(tokens)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    sorted_strings = sorted(strings)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Make a separate pass to remove any blank lines between goog.require/
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # goog.provide tokens.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    first_token = tokens[0]
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    last_token = tokens[-1]
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    i = last_token
1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    while i != first_token and i is not None:
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if i.type is Type.BLANK_LINE:
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tokenutil.DeleteToken(i)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      i = i.previous
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # A map from required/provided object name to tokens that make up the line
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # it was on, including any comments immediately before it or after it on the
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # same line.
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tokens_map = self._GetTokensMap(tokens)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Iterate over the map removing all tokens.
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for name in tokens_map:
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tokens_to_delete = tokens_map[name]
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for i in tokens_to_delete:
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        tokenutil.DeleteToken(i)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # Save token to rest of file. Sorted token will be inserted before this.
1415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    rest_of_file = tokens_map[strings[-1]][-1].next
1425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Re-add all tokens in the map in alphabetical order.
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    insert_after = tokens[0].previous
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for string in sorted_strings:
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for i in tokens_map[string]:
1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if rest_of_file:
1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          tokenutil.InsertTokenBefore(i, rest_of_file)
1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        else:
1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          tokenutil.InsertTokenAfter(i, insert_after)
1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          insert_after = i
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GetRequireOrProvideTokens(self, token, token_string):
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Gets all goog.provide or goog.require tokens in the given token stream.
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token: The first token in the token stream.
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token_string: One of 'goog.provide' or 'goog.require' to indicate which
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    tokens to find.
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      A list of goog.provide or goog.require tokens in the order they appear in
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      the token stream.
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tokens = []
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while token:
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if token.type == Type.IDENTIFIER:
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if token.string == token_string:
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          tokens.append(token)
1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        elif token.string not in [
1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)            'goog.provide', 'goog.require', 'goog.setTestOnly']:
1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          # These 3 identifiers are at the top of the file. So if any other
1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          # identifier is encountered, return.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          break
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token = token.next
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return tokens
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GetRequireOrProvideTokenStrings(self, tokens):
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Gets a list of strings corresponding to the given list of tokens.
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    The string will be the next string in the token stream after each token in
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tokens. This is used to find the object being provided/required by a given
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    goog.provide or goog.require token.
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tokens: A list of goog.provide or goog.require tokens.
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      A list of object names that are being provided or required by the given
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      list of tokens. For example:
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ['object.a', 'object.c', 'object.b']
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    token_strings = []
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for token in tokens:
1975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if not token.is_deleted:
1985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        name = tokenutil.GetStringAfterToken(token)
1995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        token_strings.append(name)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return token_strings
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GetTokensMap(self, tokens):
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Gets a map from object name to tokens associated with that object.
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Starting from the goog.provide/goog.require token, searches backwards in the
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    token stream for any lines that start with a comment. These lines are
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    associated with the goog.provide/goog.require token. Also associates any
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tokens on the same line as the goog.provide/goog.require token with that
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    token.
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tokens: A list of goog.provide or goog.require tokens.
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      A dictionary that maps object names to the tokens associated with the
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      goog.provide or goog.require of that object name. For example:
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      {
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'object.a': [JavaScriptToken, JavaScriptToken, ...],
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        'object.b': [...]
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      The list of tokens includes any comment lines above the goog.provide or
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      goog.require statement and everything after the statement on the same
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      line. For example, all of the following would be associated with
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      'object.a':
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      /** @suppress {extraRequire} */
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      goog.require('object.a'); // Some comment.
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tokens_map = {}
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for token in tokens:
2335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      object_name = tokenutil.GetStringAfterToken(token)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # If the previous line starts with a comment, presume that the comment
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # relates to the goog.require or goog.provide and keep them together when
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # sorting.
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      first_token = token
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      previous_first_token = tokenutil.GetFirstTokenInPreviousLine(first_token)
2395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      while (previous_first_token and
2405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)             previous_first_token.IsAnyType(Type.COMMENT_TYPES)):
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        first_token = previous_first_token
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        previous_first_token = tokenutil.GetFirstTokenInPreviousLine(
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            first_token)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # Find the last token on the line.
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_token = tokenutil.GetLastTokenInSameLine(token)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      all_tokens = self._GetTokenList(first_token, last_token)
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      tokens_map[object_name] = all_tokens
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return tokens_map
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def _GetTokenList(self, first_token, last_token):
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """Gets a list of all tokens from first_token to last_token, inclusive.
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      first_token: The first token to get.
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      last_token: The last token to get.
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Returns:
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      A list of all tokens between first_token and last_token, including both
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      first_token and last_token.
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Raises:
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Exception: If the token stream ends before last_token is reached.
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    token_list = []
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    token = first_token
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while token != last_token:
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if not token:
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        raise Exception('ran out of tokens')
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token_list.append(token)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      token = token.next
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    token_list.append(last_token)
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return token_list
2765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def GetFixedRequireString(self, token):
2785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """Get fixed/sorted order of goog.require statements.
2795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Args:
2815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      token: The first token in the token stream.
2825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Returns:
2845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      A string for correct sorted order of goog.require.
2855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """
2865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return self._GetFixedRequireOrProvideString(
2875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._GetRequireOrProvideTokens(token, 'goog.require'))
2885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def GetFixedProvideString(self, token):
2905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """Get fixed/sorted order of goog.provide statements.
2915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Args:
2935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      token: The first token in the token stream.
2945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
2955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Returns:
2965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      A string for correct sorted order of goog.provide.
2975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """
2985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return self._GetFixedRequireOrProvideString(
2995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        self._GetRequireOrProvideTokens(token, 'goog.provide'))
3005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  def _GetFixedRequireOrProvideString(self, tokens):
3025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """Sorts goog.provide or goog.require statements.
3035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Args:
3055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      tokens: A list of goog.provide or goog.require tokens in the order they
3065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              appear in the token stream. i.e. the first token in this list must
3075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              be the first goog.provide or goog.require token.
3085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    Returns:
3105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      A string for sorted goog.require or goog.provide statements
3115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    """
3125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # A map from required/provided object name to tokens that make up the line
3145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # it was on, including any comments immediately before it or after it on the
3155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    # same line.
3165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    tokens_map = self._GetTokensMap(tokens)
3175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    sorted_strings = sorted(tokens_map.keys())
3185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    new_order = ''
3205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for string in sorted_strings:
3215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      for i in tokens_map[string]:
3225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        new_order += i.string
3235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if i.IsLastInLine():
3245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          new_order += '\n'
3255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return new_order
327