text:
"""

DATA MODELS

This file is responsible for models and business logic. In
here, all methods should handle:

    - permission checks
      Check user for each operation.

    - basic operations
      Get, put, delete. All of those go in here.

    - queries
      Search queries all go in here.

Methods in here should try to throw informational BadValueErrors
upon failure.

Parsing web arguments and error handling go in api.py.

Specification: https://github.com/Cal-CS-61A-Staff/ok/wiki/Models
"""

#pylint: disable=no-member, unused-argument, too-many-return-statements

import datetime
import itertools
import logging

from app import app
from app.constants import STUDENT_ROLE, STAFF_ROLE, VALID_ROLES
from app.exceptions import *
from app import utils
from flask import json
from flask.json import JSONEncoder as old_json

from google.appengine.ext import ndb

class JSONEncoder(old_json):
    """
    Wrapper class to try calling an object's to_dict() method. This allows
    us to JSONify objects coming from the ORM. Also handles dates & datetimes.
    """
    def default(self, obj):
        if isinstance(obj, ndb.Key):
            got = obj.get()
            if not got:
                return None
            return got.to_json()
        elif isinstance(obj, datetime.datetime):
            obj = convert_timezone(obj)
            return obj.strftime(app.config["GAE_DATETIME_FORMAT"])
        if isinstance(obj, ndb.Model):
            return obj.to_json()
        return super(JSONEncoder, self).default(obj)

app.json_encoder = JSONEncoder

def convert_timezone(utc_dt):
    """Convert times to Pacific time."""
    # This looks like a hack... is it even right? What about daylight savings?
    # Correct approach: each course should have a timezone. All times should be
    # stored in UTC for easy comparison. Dates should be converted to
    # course-local time when displayed.
    delta = datetime.timedelta(hours=-7)
    return datetime.datetime.combine(utc_dt.date(), utc_dt.time()) + delta

class Base(ndb.Model):
    """Shared utility methods and properties."""
    created = ndb.DateTimeProperty(auto_now_add=True)

    @classmethod
    def from_dict(cls, values):
        """Creates an instance from the given values."""
        inst = cls()
        inst.populate(**values)
        return inst

    @classmethod
    def _get_kind(cls):
        return cls.__name__ + 'v2'

    def to_json(self, fields=None):
        """Converts this model to a json dictionary."""
        if fields == True:
            return self.to_dict()
        elif fields == False:
            return {}

        if not fields:
            fields = {}
        if fields:
            result = self.to_dict(include=fields.keys())
        else:
            result = self.to_dict()

        if self.key and (not fields or 'id' in fields):
            result['id'] = self.key.id()

        # Protect sensitive fields: autograding_key
        # Previous sensitive fields: zip_file_url and script
        sensitive_fields = ['autograding_key',
            'zip_file_url', 'grading_script_file']
        for sensitive in sensitive_fields:
            if sensitive in result:
                result[sensitive] = ''

        for key, value in result.items():
            if isinstance(value, ndb.Key):
                value = value.get()
                if value:
                    result[key] = value.to_json(fields.get(key))
                else:
                    result[key] = None
            elif isinstance(value, list) and len(value) > 0 and isinstance(value[0], ndb.Key):
                new_list = []
                for value in value:
                    fields_key = fields.get(key)
                    if fields_key and not isinstance(fields_key, dict):
                        if fields.get(key):
                            new_list.append(value)
                    else:
                        value = value.get()
                        if value:
                            new_list.append(value.to_json(fields.get(key)))
                result[key] = new_list
            else:
                try:
                    new_value = app.json_encoder().default(value)
                    result[key] = new_value
                except TypeError:
                    pass
        return result

    @classmethod
    def can(cls, user, need, obj=None, query=None):
        """Whether user satisfies the given need for this object.

        The index action requires a query that gets filtered and returned.
        """
        if need.action == "index":
            assert query, "No query for index"
        need.set_object(obj or cls)
        return cls._can(user, need, obj, query)

    @classmethod
    def _can(cls, user, need, obj, query):
        """
        The internal permissions method. Overridden by subclasses.
        """
        return False

def make_num_counter(helper):
    def wrapper(self, assignment, max_size=None):
        count = 0

        all_submissions = helper(self, assignment)
        if max_size is not None:
            left_to_count = max_size
            for submission_query in all_submissions:
                if count >= max_size:
                    break
                diff = submission_query.count(left_to_count)
                left_to_count -= diff
                count += diff
        else:
            for submission_query in all_submissions:
                count += submission_query.count(max_size)

        return count
    return wrapper

class User(Base):
    """Users may have multiple email addresses. Note: the built-in user model
    in appengine associates a different user object with each email.
    """
    email = ndb.StringProperty(repeated=True)
    is_admin = ndb.BooleanProperty(default=False)
    # TODO add a name
    # TODO add a student ID

    @property
    def logged_in(self):
        return self.email != ["_anon"]

    def append_email(self, email):
        if email not in self.email:
            self.email.append(email)

    def delete_email(self, email):
        if email in self.email and len(self.email) > 1:
            self.email.remove(email)

    def final_submission(self, group, assignment_key):
        """A query for the final submission of a group and assignment."""
        if isinstance(assignment_key, Assignment):
            assignment_key = assignment_key.key
        if group and self.key in group.member:
            return FinalSubmission.query(
                FinalSubmission.assignment==assignment_key,
                FinalSubmission.group==group.key)
        else:
            return FinalSubmission.query(
                FinalSubmission.assignment==assignment_key,
                FinalSubmission.submitter==self.key)

    def get_final_submission(self, assignment_key):
        """Get the current final submission for this user."""
        group = self.get_group(assignment_key)
        return self.final_submission(group, assignment_key).get()

    def _contains_files(self, backup):
        messages = backup.get_messages()
        if 'file_contents' in messages:
            return messages['file_contents']

    def members(self, group):
        """The members of a group. If group is None, then we are its only member."""
        if not group or self.key not in group.member:
            return [self.key]
        else:
            return group.member

    def backups(self, group, assignment):
        """A query that fetches all backups for a group and assignment."""
        members = self.members(group)
        return Backup.query(
            Backup.submitter.IN(members),
            Backup.assignment == assignment
        ).order(-Backup.server_time)

    def get_backups(self, assignment, num_backups=10):
        group = self.get_group(assignment)
        return self.backups(group, assignment).fetch(num_backups)

    def submissions(self, group, assignment):
        """A query that fetches all backups for a group and assignment."""
        members = self.members(group)
        return Submission.query(
            Submission.submitter.IN(members),
            Submission.assignment == assignment
        ).order(-Backup.server_time)

    def get_submissions(self, assignment, num_submissions=10):
        group = self.get_group(assignment)
        return self.submissions(group, assignment).fetch(num_submissions)

    def group(self, assignment_key):
        """Return a query that fetches the group for this user for an assignment."""
        if isinstance(assignment_key, Assignment):
            assignment_key = assignment_key.key
        return Group.query(ndb.OR(Group.member==self.key,
                                  Group.invited==self.key),
                           Group.assignment==assignment_key)

    def get_group(self, assignment_key):
        """Return the group for this user for an assignment."""
        return self.group(assignment_key).get()

    def get_course_info(self, course):
        if not course:
            raise BadValueError("Invalid course")

        @ndb.tasklet
        def final_submission_info(group, assignment):
            final_submission = yield self.final_submission(group, assignment.key).get_async()
            if final_submission:
                submission = yield final_submission.submission.get_async()
                backup = yield submission.backup.get_async()
                final_info = {
                    'submission': submission,
                    'backup': backup,
                    'final_submission': final_submission
                }
                if final_submission.revision:
                    revision = yield final_submission.revision.get_async()
                    final_info['revision'] = revision
            else:
                final_info = {}
            raise ndb.Return(final_info)

        @ndb.tasklet
        def assignment_info(assignment):
            group = self.group(assignment.key).get()
            final_info, num_backups, num_submissions = yield (
                final_submission_info(group, assignment),
                self.backups(group, assignment.key).count_async(1),
                self.submissions(group, assignment.key).count_async(1))
            raise ndb.Return({
                'group': {
                    'group_info': group,
                    'invited': group and self.key in group.invited
                },
                'final': final_info,
                'backups': num_backups > 0,
                'submissions': num_submissions > 0,
                'assignment': assignment
            })

        assignments = course.assignments.order(-Assignment.due_date).map(assignment_info)

        return {
            'assignments': assignments,
            'user': self
        }

    def update_final_submission(self, assignment, group=None):
        """Update the final submission of the user and its group.
        Call on all users after group changes.
        """
        if isinstance(assignment, Assignment):
            assignment = assignment.key

        options = [FinalSubmission.submitter == self.key]
        if not group:
            group = self.get_group(assignment)
        if group and self.key in group.member:
            options.append(FinalSubmission.group == group.key)
            options += [FinalSubmission.submitter == m for m in group.member]
        who = options[0] if len(options) == 1 else ndb.OR(*options)
        assigned = FinalSubmission.assignment == assignment
        finals = FinalSubmission.query(assigned, who).fetch()

        if finals:
            # Keep the most recent and delete the rest.
            # Note: Deleting a FinalSubmission does not delete its submission.
            finals.sort(key=lambda final: final.submission.get().server_time)
            old, latest = finals[:-1], finals[-1]
            latest.group = group.key if group else None
            latest.put()
            for final in old:
                final.key.delete()
        else:
            # Create a final submission for user from her latest submission.
            subs = Submission.query(
                Submission.submitter == self.key,
                Submission.assignment == assignment)
            latest = subs.order(-Submission.server_time).get()
            if latest:
                FinalSubmission(assignment=assignment,
                                group=group.key if group else None,
                                submission=latest.key).put()

    #@ndb.transactional
    @classmethod
    def get_or_insert(cls, email):
        """Retrieve a user by email or create that user."""
        email = email.lower()
        user = cls.lookup(email)
        if user:
            return user
        else:
            user = cls(email=[email])
            user.put()
            return user

    @classmethod
    def lookup(cls, email):
        """Retrieve a user by email or return None."""
        assert isinstance(email, (str, unicode)), "Invalid email: " + str(email)
        email = email.lower()
        users = cls.query(cls.email == email).fetch()
        if not users:
            return None
        if len(users) > 1:
            pass # TODO Decide how to handle non-unique users
        return users[0]

    @classmethod
    def _can(cls, user, need, obj, query):
        if not user.logged_in:
            return False
        if user.is_admin:
            if need.action == "index":
                return query
            return True

        if need.action == "lookup":
            return True
        elif need.action == "merge":
            # TODO(soumya) figure out how to make permissions for this
            return False
        if need.action == "get":
            if not obj or not isinstance(obj, User):
                return False
            elif obj.key == user.key:
                return True
            else:
                for part in Participant.courses(user, STAFF_ROLE):
                    course = part.course
                    if Participant.has_role(obj, course, STUDENT_ROLE):
                        return True
                return False
        elif need.action == "index":
            # TODO Update documentation: users can only index themselves.
            #      See Participant for listing users by course
            return query.filter(User.key == user.key)
        else:
            return False

    def _pre_put_hook(self):
        """Ensure that a user can be accessed by at least one email."""
        if not self.email:
            raise BadValueError("No email associated with " + str(self))
        #utils.check_user(self.key.id())

    def scores_for_assignment(self, assignment):
        """ Returns a tuple of two elements:
                1) Score data (list of lists) for STUDENT's final submission for ASSIGNMENT.
                    There is an element for each score.
                    * OBS * If the student is in a group, the list will contain an
                    element for each combination of group member and score.
                2) A boolean indicating whether the student had a
                    scored final submission for ASSIGNMENT.
            Format: [['STUDENT', 'SCORE', 'MESSAGE', 'GRADER', 'TAG']]
        """
        fs = self.get_final_submission(assignment.key)
        scores = []
        if fs:
            scores = fs.get_scores()
        return (scores, True) if scores else ([[self.email[0], 0, None, None, None]], False)

class Course(Base):
    """Courses are expected to have a unique offering."""
    offering = ndb.StringProperty() # E.g., 'cal/cs61a/fa14'
    institution = ndb.StringProperty() # E.g., 'UC Berkeley'
    display_name = ndb.StringProperty()
    instructor = ndb.KeyProperty(User, repeated=True)
    active = ndb.BooleanProperty(default=True)

    @property
    def staff(self):
        """
        Returns all the staff of this course.
        """
        return [part.user for part in Participant.query(
            Participant.course == self.key,
            Participant.role == STAFF_ROLE).fetch()]

    @classmethod
    def _can(cls, user, need, course, query):
        action = need.action
        if action == "get":
            return True
        elif action == "index":
            return query
        elif action == "modify":
            return bool(course) and user.key in course.staff
        elif action == "staff":
            if user.is_admin:
                return True
            return user.key in course.staff
        elif action == "create":
            return user.is_admin
        return False

    @property
    def assignments(self):
        """Return a query for assignments."""
        return Assignment.query(Assignment.course == self.key)

    def get_students(self, user):

        query = Participant.query(
            Participant.course == self.key,
            Participant.role == 'student')

        return list(query.fetch())

class Assignment(Base):
    """Assignments are particular to courses and have unique names."""
    name = ndb.StringProperty() # E.g., cal/cs61a/fa14/proj1
    display_name = ndb.StringProperty()
    url = ndb.StringProperty()
    points = ndb.FloatProperty()
    templates = ndb.JsonProperty()
    creator = ndb.KeyProperty(User)
    course = ndb.KeyProperty(Course)
    max_group_size = ndb.IntegerProperty()
    due_date = ndb.DateTimeProperty()
    lock_date = ndb.DateTimeProperty() # no submissions after this date
    active = ndb.ComputedProperty(
        lambda a: a.due_date and datetime.datetime.now() <= a.due_date)
    revision = ndb.BooleanProperty(default=False)
    autograding_key = ndb.StringProperty()
    autograding_enabled = ndb.BooleanProperty(default=False)

    # TODO Add services requested

    @classmethod
    def _can(cls, user, need, obj, query):
        if need.action == "index":
            return query
        if user.is_admin:
            return True
        if need.action == "get":
            return True
        elif need.action in ["grade", 'delete', 'create', 'put', 'staff']:
            if obj and isinstance(obj, Assignment):
                return Participant.has_role(user, obj.course, STAFF_ROLE)
        return False

    def __lt__(self, other):
        """ Allows us to sort assignments - reverse order so that latest due dates come first """
        return self.due_date > other.due_date

class Participant(Base):
    """Tracks participation of students & staff in courses."""
    user = ndb.KeyProperty(User)
    course = ndb.KeyProperty(Course)
    role = ndb.StringProperty() # See constants.py for roles

    @classmethod
    def _can(cls, user, need, course, query):
        action = need.action
        if action == "get":
            return True
        elif action == "staff":
            if user.is_admin:
                return True
            return user.key in course.staff
        elif action == "index":
            if cls.has_role(user, course, STAFF_ROLE):
                return query.filter(cls.course == course.key)
            else:
                return query.filter(cls.user == user.key)

    @classmethod
    def add_role(cls, user_key, course_key, role):
        if role not in VALID_ROLES:
            raise BadValueError("Bad role: " + str(role))
        if isinstance(user_key, User):
            user_key = user_key.key
        if isinstance(course_key, Course):
            course_key = course_key.key

        query = cls.query(cls.user == user_key,
                          cls.course == course_key,
                          cls.role == role)
        current = query.get()
        if not current:
            Participant(user=user_key, course=course_key, role=role).put()

    @classmethod
    def remove_role(cls, user_key, course_key, role):
        if role not in VALID_ROLES:
            raise BadValueError("Bad role: " + str(role))
        if isinstance(user_key, User):
            user_key = user_key.key
        if isinstance(course_key, Course):
            course_key = course_key.key
        query = cls.query(cls.user == user_key,
                          cls.course == course_key,
                          cls.role == role)
        current = query.get()
        if current:
            current.key.delete()

    @classmethod
    def has_role(cls, user_key, course_key, role):
        if isinstance(user_key, User):
            user_key = user_key.key
        if isinstance(course_key, Course):
            course_key = course_key.key
        query = cls.query(cls.user == user_key,
                          cls.course == course_key,
                          cls.role == role)
        return query.get() is not None

    @classmethod
    def courses(cls, user_key, role=None):
        if isinstance(user_key, User):
            user_key = user_key.key
        query = cls.query(cls.user == user_key)
        if role:
            query = query.filter(cls.role == role)
        return query.fetch()

def validate_messages(_, message_str):
    """message_str is a JSON string encoding a map from protocols to data."""
    if not message_str:
        raise BadValueError('Empty messages')
    try:
        messages = json.loads(message_str)
        if not isinstance(messages, dict):
            raise BadValueError('messages is not a JSON map')
    except Exception as exc:
        raise BadValueError(exc)

class Message(Base):
    """A message given to us from the client (e.g., the contents of files)."""
    contents = ndb.JsonProperty()
    kind = ndb.StringProperty()

    @classmethod
    def _can(cls, user, need, obj=None, query=None):
        action = need.action

        if action == "index":
            return False

        return Backup._can(user, need, obj, query)

def disjunction(query, filters):
    """Return a query in which at least one filter is true."""
    assert filters, "No filters"
    if len(filters) > 1:
        return query.filter(ndb.OR(*filters)) #pylint: disable=star-args
    else:
        return query.filter(filters[0])

class Backup(Base):
    """A backup is sent each time a student runs the client."""
    submitter = ndb.KeyProperty(User)
    assignment = ndb.KeyProperty(Assignment)
    client_time = ndb.DateTimeProperty()
    server_time = ndb.DateTimeProperty(auto_now_add=True)
    messages = ndb.StructuredProperty(Message, repeated=True)
    tags = ndb.StringProperty(repeated=True)

    def get_messages(self, fields=None):
        """Returns self.messages formatted as a dictionary.

        fields: The selected fields of the dictionary.
        """

        if not fields:
            fields = {}

        # TODO What does this do and why? Please add a comment.
        message_fields = fields.get('messages', {})
        if isinstance(message_fields, (str, unicode)):
            message_fields = message_fields == "true"

        messages = {m.kind: m.contents for m in self.messages}
        def test(item):
            if isinstance(message_fields, bool):
                return message_fields

            if not message_fields:
                return True
            return item in message_fields

        def get_contents(kind, contents):
            if isinstance(message_fields, bool):
                return contents

            if message_fields.get(kind) == "presence":
                return True
            return contents

        return {
            kind: get_contents(kind, contents)
            for kind, contents in messages.iteritems()
            if test(kind)}

    @property
    def group(self):
        return Group.lookup(self.submitter, self.assignment)

    def to_json(self, fields=None):
        json = super(Backup, self).to_json(fields)
        if 'messages' in json:
            json['messages'] = self.get_messages(fields)
        return json

    @classmethod
    def _can(cls, user, need, backup, query):
        """A user can access a backup as staff or through a group."""
        action = need.action
        if action == "get":
            if not backup or not isinstance(backup, Backup):
                raise ValueError("Need Backup instance for get action.")
            if user.is_admin or backup.submitter == user.key:
                return True
            course_key = backup.assignment.get().course
            if Participant.has_role(user, course_key, STAFF_ROLE):
                return True
            group = backup.group
            return bool(group and user.key in group.member)
        if action in ("create", "put"):
            return user.logged_in and user.key == backup.submitter
        if action == "index":
            if not user.logged_in:
                return False
            filters = [Backup.submitter == user.key]
            staff_list = Participant.courses(user, STAFF_ROLE)
            if user.key in [part.user for part in staff_list]:
                for participant in staff_list:
                    course = participant.course
                    assigns = Assignment.query(Assignment.course == course).fetch()
                    if assigns:
                        filters.append(
                            Backup.assignment.IN([a.key for a in assigns]))
            grp = backup and backup.group
            if grp and user.key in grp.member:
                filters.append(ndb.AND(
                    Backup.submitter.IN(grp.member),
                    Backup.assignment == grp.assignment))
            return disjunction(query, filters)
        return False

class Score(Base):
    """The score for a submission, either from a grader or autograder."""
    tag = ndb.TextProperty() # E.g., "Partner 0" or "composition"
    score = ndb.IntegerProperty()
    message = ndb.TextProperty() # Plain text
    grader = ndb.KeyProperty(User) # For autograders, the user who authenticated
    server_time = ndb.DateTimeProperty(auto_now_add=True)

class Submission(Base):
    """A backup that may be scored."""
    backup = ndb.KeyProperty(Backup)
    score = ndb.StructuredProperty(Score, repeated=True)
    submitter = ndb.ComputedProperty(lambda x: x.backup.get().submitter)
    assignment = ndb.ComputedProperty(lambda x: x.backup.get().assignment)
    server_time = ndb.DateTimeProperty(auto_now_add=True)
    is_revision = ndb.BooleanProperty(default=False)

    def get_final(self):
        assignment = self.assignment
        # I have no idea why this works... need it to pass tests
        group = self.submitter.get().get_group(assignment)
        submitter = self.submitter
        if group:
            final = FinalSubmission.query(
                FinalSubmission.assignment==assignment,
                FinalSubmission.group==group.key).get()
        else:
            final = FinalSubmission.query(
                FinalSubmission.assignment==assignment,
                FinalSubmission.submitter==submitter).get()
        return final

    def mark_as_final(self):
        """Create or update a final submission."""
        final = self.get_final()
        if final:
            assignment = self.assignment.get()
            if assignment.revision:
                # Follow resubmssion procedure
                final.revision = self.key
            else:
                final.submitter = self.submitter
                final.submission = self.key
        else:
            group = self.submitter.get().get_group(self.assignment)
            final = FinalSubmission(
                assignment=self.assignment, submission=self.key)
            if group:
                final.group = group.key
        return final.put()

    def resubmit(self, user_key):
        """
        Resubmits this submission as being submitted by |user|.
        """
        old_backup = self.backup.get()
        new_backup = Backup(
            submitter=user_key,
            assignment=self.assignment,
            client_time=old_backup.client_time,
            server_time=old_backup.server_time,
            messages=old_backup.messages,
            tags=old_backup.tags)
        new_backup_key = new_backup.put()
        new_subm = Submission(
            backup=new_backup_key,
            score=self.score,
            server_time=self.server_time)
        new_subm_key = new_subm.put_async()

        final = self.get_final()
        if final:
            final.submitter = user_key
            final.submission = new_subm_key.get_result()
            final.put()

    @classmethod
    def _can(cls, user, need, submission, query):
        if need.action == "grade":
            if not submission or not isinstance(submission, Submission):
                raise ValueError("Need Submission instance for grade action")
            if user.is_admin:
                return True
            course_key = submission.assignment.get().course
            return Participant.has_role(user, course_key, STAFF_ROLE)
        return Backup._can(user, need, submission.backup.get() if submission else None, query)

class Diff(Base):
    """A diff between two versions of the same project, with comments.
    A diff has three types of lines: insertions, deletions, and matches.
    Every insertion line is associated with a diff line.
    """
    before = ndb.KeyProperty(Backup) # Set to None to compare to template
    after = ndb.KeyProperty(Backup)
    diff = ndb.JsonProperty()

    @property
    def comments(self):
        """
        Returns all the comments for this diff.
        """
        return Comment.query(ancestor=self.key).order(Comment.created)

    def to_json(self, fields=None):
        data = super(Diff, self).to_json(fields)
        comments = list(self.comments)
        all_comments = {}
        for comment in comments:
            file_comments = all_comments.setdefault(comment.filename, {})
            file_comments.setdefault(comment.line, []).append(comment)

        data['comments'] = all_comments
        return data

class Comment(Base):
    """A comment is part of a diff. The key has the diff as its parent."""
    author = ndb.KeyProperty(User)
    diff = ndb.KeyProperty(Diff)
    filename = ndb.StringProperty()
    line = ndb.IntegerProperty()
    # TODO Populate submission_line so that when diffs are changed, comments
    #      don't move around.
    submission_line = ndb.IntegerProperty()
    message = ndb.TextProperty() # Markdown

    @classmethod
    def _can(cls, user, need, comment=None, query=None):
        if user.is_admin:
          return True
        if need.action in ["get", "modify", "delete"]:
            return comment.author == user.key
        return False

class Version(Base):
    """A version of client-side resources. Used for auto-updating."""
    name = ndb.StringProperty(required=True)
    versions = ndb.StringProperty(repeated=True)
    current_version = ndb.StringProperty()
    base_url = ndb.StringProperty(required=True)

    def download_link(self, version=None):
        if version is None:
            if not self.current_version:
                raise BadValueError("current version doesn't exist")
            return '/'.join((self.base_url, self.current_version,
                             self.name))
        if version not in self.versions:
            raise BadValueError("specified version %s doesn't exist" % version)
        return '/'.join((self.base_url, version, self.name))

    def to_json(self, fields=None):
        converted = super(Version, self).to_json(fields)
        if self.current_version:
            converted['current_download_link'] = self.download_link()

        return converted

    @classmethod
    def _can(cls, user, need, obj=None, query=None):
        action = need.action

        if action == "delete":
            return False
        if action == "index":
            return query
        if action == "get":
            return True
        return user.is_admin

    @classmethod
    def from_dict(cls, values):
        """Creates an instance from the given values."""
        if 'name' not in values:
            raise ValueError("Need to specify a name")
        inst = cls(key=ndb.Key(cls._get_kind(), values['name']))
        inst.populate(**values) #pylint: disable=star-args
        return inst

    @classmethod
    def get_or_insert(cls, key, **kwargs):
        assert not isinstance(id, int), "Only string keys allowed for versions"
        kwargs['name'] = key
        return super(cls, Version).get_or_insert(key, **kwargs)

    @classmethod
    def get_by_id(cls, key, **kwargs):
        assert not isinstance(id, int), "Only string keys allowed for versions"
        return super(cls, Version).get_by_id(key, **kwargs)

class Group(Base):
    """A group is a collection of users who are either members or invited.

    Members of a group can view each other's submissions.

    Specification:
    https://github.com/Cal-CS-61A-Staff/ok/wiki/Group-&-Submission-Consistency
    """
    member = ndb.KeyProperty(User, repeated=True)
    invited = ndb.KeyProperty(User, repeated=True)
    assignment = ndb.KeyProperty(Assignment, required=True)
    order = ndb.StringProperty()

    @classmethod
    def lookup(cls, user_key, assignment_key):
        """Return the group for a user key."""
        if isinstance(user_key, User):
            user_key = user_key.key
        if isinstance(assignment_key, Assignment):
            assignment_key = assignment_key.key
        return Group.query(ndb.OR(Group.member == user_key,
                                  Group.invited == user_key),
                           Group.assignment == assignment_key).get()

    @classmethod
    def _lookup_or_create(cls, user_key, assignment_key):
        """Retrieve a group for user or create a group. Group is *not* put."""
        group = cls.lookup(user_key, assignment_key)
        if group:
            return group
        if isinstance(user_key, User):
            user_key = user_key.key
        if isinstance(assignment_key, Assignment):
            assignment_key = assignment_key.key
        return Group(member=[user_key], invited=[], assignment=assignment_key)

    @classmethod
    def lookup_by_assignment(cls, assignment):
        """ Returns all groups with the given assignment """
        if isinstance(assignment, Assignment):
            assign_key = assignment.key
        return Group.query(Group.assignment == assign_key).fetch()

    #@ndb.transactional
    def invite(self, email):
        """Invites a user to the group. Returns an error message or None."""
        if isinstance(email, ndb.Key):
            user = email.get()
            email = user.email[0]
        else:
            user = User.lookup(email)
        if not user:
            return "{} is not a valid user".format(email)
        course = self.assignment.get().course
        if not Participant.has_role(user, course, STUDENT_ROLE):
            return "{} is not enrolled in {}".format(email, course.get().display_name)
        if user.key in self.invited:
            return '{} has already been invited'.format(email)
        if user.key in self.member:
            return "{} is already in the group".format(email)
        has_user = ndb.OR(Group.member == user.key, Group.invited == user.key)
        if Group.query(has_user, Group.assignment == self.assignment).get():
            return "{} is already in some other group".format(email)
        max_group_size = self.assignment.get().max_group_size
        total_member = len(self.member) + len(self.invited)
        if total_member + 1 > max_group_size:
            return "The group is full"
        self.invited.append(user.key)
        self.put()
        for member in self.member:
            member.get().update_final_submission(self.assignment, self)

    #@ndb.transactional
    @classmethod
    def invite_to_group(cls, user_key, email, assignment_key):
        """User invites email to join his/her group. Returns error or None."""
        group = cls._lookup_or_create(user_key, assignment_key)
        if isinstance(user_key, User):
            user_key = user_key.key
        if isinstance(assignment_key, Assignment):
            assignment_key = assignment_key.key
        AuditLog(
            event_type='Group.invite',
            user=user_key,
            assignment=assignment_key,
            description='Added {} to group'.format(email),
            obj=group.key,
        ).put()
        return group.invite(email)

    #@ndb.transactional
    def accept(self, user_key):
        """User accepts an invitation to join. Returns error or None."""
        if isinstance(user_key, User):
            user_key = user_key.key
        if user_key not in self.invited:  # Note: these will never happen, according to _can
            return "That user is not invited to the group"
        if user_key in self.member:
            return "That user has already accepted."
        self.invited.remove(user_key)
        self.member.append(user_key)
        self.put()
        for user_key in self.member:
            user_key.get().update_final_submission(self.assignment, self)

    #@ndb.transactional
    def exit(self, user_key):
        """User leaves the group. Empty/singleton groups are deleted."""
        if isinstance(user_key, User):
            user_key = user_key.key
        if user_key not in self.member and user_key not in self.invited:
            return 'That user is not in this group and has not been invited.'
        for users in [self.member, self.invited]:
            if user_key in users:
                users.remove(user_key)

        error = self.validate()
        if error:
            subms = FinalSubmission.query(
                FinalSubmission.group==self.key
            ).fetch()
            for subm in subms:
                subm.group = None
                subm.put()
            self.key.delete()
        else:
            self.put()

        for user in self.member + [user_key]:
            user.get().update_final_submission(self.assignment)

    @classmethod
    def _can(cls, user, need, group, query):
        action = need.action
        if not user.logged_in:
            return False

        if action == "index":
            if user.is_admin:
                return query
            return query.filter(ndb.OR(Group.member == user.key,
                                       Group.invited == user.key))

        if user.is_admin:
            return True
        if not group:
            return False
        if action in ("get", "exit"):
            return user.key in group.member or user.key in group.invited
        elif action in ("invite", "remove", "reorder"):
            return user.key in group.member
        elif action in "accept":
            return user.key in group.invited
        return False

    def validate(self):
        """Return an error string if group is invalid."""
        max_group_size = self.assignment.get().max_group_size
        total_member = len(self.member) + len(self.invited)
        if max_group_size and total_member > max_group_size:
            sizes = (total_member, max_group_size)
            return "%s member found; at most %s allowed" % sizes
        if total_member < 2:
            return "No group can have %s total member" % total_member
        if not self.member:
            return "A group must have an active member"

    def _pre_put_hook(self):
        """Ensure that the group is well-formed before put."""
        error = self.validate()
        if error:
            raise BadValueError(error)

    def scores_for_assignment(self, assignment):
        """ Returns a list of lists containing score data
            for the groups's final submission for ASSIGNMENT.
            There is one element for each combination of
            group member and score.
            Ensures that each student only appears once in the list.
            Format: [['STUDENT', 'SCORE', 'MESSAGE', 'GRADER', 'TAG']]
        """
        content = []
        member = self.member[0].get()

        for m in self.member:
            member = m.get()
            if member:
              data, success = member.scores_for_assignment(assignment)
              content.extend(data)
              if success:
                  # get_scores_for_student_or_group will return scores for all group members.
                  return content

        # Handle the case where the member key no longer exists.
        if not member:
          return [["Unknown-"+str(self.member[0]), 0, None, None, None]]

        return [[member.email[0], 0, None, None, None]]

class AuditLog(Base):
    """Keeps track of Group changes that are happening. That way, we can stop
    cases of cheating by temporary access. (e.g. A is C's partner for 10 min
    so A can copy off of C)."""
    event_type = ndb.StringProperty()
    user = ndb.KeyProperty(User)
    assignment = ndb.KeyProperty(Assignment)
    description = ndb.StringProperty()
    obj = ndb.KeyProperty()

class Queue(Base):
    """A queue of submissions to grade."""
    assignment = ndb.KeyProperty(Assignment)
    course = ndb.ComputedProperty(lambda q: q.assignment.get().course)
    assigned_staff = ndb.KeyProperty(User, repeated=True)
    owner = ndb.KeyProperty(User)

    @property
    def submissions(self):
        """
        Returns all the submissions in this queue.
        """
        query = FinalSubmission.query().filter(FinalSubmission.queue == self.key)
        return [fs for fs in query]

    @property
    def graded(self):
        """
        Returns the count of graded submissions in this queue.
        """
        return len([1 for fs in self.submissions if fs.submission.get().score])

    def to_json(self, fields=None):
        if not fields:
            fields = {}

        final_submissions = self.submissions
        subms = []
        submitters = ndb.get_multi(fs.submitter for fs in final_submissions)
        submissions = [fs.submission for fs in final_submissions]
        submissions = ndb.get_multi(submissions)
        groups = [fs.group for fs in final_submissions]
        groups = ndb.get_multi(filter(None, groups))
        groups = {v.key: v for v in groups}
        for i, fs in enumerate(final_submissions):
          submission = submissions[i]
          group = groups.get(fs.group)
          subms.append(
            {
             'id': fs.key.id(),
             'submission': submission.key.id(),
             'created': submission.created,
             'backup': submission.backup.id(),
             'submitter': submitters[i],
             'group': group,
             'score': submission.score,
            })
        owner_email = "Unknown"
        if self.owner.get():
          owner_email = self.owner.get().email[0]

        return {
            'submissions': subms,
            'count': len(final_submissions),
            'graded': len(filter(None, (subm.score for subm in submissions))),
            'assignment': {'id': self.assignment},
            'assigned_staff': [val.get() for val in self.assigned_staff],
            'owner': owner_email,
            'id': self.key.id()
        }

    @classmethod
    def _can(cls, user, need, queue, query=None):
        action = need.action
        if not user.logged_in:
            return False

        if action == "index":
            if user.is_admin:
                return query
            courses = [part.course for part in Participant.query(
                Participant.user == user.key,
                Participant.role == STAFF_ROLE).fetch()]
            if courses:
                return disjunction(
                    query, [(Queue.course == course) for course in courses])
            return False

        course = queue.assignment.get().course
        is_staff = user.is_admin or \
            Participant.has_role(user, course, STAFF_ROLE)
        if is_staff:
            return True

        return False

class FinalSubmission(Base):
    """The final submission for an assignment from a group.

    Specification:
    https://github.com/Cal-CS-61A-Staff/ok/wiki/Final-Submissions-and-Grading
    """
    assignment = ndb.KeyProperty(Assignment)
    group = ndb.KeyProperty(Group)
    submission = ndb.KeyProperty(Submission)
    revision = ndb.KeyProperty(Submission)
    queue = ndb.KeyProperty(Queue)
    server_time = ndb.ComputedProperty(lambda q: q.submission.get().server_time)
    # submitter = ndb.ComputedProperty(lambda q: q.submission.get().submitter.get())
    submitter = ndb.KeyProperty(User)
    published = ndb.BooleanProperty(default=False)

    @property
    def backup(self):
        """
        Return the associated backup.
        """
        return self.submission.get().backup.get()

    @property
    def assigned(self):
        """
        Return whether or not this assignment has been assigned to a queue.
        """
        return bool(self.queue)

    @classmethod
    def _can(cls, user, need, final, query):
        action = need.action
        if action in ("create", "put") and final:
            group = final.submission.get().backup.get().group
            if group:
              return user.logged_in and user.key in group.member
        return Submission._can(
            user, need, final.submission.get() if final else None, query)

    def _pre_put_hook(self):
        # TODO Remove when submitter is a computed property
        self.submitter = self.submission.get().submitter

    def get_scores(self):
        """
        Return a list of lists of the format [[student, score, message, grader, tag]]
        if the submission has been scored. Otherwise an empty list.
        If the submission is a group submission, there will be an element
        for each combination of student and score.
        """
        # TODO: get the most recent score for each tag.
        # Question: will all scores have a grader? In particular the scores from the autograder.
        all_scores = []
        if self.group:
            members = [member for member in self.group.get().member]
        else:
            members = [self.submitter]
        for member in members:
            member_row = member.get()
            if member_row:
              email = member_row.email[0]
              for score in self.submission.get().score:
                  all_scores.append([email,
                          score.score,
                          score.message,
                          score.grader.get().email[0],
                          score.tag])
            else:
              logging.warning("User key not found - " + str(member))
        return all_scores


text:
package structs

import (
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/types"
)

// CheckDefinition is used to JSON decode the Check definitions
type CheckDefinition struct {
	ID        types.CheckID
	Name      string
	Notes     string
	ServiceID string
	Token     string
	Status    string

	// Copied fields from CheckType without the fields
	// already present in CheckDefinition:
	//
	//   ID (CheckID), Name, Status, Notes
	//
	Script                         string
	ScriptArgs                     []string
	HTTP                           string
	Header                         map[string][]string
	Method                         string
	TCP                            string
	Interval                       time.Duration
	DockerContainerID              string
	Shell                          string
	TLSSkipVerify                  bool
	Timeout                        time.Duration
	TTL                            time.Duration
	DeregisterCriticalServiceAfter time.Duration
}

func (c *CheckDefinition) HealthCheck(node string) *HealthCheck {
	health := &HealthCheck{
		Node:      node,
		CheckID:   c.ID,
		Name:      c.Name,
		Status:    api.HealthCritical,
		Notes:     c.Notes,
		ServiceID: c.ServiceID,
	}
	if c.Status != "" {
		health.Status = c.Status
	}
	if health.CheckID == "" && health.Name != "" {
		health.CheckID = types.CheckID(health.Name)
	}
	return health
}

func (c *CheckDefinition) CheckType() *CheckType {
	return &CheckType{
		CheckID: c.ID,
		Name:    c.Name,
		Status:  c.Status,
		Notes:   c.Notes,

		Script:            c.Script,
		ScriptArgs:        c.ScriptArgs,
		HTTP:              c.HTTP,
		Header:            c.Header,
		Method:            c.Method,
		TCP:               c.TCP,
		Interval:          c.Interval,
		DockerContainerID: c.DockerContainerID,
		Shell:             c.Shell,
		TLSSkipVerify:     c.TLSSkipVerify,
		Timeout:           c.Timeout,
		TTL:               c.TTL,
		DeregisterCriticalServiceAfter: c.DeregisterCriticalServiceAfter,
	}
}

redpajama_set_name: RedPajamaGithub


text:
<?php

declare(strict_types=1);

namespace Cortex\Auth\Http\Controllers\Adminarea;

use Cortex\Auth\Models\Manager;
use Spatie\MediaLibrary\Models\Media;
use Cortex\Foundation\Http\Controllers\AuthorizedController;

class ManagersMediaController extends AuthorizedController
{
    /**
     * {@inheritdoc}
     */
    protected $resource = Manager::class;

    /**
     * {@inheritdoc}
     */
    public function authorizeResource($model, $parameter = null, array $options = [], $request = null): void
    {
        $middleware = [];
        $parameter = $parameter ?: snake_case(class_basename($model));

        foreach ($this->mapResourceAbilities() as $method => $ability) {
            $modelName = in_array($method, $this->resourceMethodsWithoutModels()) ? $model : $parameter;

            $middleware["can:update,{$modelName}"][] = $method;
            $middleware["can:{$ability},media"][] = $method;
        }

        foreach ($middleware as $middlewareName => $methods) {
            $this->middleware($middlewareName, $options)->only($methods);
        }
    }

    /**
     * Destroy given manager media.
     *
     * @param \Cortex\Auth\Models\Manager       $manager
     * @param \Spatie\MediaLibrary\Models\Media $media
     *
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
     */
    public function destroy(Manager $manager, Media $media)
    {
        $manager->media()->where($media->getKeyName(), $media->getKey())->first()->delete();

        return intend([
            'url' => route('adminarea.managers.edit', ['manager' => $manager]),
            'with' => ['warning' => trans('cortex/foundation::messages.resource_deleted', ['resource' => trans('cortex/foundation::common.media'), 'identifier' => $media->getRouteKey()])],
        ]);
    }
}


text:
<accounts-list [accounts]="_accounts" (delete)="removeAcc($event)"
    (details)="_showAccountDetails($event)"></accounts-list>


text:
#ifndef __XN_STREAM_DATA_INTERNAL_H__
#define __XN_STREAM_DATA_INTERNAL_H__

//---------------------------------------------------------------------------
// Includes
//---------------------------------------------------------------------------
#include <XnStreamData.h>

//---------------------------------------------------------------------------
// Types
//---------------------------------------------------------------------------
class XnBuffer;

struct XnStreamDataInternal
{
	XnStreamDataUpdateMode UpdateMode;
	XnStreamOutputNotificationCallback Callback;
	void* pCallbackData;
	XnBool bAllocated;
	XnUInt32 nAllocSize;
	XnBuffer* pLockedBuffer;
};

//---------------------------------------------------------------------------
// Exported Functions
//---------------------------------------------------------------------------
/**
* Creates a new stream output object which does not allocate data.
*
* @param	ppStreamOutput	[out]	A pointer to the newly created object.
* @param	StreamName		[in]	The name of the stream that this buffer will be used for.
* @param	nAllocSize		[in]	The number of bytes to allocate for the pData buffer.
*/
XN_DDK_API XnStatus XnStreamDataCreateNoBuffer(XnStreamData** ppStreamOutput, const XnChar* StreamName);

#endif //__XN_STREAM_DATA_INTERNAL_H__


text:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE ldml SYSTEM "../../common/dtd/ldml.dtd">
<!-- Copyright © 1991-2022 Unicode, Inc.
For terms of use, see http://www.unicode.org/copyright.html
SPDX-License-Identifier: Unicode-DFS-2016
CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
-->
<ldml>
	<identity>
		<version number="$Revision$"/>
		<language type="zh"/>
		<script type="Hans"/>
		<territory type="SG"/>
	</identity>
	<dates>
		<calendars>
			<calendar type="buddhist">
				<dateFormats>
					<dateFormatLength type="short">
						<dateFormat>
							<pattern draft="contributed">Gd/M/yy</pattern>
							<datetimeSkeleton draft="contributed">GyyMd</datetimeSkeleton>
						</dateFormat>
					</dateFormatLength>
				</dateFormats>
			</calendar>
			<calendar type="chinese">
				<dateFormats>
					<dateFormatLength type="full">
						<dateFormat>
							<pattern numbers="hanidec" draft="contributed">U年MMMd日EEEE</pattern>
							<datetimeSkeleton numbers="hanidec" draft="contributed">UMMMEEEEd</datetimeSkeleton>
						</dateFormat>
					</dateFormatLength>
					<dateFormatLength type="long">
						<dateFormat>
							<pattern numbers="hanidec" draft="contributed">U年MMMd日</pattern>
							<datetimeSkeleton numbers="hanidec" draft="contributed">UMMMd</datetimeSkeleton>
						</dateFormat>
					</dateFormatLength>
					<dateFormatLength type="medium">
						<dateFormat>
							<pattern numbers="hanidec" draft="contributed">U年MMMd日</pattern>
							<datetimeSkeleton numbers="hanidec" draft="contributed">UMMMd</datetimeSkeleton>
						</dateFormat>
					</dateFormatLength>
				</dateFormats>
			</calendar>
			<calendar type="generic">
				<dateFormats>
					<dateFormatLength type="short">
						<dateFormat>
							<pattern>dd/MM/yyGGGGG</pattern>
							<datetimeSkeleton>GGGGGyyMMdd</datetimeSkeleton>
						</dateFormat>
					</dateFormatLength>
				</dateFormats>
				<dateTimeFormats>
					<availableFormats>
						<dateFormatItem id="Md" draft="contributed">M-d</dateFormatItem>
						<dateFormatItem id="MEd" draft="contributed">M-dE</dateFormatItem>
						<dateFormatItem id="MMdd" draft="contributed">MM-dd</dateFormatItem>
						<dateFormatItem id="MMMMdd" draft="contributed">M月d日</dateFormatItem>
						<dateFormatItem id="yyyyMd" draft="contributed">Gy年M月d日</dateFormatItem>
						<dateFormatItem id="yyyyMEd" draft="contributed">Gy年M月d日,E</dateFormatItem>
					</availableFormats>
					<intervalFormats>
						<intervalFormatFallback draft="contributed">{0}至{1}</intervalFormatFallback>
						<intervalFormatItem id="Hmv">
							<greatestDifference id="H" draft="contributed">vHH:mm–HH:mm</greatestDifference>
							<greatestDifference id="m" draft="contributed">vHH:mm–HH:mm</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="Hv">
							<greatestDifference id="H" draft="contributed">vHH–HH</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="Md">
							<greatestDifference id="d" draft="contributed">M-d至M-d</greatestDifference>
							<greatestDifference id="M" draft="contributed">M-d至M-d</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="MEd">
							<greatestDifference id="d" draft="contributed">M-dE至M-dE</greatestDifference>
							<greatestDifference id="M" draft="contributed">M-dE至M-dE</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="MMMEd">
							<greatestDifference id="d" draft="contributed">M月d日E至M月d日E</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="yM">
							<greatestDifference id="M" draft="contributed">y年M月至y年M月</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="yMd">
							<greatestDifference id="d" draft="contributed">d/M/y至d/M/y</greatestDifference>
							<greatestDifference id="M" draft="contributed">d/M/y至d/M/y</greatestDifference>
							<greatestDifference id="y" draft="contributed">d/M/y至d/M/y</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="yMEd">
							<greatestDifference id="d" draft="contributed">d/M/yE至d/M/yE</greatestDifference>
							<greatestDifference id="M" draft="contributed">d/M/yE至d/M/yE</greatestDifference>
							<greatestDifference id="y" draft="contributed">d/M/yE至d/M/yE</greatestDifference>
						</intervalFormatItem>
					</intervalFormats>
				</dateTimeFormats>
			</calendar>
			<calendar type="gregorian">
				<dateFormats>
					<dateFormatLength type="short">
						<dateFormat>
							<pattern>dd/MM/yy</pattern>
							<datetimeSkeleton>yyMMdd</datetimeSkeleton>
						</dateFormat>
					</dateFormatLength>
				</dateFormats>
				<timeFormats>
					<timeFormatLength type="full">
						<timeFormat>
							<pattern>zzzz ah:mm:ss</pattern>
							<datetimeSkeleton>ahmmsszzzz</datetimeSkeleton>
						</timeFormat>
					</timeFormatLength>
					<timeFormatLength type="long">
						<timeFormat>
							<pattern>z ah:mm:ss</pattern>
							<datetimeSkeleton>ahmmssz</datetimeSkeleton>
						</timeFormat>
					</timeFormatLength>
					<timeFormatLength type="medium">
						<timeFormat>
							<pattern>ah:mm:ss</pattern>
							<datetimeSkeleton>ahmmss</datetimeSkeleton>
						</timeFormat>
					</timeFormatLength>
					<timeFormatLength type="short">
						<timeFormat>
							<pattern>ah:mm</pattern>
							<datetimeSkeleton>ahmm</datetimeSkeleton>
						</timeFormat>
					</timeFormatLength>
				</timeFormats>
				<dateTimeFormats>
					<availableFormats>
						<dateFormatItem id="Md" draft="contributed">M-d</dateFormatItem>
						<dateFormatItem id="MEd" draft="contributed">M-dE</dateFormatItem>
						<dateFormatItem id="MMdd" draft="contributed">MM-dd</dateFormatItem>
						<dateFormatItem id="MMM" draft="contributed">M月</dateFormatItem>
						<dateFormatItem id="MMMMdd" draft="contributed">M月d日</dateFormatItem>
						<dateFormatItem id="yMd" draft="contributed">y年M月d日</dateFormatItem>
						<dateFormatItem id="yMEd" draft="contributed">y年M月d日,E</dateFormatItem>
					</availableFormats>
					<intervalFormats>
						<intervalFormatFallback draft="contributed">{0}至{1}</intervalFormatFallback>
						<intervalFormatItem id="d">
							<greatestDifference id="d" draft="contributed">d日至d日</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="Hmv">
							<greatestDifference id="H" draft="contributed">vHH:mm–HH:mm</greatestDifference>
							<greatestDifference id="m" draft="contributed">vHH:mm–HH:mm</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="Hv">
							<greatestDifference id="H" draft="contributed">vHH–HH</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="Md">
							<greatestDifference id="d" draft="contributed">M-d至M-d</greatestDifference>
							<greatestDifference id="M" draft="contributed">M-d至M-d</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="MEd">
							<greatestDifference id="d" draft="contributed">M-dE至M-dE</greatestDifference>
							<greatestDifference id="M" draft="contributed">M-dE至M-dE</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="MMMEd">
							<greatestDifference id="d" draft="contributed">M月d日E至M月d日E</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="yM">
							<greatestDifference id="M" draft="contributed">y年M月至y年M月</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="yMd">
							<greatestDifference id="d" draft="contributed">d/M/y至d/M/y</greatestDifference>
							<greatestDifference id="M" draft="contributed">d/M/y至d/M/y</greatestDifference>
							<greatestDifference id="y" draft="contributed">d/M/y至d/M/y</greatestDifference>
						</intervalFormatItem>
						<intervalFormatItem id="yMEd">
							<greatestDifference id="d" draft="contributed">d/M/yE至d/M/yE</greatestDifference>
							<greatestDifference id="M" draft="contributed">d/M/yE至d/M/yE</greatestDifference>
							<greatestDifference id="y" draft="contributed">d/M/yE至d/M/yE</greatestDifference>
						</intervalFormatItem>
					</intervalFormats>
				</dateTimeFormats>
			</calendar>
			<calendar type="islamic">
				<dateFormats>
					<dateFormatLength type="short">
						<dateFormat>
							<pattern draft="contributed">Gd/M/yy</pattern>
							<datetimeSkeleton draft="contributed">GyyMd</datetimeSkeleton>
						</dateFormat>
					</dateFormatLength>
				</dateFormats>
			</calendar>
			<calendar type="japanese">
				<dateFormats>
					<dateFormatLength type="short">
						<dateFormat>
							<pattern draft="contributed">Gd/M/yy</pattern>
							<datetimeSkeleton draft="contributed">GyyMd</datetimeSkeleton>
						</dateFormat>
					</dateFormatLength>
				</dateFormats>
				<dateTimeFormats>
					<availableFormats>
						<dateFormatItem id="Md" draft="contributed">M/d</dateFormatItem>
						<dateFormatItem id="MEd" draft="contributed">M/dE</dateFormatItem>
					</availableFormats>
				</dateTimeFormats>
			</calendar>
			<calendar type="roc">
				<dateFormats>
					<dateFormatLength type="short">
						<dateFormat>
							<pattern draft="contributed">Gd/M/yy</pattern>
							<datetimeSkeleton draft="contributed">GyyMd</datetimeSkeleton>
						</dateFormat>
					</dateFormatLength>
				</dateFormats>
				<dateTimeFormats>
					<availableFormats>
						<dateFormatItem id="Md" draft="contributed">M-d</dateFormatItem>
						<dateFormatItem id="MEd" draft="contributed">M-dE</dateFormatItem>
						<dateFormatItem id="MMM" draft="contributed">M月</dateFormatItem>
					</availableFormats>
				</dateTimeFormats>
			</calendar>
		</calendars>
		<fields>
			<field type="second">
				<relativeTime type="future">
					<relativeTimePattern count="other">{0}秒后</relativeTimePattern>
				</relativeTime>
				<relativeTime type="past">
					<relativeTimePattern count="other">{0}秒前</relativeTimePattern>
				</relativeTime>
			</field>
		</fields>
		<timeZoneNames>
			<zone type="America/Scoresbysund">
				<exemplarCity draft="contributed">斯考斯伯松德</exemplarCity>
			</zone>
			<metazone type="Malaysia">
				<short>
					<standard>MYT</standard>
				</short>
			</metazone>
			<metazone type="Singapore">
				<short>
					<standard>SGT</standard>
				</short>
			</metazone>
		</timeZoneNames>
	</dates>
	<numbers>
		<currencyFormats numberSystem="latn">
			<unitPattern count="other" draft="contributed">{0} {1}</unitPattern>
		</currencyFormats>
		<currencies>
			<currency type="ANG">
				<displayName count="other" draft="contributed">荷兰安的列斯盾</displayName>
			</currency>
			<currency type="CNY">
				<symbol draft="contributed">CN¥</symbol>
			</currency>
			<currency type="NIO">
				<displayName count="other" draft="contributed">尼加拉瓜科多巴</displayName>
			</currency>
			<currency type="SGD">
				<symbol>$</symbol>
			</currency>
			<currency type="XAG">
				<displayName draft="contributed">白银</displayName>
			</currency>
		</currencies>
	</numbers>
	<units>
		<unitLength type="long">
			<compoundUnit type="per">
				<compoundUnitPattern draft="contributed">{0}/{1}</compoundUnitPattern>
			</compoundUnit>
			<unit type="concentr-karat">
				<displayName draft="unconfirmed">开</displayName>
				<unitPattern count="other" draft="unconfirmed">{0}开</unitPattern>
			</unit>
			<unit type="duration-second">
				<unitPattern count="other" draft="contributed">{0}秒</unitPattern>
			</unit>
			<unit type="temperature-kelvin">
				<displayName draft="contributed">开氏度</displayName>
				<unitPattern count="other" draft="contributed">{0}开氏度</unitPattern>
			</unit>
		</unitLength>
		<unitLength type="short">
			<unit type="acceleration-g-force">
				<unitPattern count="other" draft="unconfirmed">{0}G力</unitPattern>
			</unit>
			<unit type="concentr-karat">
				<displayName draft="unconfirmed">开</displayName>
				<unitPattern count="other" draft="unconfirmed">{0}开</unitPattern>
			</unit>
			<unit type="temperature-kelvin">
				<displayName draft="unconfirmed">开氏度</displayName>
				<unitPattern count="other" draft="contributed">{0}°K</unitPattern>
			</unit>
		</unitLength>
		<unitLength type="narrow">
			<unit type="length-picometer">
				<unitPattern count="other" draft="unconfirmed">{0}皮米</unitPattern>
			</unit>
			<unit type="length-mile">
				<unitPattern count="other" draft="unconfirmed">{0}英里</unitPattern>
			</unit>
			<unit type="length-yard">
				<unitPattern count="other" draft="unconfirmed">{0}码</unitPattern>
			</unit>
			<unit type="length-foot">
				<unitPattern count="other" draft="unconfirmed">{0}英尺</unitPattern>
			</unit>
			<unit type="length-inch">
				<unitPattern count="other" draft="unconfirmed">{0}英寸</unitPattern>
			</unit>
			<unit type="length-light-year">
				<unitPattern count="other" draft="unconfirmed">{0}光年</unitPattern>
			</unit>
		</unitLength>
	</units>
</ldml>


text:
package neo4j.org.testkit.backend.messages.requests;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import neo4j.org.testkit.backend.TestkitState;
import neo4j.org.testkit.backend.messages.responses.TestkitCallback;
import neo4j.org.testkit.backend.messages.responses.TestkitResponse;
import reactor.core.publisher.Mono;

/**
 * This request is sent by Testkit in response to previously sent {@link TestkitCallback}.
 */
public interface TestkitCallbackResult extends TestkitRequest {
    String getCallbackId();

    @Override
    default TestkitResponse process(TestkitState testkitState) {
        testkitState.getCallbackIdToFuture().get(getCallbackId()).complete(this);
        return null;
    }

    @Override
    default CompletionStage<TestkitResponse> processAsync(TestkitState testkitState) {
        testkitState.getCallbackIdToFuture().get(getCallbackId()).complete(this);
        return CompletableFuture.completedFuture(null);
    }

    @Override
    default Mono<TestkitResponse> processRx(TestkitState testkitState) {
        testkitState.getCallbackIdToFuture().get(getCallbackId()).complete(this);
        return Mono.empty();
    }

    @Override
    default Mono<TestkitResponse> processReactive(TestkitState testkitState) {
        testkitState.getCallbackIdToFuture().get(getCallbackId()).complete(this);
        return Mono.empty();
    }

    @Override
    default Mono<TestkitResponse> processReactiveStreams(TestkitState testkitState) {
        testkitState.getCallbackIdToFuture().get(getCallbackId()).complete(this);
        return Mono.empty();
    }
}


text:
using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections;

namespace PortableRealm
{

public class MRGameTypeSelectionDialog : MonoBehaviour
{
	private const int MaxBoLChapter = 1;

	#region Properties

	public Button DefaultSelect;
	public Button BoLMinus;
	public Button BoLPlus;
	public Text BoLChapter;
	public Button BoLSelect;

	public int BookOfLearningChapter
	{
		get{
			return mBoLChapter;
		}
	}

	public MRGame.eGameType GameType
	{
		get{
			return mGameType;
		}
	}

	public MRMainUI.OnButtonPressed Callback
	{
		get{
			return mCallback;
		}

		set{
			mCallback = value;
		}
	}

	#endregion

	#region Methods

	// Use this for initialization
	void Start ()
	{
		mBoLChapter = 1;
		mGameType = MRGame.eGameType.Default;
		DefaultSelect.onClick.AddListener(OnDefaultSelectClicked);
		BoLSelect.onClick.AddListener(OnBoLSelectClicked);
		BoLMinus.onClick.AddListener(OnBoLMinusClicked);
		BoLPlus.onClick.AddListener(OnBoLPlusClicked);
	}

	// Update is called once per frame
	void Update ()
	{
		BoLChapter.text = mBoLChapter.ToString();
	}

	private void OnDefaultSelectClicked()
	{
		mGameType = MRGame.eGameType.Default;
		if (mCallback != null)
		{
			mCallback(0);
		}
	}

	private void OnBoLSelectClicked()
	{
		mGameType = MRGame.eGameType.BookOfLearning;
		if (mCallback != null)
		{
			mCallback(1);
		}
	}

	private void OnBoLMinusClicked()
	{
		if (mBoLChapter > 1)
		--mBoLChapter;
	}

	private void OnBoLPlusClicked()
	{
		if (mBoLChapter < MaxBoLChapter)
			++mBoLChapter;
	}

	#endregion

	#region Members

	private int mBoLChapter;
	private MRGame.eGameType mGameType;
	private MRMainUI.OnButtonPressed mCallback = null;

	#endregion
}

}


text:
package stdlib

import (
	"github.com/zclconf/go-cty/cty"
	"github.com/zclconf/go-cty/cty/function"
	"github.com/zclconf/go-cty/cty/json"
)

var JSONEncodeFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name:             "val",
			Type:             cty.DynamicPseudoType,
			AllowDynamicType: true,
		},
	},
	Type: function.StaticReturnType(cty.String),
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		val := args[0]
		buf, err := json.Marshal(val, val.Type())
		if err != nil {
			return cty.NilVal, err
		}

		return cty.StringVal(string(buf)), nil
	},
})

var JSONDecodeFunc = function.New(&function.Spec{
	Params: []function.Parameter{
		{
			Name: "str",
			Type: cty.String,
		},
	},
	Type: func(args []cty.Value) (cty.Type, error) {
		str := args[0]
		if !str.IsKnown() {
			return cty.DynamicPseudoType, nil
		}

		buf := []byte(str.AsString())
		return json.ImpliedType(buf)
	},
	Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
		buf := []byte(args[0].AsString())
		return json.Unmarshal(buf, retType)
	},
})

// JSONEncode returns a JSON serialization of the given value.
func JSONEncode(val cty.Value) (cty.Value, error) {
	return JSONEncodeFunc.Call([]cty.Value{val})
}

// JSONDecode parses the given JSON string and, if it is valid, returns the
// value it represents.
//
// Note that applying JSONDecode to the result of JSONEncode may not produce
// an identically-typed result, since JSON encoding is lossy for cty Types.
// The resulting value will consist only of primitive types, object types, and
// tuple types.
func JSONDecode(str cty.Value) (cty.Value, error) {
	return JSONDecodeFunc.Call([]cty.Value{str})
}


text:
import IViewEntityNS = require("./ViewEntity");
import IViewEntity = IViewEntityNS.IViewEntity;
import Core = require("./Gl/core");

export class ObjectUniformsProvider extends IViewEntityNS.ViewEntityBase {
    constructor(public uniformObject : any, parent : IViewEntity = null) {
	super(parent);
    }
    getValue(k:string) : Core.GlValue {
	if(this.uniformObject && this.uniformObject[k] !== undefined) {
	    var v = this.uniformObject[k];
	    if(v instanceof Function){
		return v.call(this.uniformObject, this);
	    }
	    return v;
	}
	return this.parent && this.parent.getValue(k);
    }
    getProvidedValues() {
	return Object.keys(this.uniformObject);
    }
}

class ViewGroup_<T> extends ObjectUniformsProvider {
    constructor(public children: ILayerEntity<T>[] = [], obj : any = null) {
	super(obj);
	this.children.forEach(c => c.parent = this);
    }
    add( entity : IViewEntity ) {
        this.children.push(entity);
	entity.parent = this;
    }
    render( t : T) {
	super.render();
        this.children.forEach(e => e.render(t));
    }
}

class ViewGroup extends ObjectUniformsProvider {
    constructor(public children: IViewEntity[] = [], obj : any = null) {
	super(obj);
	this.children.forEach(c => c.parent = this);
    }
    add( entity : IViewEntity ) {
        this.children.push(entity);
	entity.parent = this;
    }
    render() {
	super.render();
        this.children.forEach(e => e.render());
    }
}

export = ViewGroup;


text:
@interface BYLLayerDelegate : NSObject
@property (nonatomic) BOOL returnsAction;
@end

@implementation BYLLayerDelegate
- (id <CAAction>) actionForLayer:(CALayer *)layer forKey:(NSString *)event {
  return self.returnsAction ? [CABasicAnimation animationWithKeyPath:event] : [NSNull null];
}
@end

@interface BYLBadgeViewLayer_Tests : XCTestCase

@end

@implementation BYLBadgeViewLayer_Tests
#pragma mark - Making a new layer
- (void) testInitWithLayer_copiesBadge {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  badgeLayer.badge = 20;
  BYLBadgeViewLayer *copiedLayer = [[BYLBadgeViewLayer alloc] initWithLayer:badgeLayer];
  XCTAssertEqual(copiedLayer.badge, 20u, @"Copied layer did not copy the badge");
}

- (void) testInitWithLayer_copiesBadgeRadius {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  badgeLayer.badgeRadius = 30.0f;
  BYLBadgeViewLayer *copiedLayer = [[BYLBadgeViewLayer alloc] initWithLayer:badgeLayer];
  XCTAssertEqual(copiedLayer.badgeRadius, 30.0f, @"Copied layer did not copy the badge radius");
}

- (void) testInitWithLayer_copiesTextAttributes {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  NSDictionary *expected = [self defaultAttributesWithTextSize:16.0f];
  badgeLayer.badgeTextAttributes = expected;
  BYLBadgeViewLayer *copiedLayer = [[BYLBadgeViewLayer alloc] initWithLayer:badgeLayer];
  XCTAssertEqualObjects(copiedLayer.badgeTextAttributes, expected, @"Copied layer did not copy text attributes");
}

- (void) testInit_defaultsBadgeTextColorToWhite {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  XCTAssertEqualObjects([UIColor colorWithCGColor:badgeLayer.badgeTextColor], [UIColor whiteColor], @"Initial badge text color is not white");
}

- (void) testInitWithCoder_defaultsBadgeTextColorToWhite {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] initWithCoder:nil];
  XCTAssertEqualObjects([UIColor colorWithCGColor:badgeLayer.badgeTextColor], [UIColor whiteColor], @"Initial badge text color is not white");
}

#pragma mark - Redisplaying
- (void) testNeedsDisplay_forBadge_isTrue {
  XCTAssertTrue([BYLBadgeViewLayer needsDisplayForKey:@"badge"], @"Badge should require redisplay");
}

- (void) testNeedsDisplay_forBadgeTextAttributes_isTrue {
  XCTAssertTrue([BYLBadgeViewLayer needsDisplayForKey:@"badgeTextAttributes"], @"Badge Text Attributes should require redisplay");
}

- (void) testNeedsDisplay_forBadgeTextColor_isTrue {
  XCTAssertTrue([BYLBadgeViewLayer needsDisplayForKey:@"badgeTextColor"], @"Badge Text Color should require redisplay");
}

- (void) testNeedsDisplay_forBadgeBackgroundColor_isTrue {
  XCTAssertTrue([BYLBadgeViewLayer needsDisplayForKey:@"badgeBackgroundColor"], @"Badge Background Color should require redisplay");
}

- (void) testNeedsDisplay_forBadgeRadius_isTrue {
  XCTAssertTrue([BYLBadgeViewLayer needsDisplayForKey:@"badgeRadius"], @"Badge Radius should require redisplay");
}

- (void) testNeedsDisplayForBoundsChange_isTrue {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  XCTAssertTrue([badgeLayer needsDisplayOnBoundsChange], @"Badge should be redrawn when bounds changed");
}

#pragma mark - Implicit Animations
- (void) testActionForKey_badge_withNullDelegateResponse_isNull {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  BYLLayerDelegate *delegate = [[BYLLayerDelegate alloc] init];
  badgeLayer.delegate = delegate;
  XCTAssertEqualObjects([badgeLayer actionForKey:@"badge"], nil, @"Expected there not be an animation for property");
}

- (void) testActionForKey_badge_withAnimationDelegateResponse_isProperAnimation {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  badgeLayer.badge = 50;
  BYLLayerDelegate *delegate = [[BYLLayerDelegate alloc] init];
  delegate.returnsAction = YES;
  badgeLayer.delegate = delegate;
  CABasicAnimation *expectedAnimation = [CABasicAnimation animationWithKeyPath:@"badge"];
  expectedAnimation.fromValue = @50;
  CABasicAnimation *animation = (CABasicAnimation*)[badgeLayer actionForKey:@"badge"];
  XCTAssertEqualObjects(animation.fromValue, expectedAnimation.fromValue, @"Animations from values do not match");
  XCTAssertEqualObjects(animation.keyPath, expectedAnimation.keyPath, @"Animations keyPath values do not match");
}

- (void) testActionForKey_badgeRadius_withNullDelegateResponse_isNull {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  BYLLayerDelegate *delegate = [[BYLLayerDelegate alloc] init];
  badgeLayer.delegate = delegate;
  XCTAssertEqualObjects([badgeLayer actionForKey:@"badgeRadius"], nil, @"Expected there not be an animation for property");
}

- (void) testActionForKey_badgeRadius_withAnimationDelegateResponse_isProperAnimation {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  badgeLayer.badgeRadius = 5.0f;
  BYLLayerDelegate *delegate = [[BYLLayerDelegate alloc] init];
  delegate.returnsAction = YES;
  badgeLayer.delegate = delegate;
  CABasicAnimation *expectedAnimation = [CABasicAnimation animationWithKeyPath:@"badgeRadius"];
  expectedAnimation.fromValue = @5.0f;
  CABasicAnimation *animation = (CABasicAnimation*)[badgeLayer actionForKey:@"badgeRadius"];
  XCTAssertEqualObjects(animation.fromValue, expectedAnimation.fromValue, @"Animations from values do not match");
  XCTAssertEqualObjects(animation.keyPath, expectedAnimation.keyPath, @"Animations keyPath values do not match");
}

- (void) testActionForKey_badgeTextColor_withNullDelegateResponse_isNull {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  BYLLayerDelegate *delegate = [[BYLLayerDelegate alloc] init];
  badgeLayer.delegate = delegate;
  XCTAssertEqualObjects([badgeLayer actionForKey:@"badgeTextColor"], nil, @"Expected there not be an animation for property");
}

- (void) testActionForKey_badgeTextColor_withAnimationDelegateResponse_isProperAnimation {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  badgeLayer.badgeTextColor = [UIColor blueColor].CGColor;
  BYLLayerDelegate *delegate = [[BYLLayerDelegate alloc] init];
  delegate.returnsAction = YES;
  badgeLayer.delegate = delegate;
  CABasicAnimation *expectedAnimation = [CABasicAnimation animationWithKeyPath:@"badgeTextColor"];
  expectedAnimation.fromValue = (id)[UIColor blueColor].CGColor;
  CABasicAnimation *animation = (CABasicAnimation*)[badgeLayer actionForKey:@"badgeTextColor"];
  XCTAssertEqualObjects([UIColor colorWithCGColor:(CGColorRef)animation.fromValue], [UIColor colorWithCGColor:(CGColorRef)expectedAnimation.fromValue], @"Animations from values do not match");
  XCTAssertEqualObjects(animation.keyPath, expectedAnimation.keyPath, @"Animations keyPath values do not match");
}

- (void) testActionForKey_badgeBackgroundColor_withNullDelegateResponse_isNull {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  BYLLayerDelegate *delegate = [[BYLLayerDelegate alloc] init];
  badgeLayer.delegate = delegate;
  XCTAssertEqualObjects([badgeLayer actionForKey:@"badgeBackgroundColor"], nil, @"Expected there not be an animation for property");
}

- (void) testActionForKey_badgeBackgroundColor_withAnimationDelegateResponse_isProperAnimation {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  badgeLayer.badgeBackgroundColor = [UIColor blueColor].CGColor;
  BYLLayerDelegate *delegate = [[BYLLayerDelegate alloc] init];
  delegate.returnsAction = YES;
  badgeLayer.delegate = delegate;
  CABasicAnimation *expectedAnimation = [CABasicAnimation animationWithKeyPath:@"badgeBackgroundColor"];
  expectedAnimation.fromValue = (id)[UIColor blueColor].CGColor;
  CABasicAnimation *animation = (CABasicAnimation*)[badgeLayer actionForKey:@"badgeBackgroundColor"];
  XCTAssertEqualObjects([UIColor colorWithCGColor:(CGColorRef)animation.fromValue], [UIColor colorWithCGColor:(CGColorRef)expectedAnimation.fromValue], @"Animations from values do not match");
  XCTAssertEqualObjects(animation.keyPath, expectedAnimation.keyPath, @"Animations keyPath values do not match");
}

#pragma mark - Accessors
- (void) testBadgeTextAttributes_defaultsToBlackBodyStyle {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  NSDictionary *expected = [self defaultAttributes];
  XCTAssertEqualObjects(badgeLayer.badgeTextAttributes, expected, @"Badge text attributes did not match");
}

- (void) testSetBadgeTextColor_updatesAttributesDictionary {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  NSMutableDictionary *expected = [[self defaultAttributes] mutableCopy];
  expected[NSForegroundColorAttributeName] = [UIColor redColor];
  badgeLayer.badgeTextColor = [UIColor redColor].CGColor;
  XCTAssertEqualObjects(badgeLayer.badgeTextAttributes, expected, @"Badge text did not update");
}

- (void) testBadgeTextColor_usesAttributesDictioanry {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  UIColor *expected = [self defaultAttributes][NSForegroundColorAttributeName];
  XCTAssertEqualObjects([UIColor colorWithCGColor:badgeLayer.badgeTextColor], expected, @"Badge text did not use attributes dictionary");
}

- (void) testSettingBadgeTextAttributes_updatesBadgeTextColor {
  BYLBadgeViewLayer *badgeLayer = [[BYLBadgeViewLayer alloc] init];
  NSDictionary *attributes =  @{
                                NSFontAttributeName: [UIFont fontWithDescriptor:[UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody] size:12.0],
                                NSForegroundColorAttributeName: [UIColor redColor]
                                };
  badgeLayer.badgeTextAttributes = attributes;
  XCTAssertEqualObjects(attributes[NSForegroundColorAttributeName], [UIColor colorWithCGColor:badgeLayer.badgeTextColor], @"Badge Text Color was not updated");
}

#pragma mark - Test Helpers
- (NSDictionary *) defaultAttributes {
  return [self defaultAttributesWithTextSize:6.0f];
}

- (NSDictionary *) defaultAttributesWithTextSize:(CGFloat)size {
  return @{
           NSFontAttributeName: [UIFont fontWithDescriptor:[UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody] size:size],
           NSForegroundColorAttributeName: [UIColor whiteColor]
           };
}

@end


text:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<META http-equiv="Content-Type" content="text/html; charset=utf-8">
<TITLE>
PhraseTableValueContainer.PhraseTableValues
</TITLE>

<META NAME="date" CONTENT="2014-12-06">

<LINK REL ="stylesheet" TYPE="text/css" HREF="../../../../../stylesheet.css" TITLE="Style">

      Overview
      Package
    Class
      Tree
      Deprecated
      Index
      Help

 PREV CLASS
 NEXT CLASS

  FRAMES
 NO FRAMES

  All Classes

  SUMMARY: NESTED | FIELD | CONSTR | METHOD

DETAIL: FIELD | CONSTR | METHOD

edu.berkeley.nlp.lm.phrasetable

Interface PhraseTableValueContainer.PhraseTableValues

All Superinterfaces:
Serializable

All Known Implementing Classes:
PhraseTableValueContainer.FeaturePhraseTableValues, PhraseTableValueContainer.TargetTranslationsValues

Enclosing class:
PhraseTableValueContainer

public static interface PhraseTableValueContainer.PhraseTableValues
extends Serializable

      Overview
      Package
    Class
      Tree
      Deprecated
      Index
      Help

 PREV CLASS
 NEXT CLASS

  FRAMES
 NO FRAMES

  All Classes

  SUMMARY: NESTED | FIELD | CONSTR | METHOD

DETAIL: FIELD | CONSTR | METHOD


text:
package org.zaproxy.zap.extension.pscanrules;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import net.htmlparser.jericho.Attribute;
import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.HTMLElementName;
import net.htmlparser.jericho.Source;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.parosproxy.paros.Constant;
import org.parosproxy.paros.control.Control;
import org.parosproxy.paros.core.scanner.Alert;
import org.parosproxy.paros.core.scanner.Plugin.AlertThreshold;
import org.parosproxy.paros.model.Model;
import org.parosproxy.paros.network.HttpMessage;
import org.zaproxy.addon.commonlib.CommonAlertTag;
import org.zaproxy.zap.extension.anticsrf.ExtensionAntiCSRF;
import org.zaproxy.zap.extension.pscan.PluginPassiveScanner;
import org.zaproxy.zap.extension.ruleconfig.RuleConfigParam;
import org.zaproxy.zap.model.Vulnerabilities;
import org.zaproxy.zap.model.Vulnerability;

/**
 * The CsrfCountermeasuresScanRule identifies *potential* vulnerabilities with the lack of known
 * CSRF countermeasures in pages with forms.
 *
 * @author 70pointer
 */
public class CsrfCountermeasuresScanRule extends PluginPassiveScanner {

    /** contains the base vulnerability that this plugin refers to */
    private static Vulnerability vuln = Vulnerabilities.getVulnerability("wasc_9");

    private static final Map<String, String> ALERT_TAGS =
            CommonAlertTag.toMap(
                    CommonAlertTag.OWASP_2021_A01_BROKEN_AC,
                    CommonAlertTag.OWASP_2017_A05_BROKEN_AC,
                    CommonAlertTag.WSTG_V42_SESS_05_CSRF);

    private ExtensionAntiCSRF extensionAntiCSRF;
    private String csrfIgnoreList;
    private String csrfAttIgnoreList;
    private String csrfValIgnoreList;

    /** the logger */
    private static Logger logger = LogManager.getLogger(CsrfCountermeasuresScanRule.class);

    /**
     * gets the plugin id for this extension
     *
     * @return the plugin id for this extension
     */
    @Override
    public int getPluginId() {
        return 10202;
    }

    /**
     * scans each form in the HTTP response for known anti-CSRF tokens. If any form exists that does
     * not contain a known anti-CSRF token, raise an alert.
     */
    @Override
    public void scanHttpResponseReceive(HttpMessage msg, int id, Source source) {
        if (AlertThreshold.HIGH.equals(getAlertThreshold()) && !msg.isInScope()) {
            return; // At HIGH threshold return if the msg isn't in scope
        }

        // need to do this if we are to be able to get an element's parent. Do it as early as
        // possible in the logic
        source.fullSequentialParse();

        long start = System.currentTimeMillis();

        ExtensionAntiCSRF extAntiCSRF = getExtensionAntiCSRF();

        if (extAntiCSRF == null) {
            return;
        }

        List<Element> formElements = source.getAllElements(HTMLElementName.FORM);
        List<String> tokenNames = extAntiCSRF.getAntiCsrfTokenNames();

        if (formElements != null && formElements.size() > 0) {
            boolean hasSecurityAnnotation = false;

            // Loop through all of the FORM tags
            logger.debug("Found {} forms", formElements.size());

            int numberOfFormsPassed = 0;

            List<String> ignoreList = new ArrayList<>();
            String ignoreConf = getCSRFIgnoreList();
            if (ignoreConf != null && ignoreConf.length() > 0) {
                logger.debug("Using ignore list: {}", ignoreConf);
                for (String str : ignoreConf.split(",")) {
                    String strTrim = str.trim();
                    if (strTrim.length() > 0) {
                        ignoreList.add(strTrim);
                    }
                }
            }
            String ignoreAttName = getCSRFIgnoreAttName();
            String ignoreAttValue = getCSRFIgnoreAttValue();

            for (Element formElement : formElements) {
                logger.debug(
                        "FORM [{}] has parent [{}]", formElement, formElement.getParentElement());
                StringBuilder sbForm = new StringBuilder();
                SortedSet<String> elementNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
                ++numberOfFormsPassed;
                // if the form has no parent, it is pretty likely invalid HTML (or Javascript!!!),
                // so we will not report
                // any alerts on it.
                // ie. This logic is necessary to eliminate false positives on non-HTML files.
                if (formElement.getParentElement() == null) {
                    logger.debug(
                            "Skipping HTML form because it has no parent. Likely not actually HTML.");
                    continue; // do not report a missing anti-CSRF field on this form
                }
                if (formOnIgnoreList(formElement, ignoreList)) {
                    continue;
                }
                if (!StringUtils.isEmpty(ignoreAttName)) {
                    // Check to see if the specific security annotation is present
                    Attribute att = formElement.getAttributes().get(ignoreAttName);
                    if (att != null) {
                        if (StringUtils.isEmpty(ignoreAttValue)
                                || ignoreAttValue.equals(att.getValue())) {
                            hasSecurityAnnotation = true;
                        }
                    }
                }

                List<Element> inputElements = formElement.getAllElements(HTMLElementName.INPUT);
                sbForm.append("[Form " + numberOfFormsPassed + ": \"");
                boolean foundCsrfToken = false;

                if (inputElements != null && inputElements.size() > 0) {
                    // Loop through all of the INPUT elements
                    logger.debug("Found {} inputs", inputElements.size());
                    for (Element inputElement : inputElements) {
                        String attId = inputElement.getAttributeValue("ID");
                        if (attId != null) {
                            elementNames.add(attId);
                            for (String tokenName : tokenNames) {
                                if (tokenName.equalsIgnoreCase(attId)) {
                                    foundCsrfToken = true;
                                    break;
                                }
                            }
                        }
                        String name = inputElement.getAttributeValue("NAME");
                        if (name != null) {
                            if (attId == null) {
                                // Dont bother recording both
                                elementNames.add(name);
                            }
                            for (String tokenName : tokenNames) {
                                if (tokenName.equalsIgnoreCase(name)) {
                                    foundCsrfToken = true;
                                    break;
                                }
                            }
                        }
                    }
                }
                if (foundCsrfToken) {
                    continue;
                }

                String evidence = "";
                evidence = formElement.getFirstElement().getStartTag().toString();

                // Append the form names with double quotes
                sbForm.append(String.join("\" \"", elementNames));
                sbForm.append("\" ]");

                String formDetails = sbForm.toString();
                String tokenNamesFlattened = tokenNames.toString();

                int risk = Alert.RISK_MEDIUM;
                String desc = Constant.messages.getString("pscanrules.noanticsrftokens.desc");
                String extraInfo =
                        Constant.messages.getString(
                                "pscanrules.noanticsrftokens.alert.extrainfo",
                                tokenNamesFlattened,
                                formDetails);
                if (hasSecurityAnnotation) {
                    risk = Alert.RISK_INFO;
                    extraInfo =
                            Constant.messages.getString(
                                    "pscanrules.noanticsrftokens.extrainfo.annotation");
                }

                newAlert()
                        .setRisk(risk)
                        .setConfidence(Alert.CONFIDENCE_LOW)
                        .setDescription(desc + "\n" + getDescription())
                        .setOtherInfo(extraInfo)
                        .setSolution(getSolution())
                        .setReference(getReference())
                        .setEvidence(evidence)
                        .setCweId(getCweId())
                        .setWascId(getWascId())
                        .raise();
            }
        }
        logger.debug("\tScan of record {} took {} ms", id, System.currentTimeMillis() - start);
    }

    private boolean formOnIgnoreList(Element formElement, List<String> ignoreList) {
        String id = formElement.getAttributeValue("id");
        String name = formElement.getAttributeValue("name");
        for (String ignore : ignoreList) {
            if (ignore.equals(id)) {
                logger.debug("Ignoring form with id = {}", id);
                return true;
            } else if (ignore.equals(name)) {
                logger.debug("Ignoring form with name = {}", name);
                return true;
            }
        }
        return false;
    }

    @Override
    public String getName() {
        // do not use the name of the related vulnerability
        // (because we have not actually discovered an instance of this vulnerability class!)
        return Constant.messages.getString("pscanrules.noanticsrftokens.name");
    }

    public String getDescription() {
        if (vuln != null) {
            return vuln.getDescription();
        }
        return "Failed to load vulnerability description from file";
    }

    public String getSolution() {
        if (vuln != null) {
            return vuln.getSolution();
        }
        return "Failed to load vulnerability solution from file";
    }

    public String getReference() {
        if (vuln != null) {
            StringBuilder sb = new StringBuilder();
            for (String ref : vuln.getReferences()) {
                if (sb.length() > 0) {
                    sb.append('\n');
                }
                sb.append(ref);
            }
            return sb.toString();
        }
        return "Failed to load vulnerability reference from file";
    }

    @Override
    public Map<String, String> getAlertTags() {
        return ALERT_TAGS;
    }

    public int getCweId() {
        return 352; // CWE-352: Cross-Site Request Forgery (CSRF)
    }

    public int getWascId() {
        return 9;
    }

    protected ExtensionAntiCSRF getExtensionAntiCSRF() {
        if (extensionAntiCSRF == null) {
            return Control.getSingleton()
                    .getExtensionLoader()
                    .getExtension(ExtensionAntiCSRF.class);
        }
        return extensionAntiCSRF;
    }

    protected void setExtensionAntiCSRF(ExtensionAntiCSRF extensionAntiCSRF) {
        this.extensionAntiCSRF = extensionAntiCSRF;
    }

    protected String getCSRFIgnoreList() {
        if (csrfIgnoreList == null) {
            return Model.getSingleton()
                    .getOptionsParam()
                    .getConfig()
                    .getString(RuleConfigParam.RULE_CSRF_IGNORE_LIST);
        }
        return csrfIgnoreList;
    }

    protected void setCsrfIgnoreList(String csrfIgnoreList) {
        this.csrfIgnoreList = csrfIgnoreList;
    }

    protected String getCSRFIgnoreAttName() {
        if (csrfAttIgnoreList == null) {
            return Model.getSingleton()
                    .getOptionsParam()
                    .getConfig()
                    .getString(RuleConfigParam.RULE_CSRF_IGNORE_ATT_NAME, null);
        }
        return csrfAttIgnoreList;
    }

    protected void setCSRFIgnoreAttName(String csrfAttIgnoreList) {
        this.csrfAttIgnoreList = csrfAttIgnoreList;
    }

    protected String getCSRFIgnoreAttValue() {
        if (csrfValIgnoreList == null) {
            return Model.getSingleton()
                    .getOptionsParam()
                    .getConfig()
                    .getString(RuleConfigParam.RULE_CSRF_IGNORE_ATT_VALUE, null);
        }
        return csrfValIgnoreList;
    }

    protected void setCSRFIgnoreAttValue(String csrfValIgnoreList) {
        this.csrfValIgnoreList = csrfValIgnoreList;
    }
}