text:
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/indexed_db/indexed_db_factory.h"

#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "content/browser/indexed_db/indexed_db_backing_store.h"
#include "content/browser/indexed_db/indexed_db_context_impl.h"
#include "content/browser/indexed_db/indexed_db_tracing.h"
#include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
#include "webkit/common/database/database_identifier.h"

using base::ASCIIToUTF16;

namespace content {

const int64 kBackingStoreGracePeriodMs = 2000;

IndexedDBFactory::IndexedDBFactory(IndexedDBContextImpl* context)
    : context_(context) {}

IndexedDBFactory::~IndexedDBFactory() {}

void IndexedDBFactory::RemoveDatabaseFromMaps(
    const IndexedDBDatabase::Identifier& identifier) {
  IndexedDBDatabaseMap::iterator it = database_map_.find(identifier);
  DCHECK(it != database_map_.end());
  IndexedDBDatabase* database = it->second;
  database_map_.erase(it);

  std::pair<OriginDBMap::iterator, OriginDBMap::iterator> range =
      origin_dbs_.equal_range(database->identifier().first);
  DCHECK(range.first != range.second);
  for (OriginDBMap::iterator it2 = range.first; it2 != range.second; ++it2) {
    if (it2->second == database) {
      origin_dbs_.erase(it2);
      break;
    }
  }
}

void IndexedDBFactory::ReleaseDatabase(
    const IndexedDBDatabase::Identifier& identifier,
    bool forcedClose) {

  DCHECK(!database_map_.find(identifier)->second->backing_store());

  RemoveDatabaseFromMaps(identifier);

  // No grace period on a forced-close, as the initiator is
  // assuming the backing store will be released once all
  // connections are closed.
  ReleaseBackingStore(identifier.first, forcedClose);
}

void IndexedDBFactory::ReleaseBackingStore(const GURL& origin_url,
                                           bool immediate) {
  // Only close if this is the last reference.
  if (!HasLastBackingStoreReference(origin_url))
    return;

  // If this factory does hold the last reference to the backing store, it can
  // be closed - but unless requested to close it immediately, keep it around
  // for a short period so that a re-open is fast.
  if (immediate) {
    CloseBackingStore(origin_url);
    return;
  }

  // Start a timer to close the backing store, unless something else opens it
  // in the mean time.
  DCHECK(!backing_store_map_[origin_url]->close_timer()->IsRunning());
  backing_store_map_[origin_url]->close_timer()->Start(
      FROM_HERE,
      base::TimeDelta::FromMilliseconds(kBackingStoreGracePeriodMs),
      base::Bind(&IndexedDBFactory::MaybeCloseBackingStore, this, origin_url));
}

void IndexedDBFactory::MaybeCloseBackingStore(const GURL& origin_url) {
  // Another reference may have opened since the maybe-close was posted, so it
  // is necessary to check again.
  if (HasLastBackingStoreReference(origin_url))
    CloseBackingStore(origin_url);
}

void IndexedDBFactory::CloseBackingStore(const GURL& origin_url) {
  IndexedDBBackingStoreMap::iterator it = backing_store_map_.find(origin_url);
  DCHECK(it != backing_store_map_.end());
  // Stop the timer (if it's running) - this may happen if the timer was started
  // and then a forced close occurs.
  it->second->close_timer()->Stop();
  backing_store_map_.erase(it);
}

bool IndexedDBFactory::HasLastBackingStoreReference(const GURL& origin_url)
    const {
  IndexedDBBackingStore* ptr;
  {
    // Scope so that the implicit scoped_refptr<> is freed.
    IndexedDBBackingStoreMap::const_iterator it =
        backing_store_map_.find(origin_url);
    DCHECK(it != backing_store_map_.end());
    ptr = it->second.get();
  }
  return ptr->HasOneRef();
}

void IndexedDBFactory::ForceClose(const GURL& origin_url) {
  std::pair<OriginDBMapIterator, OriginDBMapIterator> range =
      GetOpenDatabasesForOrigin(origin_url);

  while (range.first != range.second) {
    IndexedDBDatabase* db = range.first->second;
    ++range.first;
    db->ForceClose();
  }

  if (backing_store_map_.find(origin_url) != backing_store_map_.end())
    ReleaseBackingStore(origin_url, true /* immediate */);
}

void IndexedDBFactory::ContextDestroyed() {
  // Timers on backing stores hold a reference to this factory. When the
  // context (which nominally owns this factory) is destroyed during thread
  // termination the timers must be stopped so that this factory and the
  // stores can be disposed of.
  for (IndexedDBBackingStoreMap::iterator it = backing_store_map_.begin();
       it != backing_store_map_.end();
       ++it)
    it->second->close_timer()->Stop();
  backing_store_map_.clear();
  context_ = NULL;
}

void IndexedDBFactory::GetDatabaseNames(
    scoped_refptr<IndexedDBCallbacks> callbacks,
    const GURL& origin_url,
    const base::FilePath& data_directory) {
  IDB_TRACE("IndexedDBFactory::GetDatabaseNames");
  // TODO(dgrogan): Plumb data_loss back to script eventually?
  blink::WebIDBDataLoss data_loss;
  std::string data_loss_message;
  bool disk_full;
  scoped_refptr<IndexedDBBackingStore> backing_store =
      OpenBackingStore(origin_url,
                       data_directory,
                       &data_loss,
                       &data_loss_message,
                       &disk_full);
  if (!backing_store) {
    callbacks->OnError(
        IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
                               "Internal error opening backing store for "
                               "indexedDB.webkitGetDatabaseNames."));
    return;
  }

  callbacks->OnSuccess(backing_store->GetDatabaseNames());
  backing_store = NULL;
  ReleaseBackingStore(origin_url, false /* immediate */);
}

void IndexedDBFactory::DeleteDatabase(
    const base::string16& name,
    scoped_refptr<IndexedDBCallbacks> callbacks,
    const GURL& origin_url,
    const base::FilePath& data_directory) {
  IDB_TRACE("IndexedDBFactory::DeleteDatabase");
  IndexedDBDatabase::Identifier unique_identifier(origin_url, name);
  IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
  if (it != database_map_.end()) {
    // If there are any connections to the database, directly delete the
    // database.
    it->second->DeleteDatabase(callbacks);
    return;
  }

  // TODO(dgrogan): Plumb data_loss back to script eventually?
  blink::WebIDBDataLoss data_loss;
  std::string data_loss_message;
  bool disk_full = false;
  scoped_refptr<IndexedDBBackingStore> backing_store =
      OpenBackingStore(origin_url,
                       data_directory,
                       &data_loss,
                       &data_loss_message,
                       &disk_full);
  if (!backing_store) {
    callbacks->OnError(
        IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
                               ASCIIToUTF16(
                                   "Internal error opening backing store "
                                   "for indexedDB.deleteDatabase.")));
    return;
  }

  scoped_refptr<IndexedDBDatabase> database =
      IndexedDBDatabase::Create(name, backing_store, this, unique_identifier);
  if (!database) {
    callbacks->OnError(IndexedDBDatabaseError(
        blink::WebIDBDatabaseExceptionUnknownError,
        ASCIIToUTF16(
            "Internal error creating database backend for "
            "indexedDB.deleteDatabase.")));
    return;
  }

  database_map_[unique_identifier] = database;
  origin_dbs_.insert(std::make_pair(origin_url, database));
  database->DeleteDatabase(callbacks);
  RemoveDatabaseFromMaps(unique_identifier);
  database = NULL;
  backing_store = NULL;
  ReleaseBackingStore(origin_url, false /* immediate */);
}

void IndexedDBFactory::DatabaseDeleted(
    const IndexedDBDatabase::Identifier& identifier) {
  // NULL after ContextDestroyed() called, and in some unit tests.
  if (!context_)
    return;
  context_->DatabaseDeleted(identifier.first);
}

void IndexedDBFactory::HandleBackingStoreFailure(const GURL& origin_url) {
  // NULL after ContextDestroyed() called, and in some unit tests.
  if (!context_)
    return;
  context_->ForceClose(origin_url,
                       IndexedDBContextImpl::FORCE_CLOSE_BACKING_STORE_FAILURE);
}

bool IndexedDBFactory::IsDatabaseOpen(const GURL& origin_url,
                                      const base::string16& name) const {

  return !!database_map_.count(IndexedDBDatabase::Identifier(origin_url, name));
}

bool IndexedDBFactory::IsBackingStoreOpen(const GURL& origin_url) const {
  return backing_store_map_.find(origin_url) != backing_store_map_.end();
}

bool IndexedDBFactory::IsBackingStorePendingClose(const GURL& origin_url)
    const {
  IndexedDBBackingStoreMap::const_iterator it =
      backing_store_map_.find(origin_url);
  if (it == backing_store_map_.end())
    return false;
  return it->second->close_timer()->IsRunning();
}

scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore(
    const GURL& origin_url,
    const base::FilePath& data_directory,
    blink::WebIDBDataLoss* data_loss,
    std::string* data_loss_message,
    bool* disk_full) {
  const bool open_in_memory = data_directory.empty();

  IndexedDBBackingStoreMap::iterator it2 = backing_store_map_.find(origin_url);
  if (it2 != backing_store_map_.end()) {
    it2->second->close_timer()->Stop();
    return it2->second;
  }

  scoped_refptr<IndexedDBBackingStore> backing_store;
  if (open_in_memory) {
    backing_store = IndexedDBBackingStore::OpenInMemory(origin_url);
  } else {
    backing_store = IndexedDBBackingStore::Open(origin_url,
                                                data_directory,
                                                data_loss,
                                                data_loss_message,
                                                disk_full);
  }

  if (backing_store.get()) {
    backing_store_map_[origin_url] = backing_store;
    // If an in-memory database, bind lifetime to this factory instance.
    if (open_in_memory)
      session_only_backing_stores_.insert(backing_store);

    // All backing stores associated with this factory should be of the same
    // type.
    DCHECK(session_only_backing_stores_.empty() || open_in_memory);

    return backing_store;
  }

  return 0;
}

void IndexedDBFactory::Open(
    const base::string16& name,
    int64 version,
    int64 transaction_id,
    scoped_refptr<IndexedDBCallbacks> callbacks,
    scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
    const GURL& origin_url,
    const base::FilePath& data_directory) {
  IDB_TRACE("IndexedDBFactory::Open");
  scoped_refptr<IndexedDBDatabase> database;
  IndexedDBDatabase::Identifier unique_identifier(origin_url, name);
  IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
  blink::WebIDBDataLoss data_loss =
      blink::WebIDBDataLossNone;
  std::string data_loss_message;
  bool disk_full = false;
  bool was_open = (it != database_map_.end());
  if (!was_open) {
    scoped_refptr<IndexedDBBackingStore> backing_store =
        OpenBackingStore(origin_url,
                         data_directory,
                         &data_loss,
                         &data_loss_message,
                         &disk_full);
    if (!backing_store) {
      if (disk_full) {
        callbacks->OnError(
            IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError,
                                   ASCIIToUTF16(
                                       "Encountered full disk while opening "
                                       "backing store for indexedDB.open.")));
        return;
      }
      callbacks->OnError(IndexedDBDatabaseError(
          blink::WebIDBDatabaseExceptionUnknownError,
          ASCIIToUTF16(
              "Internal error opening backing store for indexedDB.open.")));
      return;
    }

    database =
        IndexedDBDatabase::Create(name, backing_store, this, unique_identifier);
    if (!database) {
      callbacks->OnError(IndexedDBDatabaseError(
          blink::WebIDBDatabaseExceptionUnknownError,
          ASCIIToUTF16(
              "Internal error creating database backend for indexedDB.open.")));
      return;
    }
  } else {
    database = it->second;
  }

  if (data_loss != blink::WebIDBDataLossNone)
    callbacks->OnDataLoss(data_loss, data_loss_message);

  database->OpenConnection(
      callbacks, database_callbacks, transaction_id, version);

  if (!was_open && database->ConnectionCount() > 0) {
    database_map_[unique_identifier] = database;
    origin_dbs_.insert(std::make_pair(origin_url, database));
  }
}

std::pair<IndexedDBFactory::OriginDBMapIterator,
          IndexedDBFactory::OriginDBMapIterator>
IndexedDBFactory::GetOpenDatabasesForOrigin(const GURL& origin_url) const {
  return origin_dbs_.equal_range(origin_url);
}

size_t IndexedDBFactory::GetConnectionCount(const GURL& origin_url) const {
  size_t count(0);

  std::pair<OriginDBMapIterator, OriginDBMapIterator> range =
      GetOpenDatabasesForOrigin(origin_url);
  for (OriginDBMapIterator it = range.first; it != range.second; ++it)
    count += it->second->ConnectionCount();

  return count;
}

}  // namespace content


text:
module Aws

  # +Aws::Record+ is the module you include in your model classes in order to
  # decorate them with the Amazon DynamoDB integration methods provided by this
  # library. Methods you can use are shown below, in sub-modules organized by
  # functionality.
  #
  # @example A class definition using +Aws::Record+
  #   class MyModel
  #     include Aws::Record
  #     string_attr     :uuid,    hash_key: true
  #     integer_attr    :post_id, range_key: true
  #     boolean_attr    :is_active
  #     datetime_attr   :created_at
  #     string_set_attr :tags
  #     map_attr        :metadata
  #   end
  module Record
    # @!parse extend RecordClassMethods
    # @!parse include Attributes
    # @!parse extend Attributes::ClassMethods
    # @!parse include ItemOperations
    # @!parse extend ItemOperations::ItemOperationsClassMethods
    # @!parse include Query
    # @!parse extend Query::QueryClassMethods
    # @!parse include SecondaryIndexes
    # @!parse extend SecondaryIndexes::SecondaryIndexesClassMethods
    # @!parse include DirtyTracking
    # @!parse extend DirtyTracking::DirtyTrackingClassMethods

    # Usage of {Aws::Record} requires only that you include this module. This
    # method will then pull in the other default modules.
    #
    # @example
    #   class MyTable
    #     include Aws::Record
    #     # Attribute definitions go here...
    #   end
    def self.included(sub_class)
      @track_mutations = true
      sub_class.send(:extend, RecordClassMethods)
      sub_class.send(:include, Attributes)
      sub_class.send(:include, ItemOperations)
      sub_class.send(:include, DirtyTracking)
      sub_class.send(:include, Query)
      sub_class.send(:include, SecondaryIndexes)
    end

    private
    def dynamodb_client
      self.class.dynamodb_client
    end

    module RecordClassMethods

      # Returns the Amazon DynamoDB table name for this model class.
      #
      # By default, this will simply be the name of the class. However, you can
      # also define a custom table name at the class level to be anything that
      # you want.
      #
      # @example
      #   class MyTable
      #     include Aws::Record
      #   end
      #
      #   class MyTableTest
      #     include Aws::Record
      #     set_table_name "test_MyTable"
      #   end
      #
      #   MyTable.table_name      # => "MyTable"
      #   MyOtherTable.table_name # => "test_MyTable"
      def table_name
        if @table_name
          @table_name
        else
          @table_name = self.name.split("::").join("_")
        end
      end

      # Allows you to set a custom Amazon DynamoDB table name for this model
      # class.
      #
      # @example
      #   class MyTable
      #     include Aws::Record
      #     set_table_name "prod_MyTable"
      #   end
      #
      #   class MyTableTest
      #     include Aws::Record
      #     set_table_name "test_MyTable"
      #   end
      #
      #   MyTable.table_name      # => "prod_MyTable"
      #   MyOtherTable.table_name # => "test_MyTable"
      def set_table_name(name)
        @table_name = name
      end

      # Fetches the table's provisioned throughput from the associated Amazon
      # DynamoDB table.
      #
      # @return [Hash] a hash containing the +:read_capacity_units+ and
      #   +:write_capacity_units+ of your remote table.
      # @raise [Aws::Record::Errors::TableDoesNotExist] if the table name does
      #   not exist in DynamoDB.
      def provisioned_throughput
        begin
          resp = dynamodb_client.describe_table(table_name: table_name)
          throughput = resp.table.provisioned_throughput
          return {
            read_capacity_units: throughput.read_capacity_units,
            write_capacity_units: throughput.write_capacity_units
          }
        rescue DynamoDB::Errors::ResourceNotFoundException
          raise Record::Errors::TableDoesNotExist
        end
      end

      # Checks if the model's table name exists in Amazon DynamoDB.
      #
      # @return [Boolean] true if the table does exist, false if it does not.
      def table_exists?
        begin
          resp = dynamodb_client.describe_table(table_name: table_name)
          if resp.table.table_status == "ACTIVE"
            true
          else
            false
          end
        rescue DynamoDB::Errors::ResourceNotFoundException
          false
        end
      end

      # Configures the Amazon DynamoDB client used by this class and all
      # instances of this class.
      #
      # Please note that this method is also called internally when you first
      # attempt to perform an operation against the remote end, if you have not
      # already configured a client. As such, please read and understand the
      # documentation in the AWS SDK for Ruby V2 around
      # {http://docs.aws.amazon.com/sdkforruby/api/index.html#Configuration configuration}
      # to ensure you understand how default configuration behavior works. When
      # in doubt, call this method to ensure your client is configured the way
      # you want it to be configured.
      #
      # @param [Hash] opts the options you wish to use to create the client.
      #  Note that if you include the option +:client+, all other options
      #  will be ignored. See the documentation for other options in the
      #  {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html#initialize-instance_method AWS SDK for Ruby V2}.
      # @option opts [Aws::DynamoDB::Client] :client allows you to pass in your
      #  own pre-configured client.
      def configure_client(opts = {})
        provided_client = opts.delete(:client)
        opts[:user_agent_suffix] = _user_agent(opts.delete(:user_agent_suffix))
        client = provided_client || Aws::DynamoDB::Client.new(opts)
        @dynamodb_client = client
      end

      # Gets the
      # {http://docs.aws.amazon.com/sdkforruby/api/Aws/DynamoDB/Client.html Aws::DynamoDB::Client}
      # instance that this model uses. When called for the first time, if
      # {#configure_client} has not yet been called, will configure a new client
      # for you with default parameters.
      #
      # @return [Aws::DynamoDB::Client] the Amazon DynamoDB client instance.
      def dynamodb_client
        @dynamodb_client ||= configure_client
      end

      # Turns off mutation tracking for all attributes in the model.
      def disable_mutation_tracking
        @track_mutations = false
      end

      # Turns on mutation tracking for all attributes in the model. Note that
      # mutation tracking is on by default, so you generally would not need to
      # call this. It is provided in case there is a need to dynamically turn
      # this feature on and off, though that would be generally discouraged and
      # could cause inaccurate mutation tracking at runtime.
      def enable_mutation_tracking
        @track_mutations = true
      end

      # @return [Boolean] true if mutation tracking is enabled at the model
      # level, false otherwise.
      def mutation_tracking_enabled?
        @track_mutations == false ? false : true
      end

      def model_valid?
        if @keys.hash_key.nil?
          raise Errors::InvalidModel.new("Table models must include a hash key")
        end
      end

      private
      def _user_agent(custom)
        if custom
          custom
        else
          " aws-record/#{VERSION}"
        end
      end
    end
  end
end

redpajama_set_name: RedPajamaGithub


text:
package com.hurence.logisland.record;

import com.hurence.logisland.logging.ComponentLog;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Set;

public interface Record extends Serializable {
    Date getTime();

    Record setTime(Date recordTime);

    Record setFields(Map<String, Field> fields);

    Record addFields(Map<String, Field> fields);

    Record setType(String type);

    String getType();

    Record setId(String id);

    String getId();

    boolean hasField(String fieldName);

    Record setField(Field field);

    Record setField(String fieldName, FieldType fieldType, Object value);

    Record setStringField(String fieldName, String value);

    Field removeField(String fieldName);

    Field getField(String fieldName);

    Record setStringFields(Map<String, String> entrySets);

    Collection<Field> getAllFieldsSorted();

    Collection<Field> getAllFields();

    Set<String> getAllFieldNames();

    Set<Map.Entry<String, Field>> getFieldsEntrySet();

    boolean isEmpty();

    boolean isValid();

    int size();

    int sizeInBytes();

    Record addError(String errorType, String errorMessage);

    Record addError(String errorType);

    Record addError(String errorType, ComponentLog logger, String errorMessage);

    Record addError(String errorType, ComponentLog logger, String errorMessage, Object[] os);

    Collection<String> getErrors();
}


text:
Files List

resin4
├─lib
│  └─javax.mail-1.5.6.jar								//Java Mail jar.
└─webapps
        myWebApp											//The application folder.
        ├─myPage.jsp
        ├─...
        └─WEB-INF
            ├─classes
            │  └─efw.properties
            ├─efw
            │  ├─event									//Efw event folder
            │  │  ├─myEvent.js
            │  │  └─...
            │  ├─mail										//Outside mail folder
            │  │  ├─mails.xml
            │  │  └─...
            │  ├─sql										//Outside sql folder
            │  │  ├─mySqlGroup.xml
            │  │  └─...
            │  └─storage									//Storage folder
            │      └─...
            └─lib											//Lib folder
                ├─...
                ├─efw-3.#.###.jar
                ├─efw-addition-tags-3.#.###.jar
                └─poi_3.15_allinone.jar

Java Mail JAR

You must put javax.mail.###.jar in the tomcat lib folder. Or the web app initialization will be wrong.

Application Folder

You can put jsp files here. It is not recommended to create sub jsp folder. If you do it, please remember to put a base tag in you jsp head, or the <efw:Client> will be wrong.

<base href="/myWebApp/">

Efw Event Folder

You should add your event files here. And you can create sub folders.

Outside SQL Folder

You should add your sql defines here. And you can create sub folders.

Outside Mail Folder

You should add your mail defines here. And you can create sub folders.

Context File

The context file contains the db resource define and the mail resource define.

Properties File

The properties file contains the setting to the web application.

Storage Folder

All files your application needs should be placed here. In your event programs, file and folder operating is relatived to the storage folder.

Lib Folder

All jars are here. You can put or replace the jars.


text:
class CefWriteHandlerCToCpp
    : public CefCToCpp<CefWriteHandlerCToCpp, CefWriteHandler,
        cef_write_handler_t> {
 public:
  explicit CefWriteHandlerCToCpp(cef_write_handler_t* str)
      : CefCToCpp<CefWriteHandlerCToCpp, CefWriteHandler, cef_write_handler_t>(
          str) {}
  virtual ~CefWriteHandlerCToCpp() {}

  // CefWriteHandler methods
  virtual size_t Write(const void* ptr, size_t size, size_t n) OVERRIDE;
  virtual int Seek(int64 offset, int whence) OVERRIDE;
  virtual int64 Tell() OVERRIDE;
  virtual int Flush() OVERRIDE;
};

#endif  // BUILDING_CEF_SHARED
#endif  // CEF_LIBCEF_DLL_CTOCPP_WRITE_HANDLER_CTOCPP_H_


text:
"""
XPath 2.0 implementation - part 3 (functions)
"""
import math
import datetime
import time
import re
import locale
import os.path
import unicodedata
from copy import copy
from decimal import Decimal, DecimalException
from string import ascii_letters
from urllib.parse import urlsplit, quote as urllib_quote

from ..exceptions import ElementPathValueError
from ..helpers import is_idrefs, is_xml_codepoint, round_number
from ..datatypes import QNAME_PATTERN, DateTime10, DateTime, Date10, Date, \
    Float10, DoubleProxy, Time, Duration, DayTimeDuration, YearMonthDuration, \
    UntypedAtomic, AnyURI, QName, NCName, Id, ArithmeticProxy, NumericProxy
from ..namespaces import XML_NAMESPACE, get_namespace, split_expanded_name, \
    XML_BASE, XML_ID, XML_LANG
from ..etree import etree_deep_equal
from ..xpath_context import XPathSchemaContext
from ..xpath_nodes import XPathNode, DocumentNode, ElementNode, AttributeNode, \
    NamespaceNode, CommentNode, ProcessingInstructionNode
from ..xpath_token import XPathFunction
from ..regex import RegexError, translate_pattern
from ._xpath2_operators import XPath2Parser

method = XPath2Parser.method
function = XPath2Parser.function

def is_local_url_scheme(scheme):
    return scheme in ('', 'file') or len(scheme) == 1 and scheme in ascii_letters

def is_local_dir_url(url):
    url_parts = urlsplit(url)
    return is_local_url_scheme(url_parts.scheme) and os.path.isdir(url_parts.path.lstrip(':'))

###
# Sequence types (allowed only for type checking in treat-as/instance-of statements)
function('empty-sequence', nargs=0, label='sequence type')

@method(function('item', nargs=0, label='sequence type'))
def evaluate_item_sequence_type(self, context=None):
    if context is None:
        raise self.missing_context()
    return context.root if context.item is None else context.item

@method('item')
def nud_item_sequence_type(self):
    XPathFunction.nud(self)
    if self.parser.next_token.symbol in ('*', '+', '?'):
        self.occurrence = self.parser.next_token.symbol
        self.parser.advance()
    return self

###
# Function for QNames
@method(function('prefix-from-QName', nargs=1,
                 sequence_types=('xs:QName?', 'xs:NCName?')))
def evaluate_prefix_from_qname_function(self, context=None):
    qname = self.get_argument(context)
    if qname is None:
        return []
    elif not isinstance(qname, QName):
        raise self.error('XPTY0004', 'argument has an invalid type %r' % type(qname))
    return NCName(qname.prefix) if qname.prefix else []

@method(function('local-name-from-QName', nargs=1,
                 sequence_types=('xs:QName?', 'xs:NCName?')))
def evaluate_local_name_from_qname_function(self, context=None):
    qname = self.get_argument(context)
    if qname is None:
        return
    elif not isinstance(qname, QName):
        if self.parser.version >= '3.0' and \
                isinstance(self.data_value(qname), UntypedAtomic):
            code = 'XPTY0117'
        else:
            code = 'XPTY0004'
        raise self.error(code, 'argument has an invalid type %r' % type(qname))
    return NCName(qname.local_name)

@method(function('namespace-uri-from-QName', nargs=1,
                 sequence_types=('xs:QName?', 'xs:anyURI?')))
def evaluate_uri_from_qname_function(self, context=None):
    qname = self.get_argument(context)
    if qname is None:
        return
    elif not isinstance(qname, QName):
        if self.parser.version >= '3.0' and \
                isinstance(self.data_value(qname), UntypedAtomic):
            code = 'XPTY0117'
        else:
            code = 'XPTY0004'
        raise self.error(code, 'argument has an invalid type %r' % type(qname))
    return AnyURI(qname.uri or '')

@method(function('namespace-uri-for-prefix', nargs=2,
                 sequence_types=('xs:string?', 'element()', 'xs:anyURI?')))
def evaluate_namespace_uri_for_prefix_function(self, context=None):
    if context is None:
        raise self.missing_context()

    prefix = self.get_argument(context=copy(context))
    if prefix is None:
        prefix = ''
    if not isinstance(prefix, str):
        raise self.error('FORG0006', '1st argument has an invalid type %r' % type(prefix))

    elem = self.get_argument(context, index=1)
    if not isinstance(elem, ElementNode):
        raise self.error('FORG0006', '2nd argument %r is not an element node' % elem)

    ns_uris = {get_namespace(e.tag) for e in elem.elem.iter() if not callable(e.tag)}
    for p, uri in self.parser.namespaces.items():
        if uri in ns_uris:
            if p == prefix:
                if not prefix or uri:
                    return AnyURI(uri)
                else:
                    msg = 'Prefix %r is associated to no namespace'
                    raise self.error('XPST0081', msg % prefix)

@method(function('in-scope-prefixes', nargs=1, sequence_types=('element()', 'xs:string*')))
def select_in_scope_prefixes_function(self, context=None):
    if context is None:
        raise self.missing_context()

    arg = self.get_argument(context, required=True)
    if not isinstance(arg, ElementNode):
        raise self.error('XPTY0004', 'argument %r is not an element node' % arg)

    elem = arg.elem
    if isinstance(context, XPathSchemaContext):
        # For schema context returns prefixes of static namespaces
        for pfx, uri in self.parser.namespaces.items():
            if uri:
                yield pfx or ''
    elif hasattr(elem, 'nsmap'):
        # For lxml returns Element nsmap prefixes, replacing None with ''
        if 'xml' not in elem.nsmap:
            yield 'xml'
        for pfx, uri in elem.nsmap.items():
            if uri:
                yield pfx or ''
    else:
        # For ElementTree returns module registered prefixes
        for pfx, uri in self.parser.namespaces.items():
            if uri:
                yield pfx or ''

        if context.namespaces:
            yield from (x for x in context.namespaces if x not in self.parser.namespaces)

@method(function('resolve-QName', nargs=2,
                 sequence_types=('xs:string?', 'element()', 'xs:QName?')))
def evaluate_resolve_qname_function(self, context=None):
    qname = self.get_argument(context=copy(context))
    if qname is None:
        return
    elif not isinstance(qname, str):
        raise self.error('FORG0006', '1st argument has an invalid type %r' % type(qname))

    if context is None:
        raise self.missing_context()

    elem = self.get_argument(context, index=1)
    if not isinstance(elem, ElementNode):
        raise self.error('FORG0006', '2nd argument %r is not an element node' % elem)

    qname = qname.strip()
    match = QNAME_PATTERN.match(qname)
    if match is None:
        raise self.error('FOCA0002', '1st argument must be an xs:QName')

    prefix = match.groupdict()['prefix'] or ''
    if prefix == 'xml':
        return QName(XML_NAMESPACE, qname)

    try:
        nsmap = elem.nsmap
    except AttributeError:
        nsmap = self.parser.namespaces

    for pfx, uri in nsmap.items():
        if pfx is None:
            pfx = ''
        if pfx == prefix:
            if pfx:
                return QName(uri, '{}:{}'.format(pfx, match.groupdict()['local']))
            else:
                return QName(uri, match.groupdict()['local'])

    if prefix or '' in nsmap or None in nsmap:
        raise self.error('FONS0004', 'no namespace found for prefix %r' % prefix)
    return QName('', qname)

###
# Accessor functions
@method(function('node-name', nargs=1, sequence_types=('node()?', 'xs:QName?')))
def evaluate_node_name_function(self, context=None):
    arg = self.get_argument(context)
    if arg is None:
        return None
    elif not isinstance(arg, XPathNode):
        raise self.error('XPTY0004', 'an XPath node required')

    name = arg.name
    if name is None:
        return
    elif name.startswith('{'):
        # name is a QName in extended format
        namespace, local_name = split_expanded_name(name)
        for pfx, uri in self.parser.namespaces.items():
            if uri == namespace:
                return QName(uri, '{}:{}'.format(pfx, local_name))
        raise self.error('FONS0004', 'no prefix found for namespace {}'.format(namespace))
    else:
        # name is a local name
        return QName(self.parser.namespaces.get('', ''), name)

@method(function('nilled', nargs=1, sequence_types=('node()?', 'xs:boolean?')))
def evaluate_nilled_function(self, context=None):
    arg = self.get_argument(context)
    if arg is None:
        return None
    elif not isinstance(arg, XPathNode):
        raise self.error('XPTY0004', 'an XPath node required')
    return arg.nilled

@method(function('data', nargs=1, sequence_types=('item()*', 'xs:anyAtomicType*')))
def select_data_function(self, context=None):
    yield from self[0].atomization(context)

@method(function('base-uri', nargs=(0, 1), sequence_types=('node()?', 'xs:anyURI?')))
def evaluate_base_uri_function(self, context=None):
    item = self.get_argument(context, default_to_context=True)
    if context is None:
        raise self.missing_context("context item is undefined")
    elif item is None:
        return None
    elif not isinstance(item, XPathNode):
        raise self.wrong_context_type("context item is not a node")
    elif isinstance(item, DocumentNode):
        base_uri = item.document.getroot().get(XML_BASE)
        return AnyURI(base_uri if base_uri is not None else '')
    else:
        context.item = item
        base_uri = []
        for item in context.iter_ancestors(axis='ancestor-or-self'):
            if isinstance(item, ElementNode):
                uri = item.elem.get(XML_BASE)
                if uri is not None:
                    if base_uri and urlsplit(uri).scheme:
                        break

                    base_uri.append(uri)
                    if not urlsplit(uri).path.endswith('/'):
                        break

        return AnyURI(''.join(base_uri))

@method(function('document-uri', nargs=1, sequence_types=('node()?', 'xs:anyURI?')))
def evaluate_document_uri_function(self, context=None):
    if context is None:
        raise self.missing_context()

    arg = self.get_argument(context)
    if isinstance(arg, DocumentNode):
        uri = arg.document_uri
        if uri is not None:
            return AnyURI(uri)
        elif isinstance(context.root, DocumentNode):
            if context.documents:
                for uri, doc in context.documents.items():
                    if doc and doc.document is context.root.document:
                        return AnyURI(uri)

    return None

###
# Number functions
@method(function('round-half-to-even', nargs=(1, 2),
                 sequence_types=('numeric?', 'xs:integer', 'numeric?')))
def evaluate_round_half_to_even_function(self, context=None):
    item = self.get_argument(context)
    if item is None:
        return
    elif isinstance(item, float) and (math.isnan(item) or math.isinf(item)):
        return item
    elif not isinstance(item, (float, int, Decimal)):
        code = 'XPTY0004' if isinstance(item, str) else 'FORG0006'
        raise self.error(code, "invalid argument type {!r}".format(type(item)))

    precision = 0 if len(self) < 2 else self[1].evaluate(context)
    try:
        if isinstance(item, int):
            return round(item, precision)
        elif isinstance(item, Decimal):
            return round(item, precision)
        elif isinstance(item, Float10):
            return Float10(round(item, precision))
        return float(round(Decimal.from_float(item), precision))
    except TypeError as err:
        raise self.error('XPTY0004', err)
    except (DecimalException, OverflowError):
        if isinstance(item, Decimal):
            return Decimal.from_float(round(float(item), precision))
        return round(item, precision)

@method(function('abs', nargs=1, sequence_types=('numeric?', 'numeric?')))
def evaluate_abs_function(self, context=None):
    item = self.get_argument(context)
    if item is None:
        return
    elif isinstance(item, float) and math.isnan(item):
        return item
    elif isinstance(item, XPathNode):
        value = self.string_value(item)
        try:
            return abs(Decimal(value))
        except DecimalException:
            raise self.error('FOCA0002', "invalid string value {!r} for {!r}".format(value, item))
    elif isinstance(item, bool) or not isinstance(item, (float, int, Decimal)):
        raise self.error('XPTY0004', "invalid argument type {!r}".format(type(item)))
    else:
        return abs(item)

###
# Aggregate functions
@method(function('avg', nargs=1, sequence_types=('xs:anyAtomicType*', 'xs:anyAtomicType')))
def evaluate_avg_function(self, context=None):
    values = []
    for item in self[0].atomization(context):
        if isinstance(item, UntypedAtomic):
            values.append(self.cast_to_double(item.value))
        elif isinstance(item, (AnyURI, bool)):
            raise self.error('FORG0006', 'non numeric value {!r} in the sequence'.format(item))
        else:
            values.append(item)

    if not values:
        return
    elif isinstance(values[0], Duration):
        value = values[0]
        try:
            for item in values[1:]:
                value = value + item
            return value / len(values)
        except TypeError as err:
            raise self.error('FORG0006', err)
    elif all(isinstance(x, int) for x in values):
        result = sum(values) / Decimal(len(values))
        return int(result) if result % 1 == 0 else result
    elif all(isinstance(x, (int, Decimal)) for x in values):
        return sum(values) / Decimal(len(values))
    elif all(not isinstance(x, DoubleProxy) for x in values):
        try:
            return sum(Float10(x) if isinstance(x, Decimal) else x for x in values) / len(values)
        except TypeError as err:
            raise self.error('FORG0006', err)
    else:
        try:
            return sum(float(x) if isinstance(x, Decimal) else x for x in values) / len(values)
        except TypeError as err:
            raise self.error('FORG0006', err)

@method(function('max', nargs=(1, 2),
                 sequence_types=('xs:anyAtomicType*', 'xs:string', 'xs:anyAtomicType?')))
@method(function('min', nargs=(1, 2),
                 sequence_types=('xs:anyAtomicType*', 'xs:string', 'xs:anyAtomicType?')))
def evaluate_max_min_functions(self, context=None):

    def max_or_min():
        if not values:
            return values
        elif all(isinstance(x, str) for x in values):
            if to_any_uri:
                return AnyURI(aggregate_func(values))
        elif any(isinstance(x, str) for x in values):
            if any(isinstance(x, ArithmeticProxy) for x in values):
                raise self.error('FORG0006', "cannot compare strings with numeric data")
        elif all(isinstance(x, (Decimal, int)) for x in values):
            return aggregate_func(values)
        elif any(isinstance(x, float) and math.isnan(x) for x in values):
            return float_class('NaN')
        elif all(isinstance(x, (int, float, Decimal)) for x in values):
            return float_class(aggregate_func(values))
        return aggregate_func(values)

    values = []
    float_class = None
    to_any_uri = None
    aggregate_func = max if self.symbol == 'max' else min

    for item in self[0].atomization(context):
        if isinstance(item, UntypedAtomic):
            values.append(self.cast_to_double(item))
            float_class = float
        elif isinstance(item, float):
            values.append(item)
            if float_class is None:
                float_class = type(item)
            elif float_class is Float10 and not isinstance(item, Float10):
                float_class = float
        elif isinstance(item, AnyURI):
            values.append(item.value)
            if to_any_uri is None:
                to_any_uri = True
        elif isinstance(item, (DayTimeDuration, YearMonthDuration)):
            values.append(item)
        elif isinstance(item, (Duration, QName)):
            raise self.error('FORG0006', "xs:{} is not an ordered type".format(type(item).name))
        else:
            to_any_uri = False
            values.append(item)

    try:
        if len(self) > 1:
            with self.use_locale(collation=self.get_argument(context, 1)):
                return max_or_min()
        return max_or_min()
    except TypeError as err:
        raise self.error('FORG0006', err)

###
# General functions for sequences
@method(function('empty', nargs=1, sequence_types=('item()*', 'xs:boolean')))
@method(function('exists', nargs=1, sequence_types=('item()*', 'xs:boolean')))
def evaluate_empty_and_exists_functions(self, context=None):
    return next(iter(self.select(context)))

@method('empty')
def select_empty_function(self, context=None):
    try:
        next(iter(self[0].select(context)))
    except StopIteration:
        yield True
    else:
        yield False

@method('exists')
def select_exists_function(self, context=None):
    try:
        next(iter(self[0].select(context)))
    except StopIteration:
        yield False
    else:
        yield True

@method(function('distinct-values', nargs=(1, 2),
                 sequence_types=('xs:anyAtomicType*', 'xs:string', 'xs:anyAtomicType*')))
def select_distinct_values_function(self, context=None):

    def distinct_values():
        nan = False
        results = []
        for value in self[0].atomization(context):
            if isinstance(value, (float, Decimal)):
                if math.isnan(value):
                    if not nan:
                        yield value
                        nan = True
                elif all(not math.isclose(value, x, rel_tol=1E-18, abs_tol=0)
                         for x in results if isinstance(x, (int, Decimal, float))):
                    yield value
                    results.append(value)

            elif value not in results:
                yield value
                results.append(value)

    if len(self) > 1:
        with self.use_locale(collation=self.get_argument(context, 1)):
            yield from distinct_values()
    else:
        yield from distinct_values()

@method(function('insert-before', nargs=3,
                 sequence_types=('item()*', 'xs:integer', 'item()*', 'item()*')))
def select_insert_before_function(self, context=None):
    position = self.get_argument(context, 1, required=True, cls=int)
    insert_at_pos = max(0, position - 1)

    inserted = False
    for pos, result in enumerate(self[0].select(context)):
        if not inserted and pos == insert_at_pos:
            yield from self[2].select(context)
            inserted = True
        yield result

    if not inserted:
        yield from self[2].select(context)

@method(function('index-of', nargs=(2, 3), sequence_types=(
        'xs:anyAtomicType*', 'xs:anyAtomicType', 'xs:string', 'xs:integer*')))
def select_index_of_function(self, context=None):
    value = self[1].get_atomized_operand(copy(context))
    if value is None:
        raise self.error('XPTY0004', "2nd argument cannot be an empty sequence")

    if len(self) < 3:
        for pos, result in enumerate(self[0].atomization(context), start=1):
            if result == value:
                yield pos
    else:
        with self.use_locale(collation=self.get_argument(context, 2)):
            for pos, result in enumerate(self[0].atomization(context), start=1):
                if result == value:
                    yield pos

@method(function('remove', nargs=2, sequence_types=('item()*', 'xs:integer', 'item()*')))
def select_remove_function(self, context=None):
    position = self.get_argument(context, 1)
    if not isinstance(position, int):
        raise self.error('XPTY0004', 'an xs:integer required')

    for pos, result in enumerate(self[0].select(context), start=1):
        if pos != position:
            yield result

@method(function('reverse', nargs=1, sequence_types=('item()*', 'item()*')))
def select_reverse_function(self, context=None):
    yield from reversed([x for x in self[0].select(context)])

@method(function('subsequence', nargs=(2, 3),
                 sequence_types=('item()*', 'xs:double', 'xs:double', 'item()*')))
def select_subsequence_function(self, context=None):
    starting_loc = self.get_argument(context, 1, cls=NumericProxy)
    if not math.isnan(starting_loc) and not math.isinf(starting_loc):
        starting_loc = float(round_number(starting_loc))

    if len(self) == 2:
        for pos, result in enumerate(self[0].select(context), start=1):
            if starting_loc <= pos:
                yield result
    else:
        length = self.get_argument(context, 2, cls=NumericProxy)
        if not math.isnan(length) and not math.isinf(length):
            length = float(round_number(length))

        for pos, result in enumerate(self[0].select(context), start=1):
            if starting_loc <= pos < starting_loc + length:
                yield result

@method(function('unordered', nargs=1, sequence_types=('item()*', 'item()*')))
def select_unordered_function(self, context=None):
    yield from sorted([x for x in self[0].select(context)], key=lambda x: self.string_value(x))

###
# Cardinality functions for sequences
@method(function('zero-or-one', nargs=1, sequence_types=('item()*', 'item()?')))
def select_zero_or_one_function(self, context=None):
    results = iter(self[0].select(context))
    try:
        item = next(results)
    except StopIteration:
        return

    try:
        next(results)
    except StopIteration:
        yield item
    else:
        raise self.error('FORG0003')

@method(function('one-or-more', nargs=1, sequence_types=('item()*', 'item()+')))
def select_one_or_more_function(self, context=None):
    results = iter(self[0].select(context))
    try:
        item = next(results)
    except StopIteration:
        raise self.error('FORG0004') from None
    else:
        yield item
        while True:
            try:
                yield next(results)
            except StopIteration:
                break

@method(function('exactly-one', nargs=1, sequence_types=('item()*', 'item()')))
def select_exactly_one_function(self, context=None):
    results = iter(self[0].select(context))
    try:
        item = next(results)
    except StopIteration:
        raise self.error('FORG0005') from None
    else:
        try:
            next(results)
        except StopIteration:
            yield item
        else:
            raise self.error('FORG0005')

###
# Comparing sequences
@method(function('deep-equal', nargs=(2, 3),
                 sequence_types=('item()*', 'item()*', 'xs:string', 'xs:boolean')))
def evaluate_deep_equal_function(self, context=None):

    def deep_equal():
        while True:
            value1 = next(seq1, None)
            value2 = next(seq2, None)
            if isinstance(value1, XPathFunction) or isinstance(value2, XPathFunction):
                raise self.error('FOTY0015')

            if (value1 is None) ^ (value2 is None):
                return False
            elif value1 is None:
                return True
            elif isinstance(value1, XPathNode) ^ isinstance(value2, XPathNode):
                return False
            elif not isinstance(value1, XPathNode):
                try:
                    if isinstance(value1, bool):
                        if not isinstance(value2, bool) or value1 is not value2:
                            return False

                    elif isinstance(value2, bool):
                        return False

                    elif isinstance(value1, UntypedAtomic):
                        if not isinstance(value2, UntypedAtomic) or value1 != value2:
                            return False

                    elif isinstance(value2, UntypedAtomic):
                        return False

                    elif isinstance(value1, float):
                        if math.isnan(value1):
                            if not math.isnan(value2):
                                return False
                        elif math.isinf(value1):
                            if value1 != value2:
                                return False
                        elif isinstance(value2, Decimal):
                            if value1 != float(value2):
                                return False
                        elif not isinstance(value2, (value1.__class__, int)):
                            return False
                        elif value1 != value2:
                            return False

                    elif isinstance(value2, float):
                        if math.isnan(value2):
                            return False
                        elif math.isinf(value2):
                            if value1 != value2:
                                return False
                        elif isinstance(value1, Decimal):
                            if value2 != float(value1):
                                return False
                        elif not isinstance(value1, (value2.__class__, int)):
                            return False
                        elif value1 != value2:
                            return False

                    elif value1 != value2:
                        return False
                except TypeError:
                    return False
            elif value1.kind != value2.kind:
                return False
            elif isinstance(value1, (ElementNode, CommentNode, ProcessingInstructionNode)):
                if not etree_deep_equal(value1.elem, value2.elem):
                    return False
            elif isinstance(value1, DocumentNode):
                if not etree_deep_equal(value1.document.getroot(), value2.document.getroot()):
                    return False
            elif value1.value != value2.value:
                return False
            elif isinstance(value1, AttributeNode):
                if value1.name != value2.name:
                    return False
            elif isinstance(value1, NamespaceNode):
                if value1.prefix != value2.prefix:
                    return False

    seq1 = iter(self[0].select(copy(context)))
    seq2 = iter(self[1].select(copy(context)))

    if len(self) > 2:
        with self.use_locale(collation=self.get_argument(context, 2)):
            return deep_equal()
    else:
        return deep_equal()

###
# Regex
@method(function('matches', nargs=(2, 3),
                 sequence_types=('xs:string?', 'xs:string', 'xs:string', 'xs:boolean')))
def evaluate_matches_function(self, context=None):
    input_string = self.get_argument(context, default='', cls=str)
    pattern = self.get_argument(context, 1, required=True, cls=str)
    flags = 0
    if len(self) > 2:
        for c in self.get_argument(context, 2, required=True, cls=str):
            if c in 'smix':
                flags |= getattr(re, c.upper())
            elif c == 'q' and self.parser.version > '2':
                pattern = re.escape(pattern)
            else:
                raise self.error('FORX0001', "Invalid regular expression flag %r" % c)

    try:
        python_pattern = translate_pattern(pattern, flags, self.parser.xsd_version)
        return re.search(python_pattern, input_string, flags=flags) is not None
    except (re.error, RegexError) as err:
        msg = "Invalid regular expression: {}"
        raise self.error('FORX0002', msg.format(str(err))) from None
    except OverflowError as err:
        raise self.error('FORX0002', err) from None

REPLACEMENT_PATTERN = re.compile(r'^([^\\$]|[\\]{2}|\\\$|\$\d+)*$')

@method(function('replace', nargs=(3, 4), sequence_types=(
        'xs:string?', 'xs:string', 'xs:string', 'xs:string', 'xs:string')))
def evaluate_replace_function(self, context=None):
    input_string = self.get_argument(context, default='', cls=str)
    pattern = self.get_argument(context, 1, required=True, cls=str)
    replacement = self.get_argument(context, 2, required=True, cls=str)
    flags = 0
    q_flag = False
    if len(self) > 3:
        for c in self.get_argument(context, 3, required=True, cls=str):
            if c in 'smix':
                flags |= getattr(re, c.upper())
            elif c == 'q' and self.parser.version > '2':
                pattern = re.escape(pattern)
                q_flag = True
            else:
                raise self.error('FORX0001', "Invalid regular expression flag %r" % c)

    try:
        python_pattern = translate_pattern(pattern, flags, self.parser.xsd_version)
        pattern = re.compile(python_pattern, flags=flags)
    except (re.error, RegexError):
        raise self.error('FORX0002', "Invalid regular expression %r" % pattern)
    else:
        if pattern.search(''):
            msg = "Regular expression %r matches zero-length string"
            raise self.error('FORX0003', msg % pattern.pattern)
        elif q_flag:
            # use replacement string as is (but inactivating escapes)
            replacement = replacement.replace('\\', '\\\\')
            input_string = input_string.replace('\\', '\\\\')
            return pattern.sub(replacement, input_string).replace('\\\\', '\\')

        elif REPLACEMENT_PATTERN.search(replacement) is None:
            raise self.error('FORX0004', "Invalid replacement string %r" % replacement)
        else:
            for g in range(pattern.groups, -1, -1):
                if '$%d' % g in replacement:
                    replacement = re.sub(r'(?<!\\)\$%d' % g, r'\\g<%d>' % g, replacement)

            return pattern.sub(replacement, input_string).replace('\\$', '$')

@method(function('tokenize', nargs=(2, 3),
                 sequence_types=('xs:string?', 'xs:string', 'xs:string', 'xs:string*')))
def select_tokenize_function(self, context=None):
    input_string = self.get_argument(context, cls=str)
    pattern = self.get_argument(context, 1, required=True, cls=str)
    flags = 0
    if len(self) > 2:
        for c in self.get_argument(context, 2, required=True, cls=str):
            if c in 'smix':
                flags |= getattr(re, c.upper())
            elif c == 'q' and self.parser.version > '2':
                pattern = re.escape(pattern)
            else:
                raise self.error('FORX0001', "Invalid regular expression flag %r" % c)

    try:
        python_pattern = translate_pattern(pattern, flags, self.parser.xsd_version)
        pattern = re.compile(python_pattern, flags=flags)
    except (re.error, RegexError):
        raise self.error('FORX0002', "Invalid regular expression %r" % pattern) from None
    else:
        if pattern.search(''):
            msg = "Regular expression %r matches zero-length string"
            raise self.error('FORX0003', msg % pattern.pattern)

    if input_string:
        for value in pattern.split(input_string):
            if value is not None and pattern.search(value) is None:
                yield value

###
# Functions on anyURI
@method(function('resolve-uri', nargs=(1, 2),
                 sequence_types=('xs:string?', 'xs:string', 'xs:anyURI?')))
def evaluate_resolve_uri_function(self, context=None):
    relative = self.get_argument(context, cls=str)
    if len(self) == 1:
        if self.parser.base_uri is None:
            raise self.error('FONS0005')
        elif relative is None:
            return
        elif not AnyURI.is_valid(relative):
            raise self.error('FORG0002', '{!r} is not a valid URI'.format(relative))
        else:
            return self.get_absolute_uri(relative, as_string=False)

    base_uri = self.get_argument(context, index=1, required=True, cls=str)
    if not AnyURI.is_valid(base_uri):
        raise self.error('FORG0002', '{!r} is not a valid URI'.format(base_uri))
    elif relative is None:
        return
    elif not AnyURI.is_valid(relative):
        raise self.error('FORG0002', '{!r} is not a valid URI'.format(relative))
    else:
        return self.get_absolute_uri(relative, base_uri, as_string=False)

###
# String functions

@method(function('codepoints-to-string', nargs=1,
                 sequence_types=('xs:integer*', 'xs:string')))
def evaluate_codepoints_to_string_function(self, context=None):
    result = []
    for value in self[0].select(context):
        if isinstance(value, UntypedAtomic):
            value = int(value)

        if not isinstance(value, int):
            msg = "invalid type {} for codepoint {}".format(type(value), value)
            if isinstance(value, str):
                raise self.error('XPTY0004', msg)
            raise self.error('FORG0006', msg)
        elif is_xml_codepoint(value):
            result.append(chr(value))
        else:
            msg = "{} is not a valid XML 1.0 codepoint".format(value)
            raise self.error('FOCH0001', msg)

    return ''.join(result)

@method(function('string-to-codepoints', nargs=1,
                 sequence_types=('xs:string?', 'xs:integer*')))
def evaluate_string_to_codepoints_function(self, context=None):
    arg = self.get_argument(context, cls=str)
    return [ord(c) for c in arg] if arg else None

@method(function('compare', nargs=(2, 3),
                 sequence_types=('xs:string?', 'xs:string?', 'xs:string', 'xs:integer?')))
def evaluate_compare_function(self, context=None):
    comp1 = self.get_argument(context, 0, cls=str, promote=(AnyURI, UntypedAtomic))
    comp2 = self.get_argument(context, 1, cls=str, promote=(AnyURI, UntypedAtomic))
    if comp1 is None or comp2 is None:
        return None

    if len(self) < 3:
        value = locale.strcoll(comp1, comp2)
    else:
        with self.use_locale(collation=self.get_argument(context, 2)):
            value = locale.strcoll(comp1, comp2)

    return 0 if not value else 1 if value > 0 else -1

@method(function('contains', nargs=(2, 3),
                 sequence_types=('xs:string?', 'xs:string?', 'xs:string', 'xs:boolean')))
def evaluate_contains_function(self, context=None):
    arg1 = self.get_argument(context, default='', cls=str)
    arg2 = self.get_argument(context, index=1, default='', cls=str)

    if len(self) < 3:
        return arg2 in arg1
    else:
        with self.use_locale(collation=self.get_argument(context, 2)):
            return arg2 in arg1

@method(function('codepoint-equal', nargs=2,
                 sequence_types=('xs:string?', 'xs:string?', 'xs:boolean?')))
def evaluate_codepoint_equal_function(self, context=None):
    comp1 = self.get_argument(context, 0, cls=str)
    comp2 = self.get_argument(context, 1, cls=str)
    if comp1 is None or comp2 is None:
        return
    elif len(comp1) != len(comp2):
        return False
    else:
        return all(ord(c1) == ord(c2) for c1, c2 in zip(comp1, comp2))

@method(function('string-join', nargs=2,
                 sequence_types=('xs:string*', 'xs:string', 'xs:string')))
def evaluate_string_join_function(self, context=None):
    items = [
        self.validated_value(s, cls=str, promote=AnyURI)
        for s in self[0].atomization(context)
    ]
    return self.get_argument(context, 1, required=True, cls=str).join(items)

@method(function('normalize-unicode', nargs=(1, 2),
                 sequence_types=('xs:string?', 'xs:string', 'xs:string')))
def evaluate_normalize_unicode_function(self, context=None):
    arg = self.get_argument(context, default='', cls=str)
    if len(self) > 1:
        normalization_form = self.get_argument(context, 1, cls=str)
        if normalization_form is None:
            raise self.error('XPTY0004', "2nd argument can't be an empty sequence")
        else:
            normalization_form = normalization_form.strip().upper()
    else:
        normalization_form = 'NFC'

    if normalization_form == 'FULLY-NORMALIZED':
        msg = "%r normalization form not supported" % normalization_form
        raise self.error('FOCH0003', msg)
    if not arg:
        return ''
    elif not normalization_form:
        return arg

    try:
        return unicodedata.normalize(normalization_form, arg)
    except ValueError:
        msg = "unsupported normalization form %r" % normalization_form
        raise self.error('FOCH0003', msg) from None

@method(function('upper-case', nargs=1, sequence_types=('xs:string?', 'xs:string')))
def evaluate_upper_case_function(self, context=None):
    return self.get_argument(context, default='', cls=str).upper()

@method(function('lower-case', nargs=1, sequence_types=('xs:string?', 'xs:string')))
def evaluate_lower_case_function(self, context=None):
    return self.get_argument(context, default='', cls=str).lower()

@method(function('encode-for-uri', nargs=1, sequence_types=('xs:string?', 'xs:string')))
def evaluate_encode_for_uri_function(self, context=None):
    uri_part = self.get_argument(context, cls=str)
    return '' if uri_part is None else urllib_quote(uri_part, safe='~')

@method(function('iri-to-uri', nargs=1, sequence_types=('xs:string?', 'xs:string')))
def evaluate_iri_to_uri_function(self, context=None):
    iri = self.get_argument(context, cls=str, promote=AnyURI)
    return '' if iri is None else urllib_quote(iri, safe='-_.!~*\'()#;/?:@&=+$,[]%')

@method(function('escape-html-uri', nargs=1, sequence_types=('xs:string?', 'xs:string')))
def evaluate_escape_html_uri_function(self, context=None):
    uri = self.get_argument(context, cls=str)
    if uri is None:
        return ''
    return urllib_quote(uri, safe=''.join(chr(cp) for cp in range(32, 127)))

@method(function('starts-with', nargs=(2, 3),
                 sequence_types=('xs:string?', 'xs:string?', 'xs:string', 'xs:boolean')))
def evaluate_starts_with_function(self, context=None):
    arg1 = self.get_argument(context, default='', cls=str)
    arg2 = self.get_argument(context, index=1, default='', cls=str)

    if len(self) < 3:
        return arg1.startswith(arg2)
    else:
        with self.use_locale(collation=self.get_argument(context, 2)):
            return arg1.startswith(arg2)

@method(function('ends-with', nargs=(2, 3),
                 sequence_types=('xs:string?', 'xs:string?', 'xs:string', 'xs:boolean')))
def evaluate_ends_with_function(self, context=None):
    arg1 = self.get_argument(context, default='', cls=str)
    arg2 = self.get_argument(context, index=1, default='', cls=str)

    if len(self) < 3:
        return arg1.endswith(arg2)
    else:
        with self.use_locale(collation=self.get_argument(context, 2)):
            return arg1.endswith(arg2)

@method(function('substring-before', nargs=(2, 3),
                 sequence_types=('xs:string?', 'xs:string?', 'xs:string', 'xs:string')))
@method(function('substring-after', nargs=(2, 3),
                 sequence_types=('xs:string?', 'xs:string?', 'xs:string', 'xs:string')))
def evaluate_substring_functions(self, context=None):
    arg1 = self.get_argument(context, default='', cls=str)
    arg2 = self.get_argument(context, index=1, default='', cls=str)

    if len(self) < 3:
        index = arg1.find(arg2)
    else:
        with self.use_locale(collation=self.get_argument(context, 2)):
            index = arg1.find(arg2)

    if index < 0:
        return ''
    if self.symbol == 'substring-before':
        return arg1[:index]
    else:
        return arg1[index + len(arg2):]

###
# Functions on durations, dates and times
@method(function('years-from-duration', nargs=1,
                 sequence_types=('xs:duration?', 'xs:integer?')))
def evaluate_years_from_duration_function(self, context=None):
    item = self.get_argument(context, cls=Duration)
    if item is None:
        return None
    elif item.months >= 0:
        return item.months // 12
    else:
        return -(abs(item.months) // 12)

@method(function('months-from-duration', nargs=1,
                 sequence_types=('xs:duration?', 'xs:integer?')))
def evaluate_months_from_duration_function(self, context=None):
    item = self.get_argument(context, cls=Duration)
    if item is None:
        return None
    elif item.months >= 0:
        return item.months % 12
    else:
        return -(abs(item.months) % 12)

@method(function('days-from-duration', nargs=1,
                 sequence_types=('xs:duration?', 'xs:integer?')))
def evaluate_days_from_duration_function(self, context=None):
    item = self.get_argument(context, cls=Duration)
    if item is None:
        return None
    elif item.seconds >= 0:
        return int(item.seconds // 86400)
    else:
        return - int(abs(item.seconds) // 86400)

@method(function('hours-from-duration', nargs=1,
                 sequence_types=('xs:duration?', 'xs:integer?')))
def evaluate_hours_from_duration_function(self, context=None):
    item = self.get_argument(context, cls=Duration)
    if item is None:
        return None
    elif item.seconds >= 0:
        return int(item.seconds // 3600 % 24)
    else:
        return - int(abs(item.seconds) // 3600 % 24)

@method(function('minutes-from-duration', nargs=1,
                 sequence_types=('xs:duration?', 'xs:integer?')))
def evaluate_minutes_from_duration_function(self, context=None):
    item = self.get_argument(context, cls=Duration)
    if item is None:
        return None
    elif item.seconds >= 0:
        return int(item.seconds // 60 % 60)
    else:
        return - int(abs(item.seconds) // 60 % 60)

@method(function('seconds-from-duration', nargs=1,
                 sequence_types=('xs:duration?', 'xs:decimal?')))
def evaluate_seconds_from_duration_function(self, context=None):
    item = self.get_argument(context, cls=Duration)
    if item is None:
        return None
    elif item.seconds >= 0:
        return item.seconds % 60
    else:
        return -(abs(item.seconds) % 60)

@method(function('year-from-dateTime', nargs=1, sequence_types=('xs:dateTime?', 'xs:integer?')))
@method(function('month-from-dateTime', nargs=1, sequence_types=('xs:dateTime?', 'xs:integer?')))
@method(function('day-from-dateTime', nargs=1, sequence_types=('xs:dateTime?', 'xs:integer?')))
@method(function('hours-from-dateTime', nargs=1, sequence_types=('xs:dateTime?', 'xs:integer?')))
@method(function('minutes-from-dateTime', nargs=1, sequence_types=('xs:dateTime?', 'xs:integer?')))
@method(function('seconds-from-dateTime', nargs=1, sequence_types=('xs:dateTime?', 'xs:decimal?')))
def evaluate_from_datetime_functions(self, context=None):
    cls = DateTime if self.parser.xsd_version == '1.1' else DateTime10
    item = self.get_argument(context, cls=cls)
    if item is None:
        return
    elif self.symbol.startswith('year'):
        return item.year
    elif self.symbol.startswith('month'):
        return item.month
    elif self.symbol.startswith('day'):
        return item.day
    elif self.symbol.startswith('hour'):
        return item.hour
    elif self.symbol.startswith('minute'):
        return item.minute
    elif item.microsecond:
        return Decimal('{}.{}'.format(item.second, item.microsecond))
    else:
        return item.second

@method(function('timezone-from-dateTime', nargs=1,
                 sequence_types=('xs:dateTime?', 'xs:dayTimeDuration?')))
def evaluate_timezone_from_datetime_function(self, context=None):
    cls = DateTime if self.parser.xsd_version == '1.1' else DateTime10
    item = self.get_argument(context, cls=cls)
    if item is None or item.tzinfo is None:
        return
    return DayTimeDuration(seconds=item.tzinfo.offset.total_seconds())

@method(function('year-from-date', nargs=1, sequence_types=('xs:date?', 'xs:integer?')))
@method(function('month-from-date', nargs=1, sequence_types=('xs:date?', 'xs:integer?')))
@method(function('day-from-date', nargs=1, sequence_types=('xs:date?', 'xs:integer?')))
@method(function('timezone-from-date', nargs=1,
                 sequence_types=('xs:date?', 'xs:dayTimeDuration?')))
def evaluate_from_date_functions(self, context=None):
    cls = Date if self.parser.xsd_version == '1.1' else Date10
    item = self.get_argument(context, cls=cls)
    if item is None:
        return
    elif self.symbol.startswith('year'):
        return item.year
    elif self.symbol.startswith('month'):
        return item.month
    elif self.symbol.startswith('day'):
        return item.day
    elif item.tzinfo is None:
        return
    return DayTimeDuration(seconds=item.tzinfo.offset.total_seconds())

@method(function('hours-from-time', nargs=1, sequence_types=('xs:time?', 'xs:integer?')))
def evaluate_hours_from_time_function(self, context=None):
    item = self.get_argument(context, cls=Time)
    return None if item is None else item.hour

@method(function('minutes-from-time', nargs=1, sequence_types=('xs:time?', 'xs:integer?')))
def evaluate_minutes_from_time_function(self, context=None):
    item = self.get_argument(context, cls=Time)
    return None if item is None else item.minute

@method(function('seconds-from-time', nargs=1, sequence_types=('xs:time?', 'xs:decimal?')))
def evaluate_seconds_from_time_function(self, context=None):
    item = self.get_argument(context, cls=Time)
    return None if item is None else item.second + item.microsecond / Decimal('1000000.0')

@method(function('timezone-from-time', nargs=1,
                 sequence_types=('xs:time?', 'xs:dayTimeDuration?')))
def evaluate_timezone_from_time_function(self, context=None):
    item = self.get_argument(context, cls=Time)
    if item is None or item.tzinfo is None:
        return
    return DayTimeDuration(seconds=item.tzinfo.offset.total_seconds())

###
# Timezone adjustment functions
@method(function('adjust-dateTime-to-timezone', nargs=(1, 2),
                 sequence_types=('xs:dateTime?', 'xs:dayTimeDuration?', 'xs:dateTime?')))
def evaluate_adjust_datetime_to_timezone_function(self, context=None):
    cls = DateTime if self.parser.xsd_version == '1.1' else DateTime10
    return self.adjust_datetime(context, cls)

@method(function('adjust-date-to-timezone', nargs=(1, 2),
                 sequence_types=('xs:date?', 'xs:dayTimeDuration?', 'xs:date?')))
def evaluate_adjust_date_to_timezone_function(self, context=None):
    cls = Date if self.parser.xsd_version == '1.1' else Date10
    return self.adjust_datetime(context, cls)

@method(function('adjust-time-to-timezone', nargs=(1, 2),
                 sequence_types=('xs:time?', 'xs:dayTimeDuration?', 'xs:time?')))
def evaluate_adjust_time_to_timezone_function(self, context=None):
    return self.adjust_datetime(context, Time)

###
# Static context functions
@method(function('default-collation', nargs=0, sequence_types=('xs:string',)))
def evaluate_default_collation_function(self, context=None):
    return self.parser.default_collation

@method(function('static-base-uri', nargs=0, sequence_types=('xs:anyURI?',)))
def evaluate_static_base_uri_function(self, context=None):
    if self.parser.base_uri is None:
        return None
    return AnyURI(self.parser.base_uri)

###
# Dynamic context functions
@method(function('current-dateTime', nargs=0, sequence_types=('xs:dateTime',)))
def evaluate_current_datetime_function(self, context=None):
    dt = datetime.datetime.now() if context is None else context.current_dt
    if self.parser.xsd_version == '1.1':
        return DateTime(dt.year, dt.month, dt.day, dt.hour, dt.minute,
                        dt.second, dt.microsecond, dt.tzinfo)
    return DateTime10(dt.year, dt.month, dt.day, dt.hour, dt.minute,
                      dt.second, dt.microsecond, dt.tzinfo)

@method(function('current-date', nargs=0, sequence_types=('xs:date',)))
def evaluate_current_date_function(self, context=None):
    dt = datetime.datetime.now() if context is None else context.current_dt
    if self.parser.xsd_version == '1.1':
        return Date(dt.year, dt.month, dt.day, tzinfo=dt.tzinfo)
    return Date10(dt.year, dt.month, dt.day, tzinfo=dt.tzinfo)

@method(function('current-time', nargs=0, sequence_types=('xs:time',)))
def evaluate_current_time_function(self, context=None):
    dt = datetime.datetime.now() if context is None else context.current_dt
    return Time(dt.hour, dt.minute, dt.second, dt.microsecond, dt.tzinfo)

@method(function('implicit-timezone', nargs=0, sequence_types=('xs:dayTimeDuration',)))
def evaluate_implicit_timezone_function(self, context=None):
    if context is not None and context.timezone is not None:
        return DayTimeDuration.fromtimedelta(context.timezone.offset)
    else:
        return DayTimeDuration.fromtimedelta(datetime.timedelta(seconds=time.timezone))

###
# The root function (Ref: https://www.w3.org/TR/2010/REC-xpath-functions-20101214/#func-root)
@method(function('root', nargs=(0, 1), sequence_types=('node()?', 'node()?')))
def evaluate_root_function(self, context=None):
    if context is None:
        raise self.missing_context()
    elif isinstance(context, XPathSchemaContext):
        return None
    elif not self:
        if context.item is None:
            return context.root
        elif not isinstance(context.item, XPathNode):
            raise self.error('XPTY0004')
        return context.get_root(context.item)
    else:
        item = self.get_argument(context)
        if item is None:
            return None
        elif not isinstance(item, XPathNode):
            raise self.error('XPTY0004')
        return context.get_root(item)

@method(function('lang', nargs=(1, 2),
                 sequence_types=('xs:string?', 'node()', 'xs:boolean')))
def evaluate_lang_function(self, context=None):
    if len(self) > 1:
        item = self.get_argument(context, index=1, default_to_context=True)
    elif context is None:
        raise self.missing_context()
    else:
        item = context.item

    if not isinstance(item, ElementNode):
        raise self.error('XPTY0004')

    try:
        lang = item.elem.attrib[XML_LANG].strip()
    except KeyError:
        if len(self) > 1:
            return False

        for elem in context.iter_ancestors():
            try:
                if XML_LANG in elem.elem.attrib:
                    lang = elem.elem.attrib[XML_LANG]
                    break
            except AttributeError:
                pass  # is a document node
        else:
            return False

    test_lang = self.get_argument(context, cls=str)
    if test_lang is None:
        test_lang = ''

    test_lang = test_lang.strip().lower()
    lang = lang.strip().lower()
    return lang == test_lang or lang.startswith(test_lang) and lang[len(test_lang)] == '-'

###
# Functions that generate sequences
@method(function('element-with-id', nargs=(1, 2),
                 sequence_types=('xs:string*', 'node()', 'element()*')))
@method(function('id', nargs=(1, 2),
                 sequence_types=('xs:string*', 'node()', 'element()*')))
def select_id_function(self, context=None):
    idrefs = {x for item in self[0].select(copy(context))
              for x in self.string_value(item).split() if Id.is_valid(x)}

    if context is None:
        raise self.missing_context()

    if len(self) == 1:
        node = context.item
        if node is None:
            node = context.root
    else:
        node = self.get_argument(context, index=1)

    if not isinstance(node, XPathNode):
        raise self.error('XPTY0004')

    if isinstance(context, XPathSchemaContext):
        return None

    root = context.get_root(node)
    if root is None:
        return None

    # TODO: PSVI bindings with also xsi:type evaluation
    for element in filter(lambda x: isinstance(x, ElementNode), root.iter_descendants()):
        if element.elem.text in idrefs:
            if self.parser.schema is not None:
                xsd_element = self.parser.schema.find(element.path, self.parser.namespaces)
                if xsd_element is None or not xsd_element.type.is_key():
                    continue

            idrefs.remove(element.elem.text)
            if self.symbol == 'id':
                yield element
            else:
                parent = element.parent
                if parent is not None:
                    yield parent
            continue  # pragma: no cover

        for attr in element.attributes:
            if attr.value in idrefs:
                if attr.name == XML_ID:
                    idrefs.remove(attr.value)
                    yield element
                    break

                if self.parser.schema is None:
                    continue

                xsd_element = self.parser.schema.find(element.path, self.parser.namespaces)
                if xsd_element is None:
                    continue

                xsd_attribute = xsd_element.attrib.get(attr.name)
                if xsd_attribute is None or not xsd_attribute.type.is_key():
                    continue  # pragma: no cover

                idrefs.remove(attr.value)
                yield element
                break

@method(function('idref', nargs=(1, 2), sequence_types=('xs:string*', 'node()', 'node()*')))
def select_idref_function(self, context=None):
    # TODO: PSVI bindings with also xsi:type evaluation
    ids = [x for x in self[0].select(context=copy(context))]
    node = self.get_argument(context, index=1, default_to_context=True)

    if isinstance(context, XPathSchemaContext):
        return
    elif context is None or node is not context.item:
        pass
    elif context.item is None:
        node = context.root

    if not isinstance(node, XPathNode):
        raise self.error('XPTY0004')
    elif not isinstance(node, (ElementNode, DocumentNode)):
        return

    for element in filter(lambda x: isinstance(x, ElementNode), node.iter_descendants()):
        text = element.elem.text
        if text and is_idrefs(text) and any(v in text.split() for x in ids for v in x.split()):
            yield element
            continue

        for attr in element.attributes:  # pragma: no cover
            if attr.name != XML_ID and \
                    any(v in attr.value.split() for x in ids for v in x.split()):
                yield element
                break

@method(function('doc', nargs=1, sequence_types=('xs:string?', 'document-node()?')))
@method(function('doc-available', nargs=1, sequence_types=('xs:string?', 'xs:boolean')))
def evaluate_doc_functions(self, context=None):
    uri = self.get_argument(context)
    if uri is None:
        return None if self.symbol == 'doc' else False
    elif isinstance(uri, str):
        pass
    elif isinstance(uri, AnyURI):
        uri = str(uri)
    elif isinstance(uri, UntypedAtomic):
        raise self.error('FODC0002')
    else:
        raise self.error('XPTY0004')

    if context is None:
        raise self.missing_context()
    elif isinstance(context, XPathSchemaContext):
        return None

    uri = uri.strip()
    if uri.startswith(':'):
        raise self.error('FODC0005')

    try:
        uri = self.get_absolute_uri(uri)
    except ElementPathValueError as err:
        if self.symbol == 'doc':
            raise self.error('FODC0002', err.message) from None
        return False

    try:
        doc = context.documents[uri]
    except (KeyError, TypeError):
        if self.symbol == 'doc':
            if is_local_dir_url(uri):
                raise self.error('FODC0005', 'document URI is a directory')
            raise self.error('FODC0002')
        return False
    else:
        if doc is None:
            raise self.error('FODC0002')

    try:
        sequence_type = self.parser.document_types[uri]
    except (KeyError, TypeError):
        sequence_type = 'document-node()'

    if not self.parser.match_sequence_type(doc, sequence_type):
        msg = "Type does not match sequence type {!r}"
        raise self.wrong_sequence_type(msg.format(sequence_type))

    return doc if self.symbol == 'doc' else True

@method(function('collection', nargs=(0, 1), sequence_types=('xs:string?', 'node()*')))
def evaluate_collection_function(self, context=None):
    uri = self.get_argument(context)
    if context is None:
        raise self.missing_context()
    elif isinstance(context, XPathSchemaContext):
        return
    elif not self or uri is None:
        if context.default_collection is None:
            raise self.error('FODC0002', 'no default collection has been defined')

        collection = context.default_collection
        sequence_type = self.parser.default_collection_type
    else:
        uri = self.get_absolute_uri(uri)
        try:
            collection = context.collections[uri]
        except (KeyError, TypeError):
            if is_local_dir_url(uri):
                raise self.error('FODC0004', 'collection URI is a directory')
            raise self.error('FODC0002', '{!r} collection not found'.format(uri)) from None

        try:
            sequence_type = self.parser.collection_types[uri]
        except (KeyError, TypeError):
            return collection

    if not self.parser.match_sequence_type(collection, sequence_type):
        msg = "Type does not match sequence type {!r}"
        raise self.wrong_sequence_type(msg.format(sequence_type))

    return collection

###
# The error function
#
# https://www.w3.org/TR/2010/REC-xpath-functions-20101214/#func-error
# https://www.w3.org/TR/xpath-functions/#func-error
#
@method(function('error', nargs=(0, 3),
                 sequence_types=('xs:QName?', 'xs:string', 'item()*', 'none')))
def evaluate_error_function(self, context=None):
    if not self:
        raise self.error('FOER0000')
    elif len(self) == 1:
        error = self.get_argument(context, cls=QName)
        if error is None:
            raise self.error('XPTY0004', "an xs:QName expected")
        raise self.error(error or 'FOER0000')
    else:
        error = self.get_argument(context, cls=QName)
        description = self.get_argument(context, index=1, cls=str)
        raise self.error(error or 'FOER0000', description)

###
# The trace function
#
# https://www.w3.org/TR/2010/REC-xpath-functions-20101214/#func-trace
#
@method(function('trace', nargs=2, sequence_types=('item()*', 'xs:string', 'item()*')))
def select_trace_function(self, context=None):
    label = self.get_argument(context, index=1, cls=str)
    for value in self[0].select(context):
        '{} {}'.format(label, str(value).strip())  # TODO: trace dataset
        yield value

# XPath 2.0 definitions continue into module xpath2_constructors


text:
package actions

import (
	"reflect"
	"testing"

	"github.com/kr/pretty"
	apps "k8s.io/api/apps/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"

	"github.com/jetstack/navigator/internal/test/unit/framework"
	"github.com/jetstack/navigator/internal/test/util/generate"
	"github.com/jetstack/navigator/pkg/apis/navigator/v1alpha1"
	"github.com/jetstack/navigator/pkg/util/ptr"
)

const esImageRepo = "docker.elastic.co/elasticsearch/elasticsearch"

func TestUpdateVersion(t *testing.T) {
	type testT struct {
		kubeObjects         []runtime.Object
		navObjects          []runtime.Object
		cluster             *v1alpha1.ElasticsearchCluster
		nodePool            *v1alpha1.ElasticsearchClusterNodePool
		shouldUpdate        bool
		expectedStatefulSet *apps.StatefulSet
		err                 bool
	}
	redHealth := v1alpha1.ElasticsearchClusterHealthRed
	greenHealth := v1alpha1.ElasticsearchClusterHealthGreen
	tests := map[string]testT{
		"should update statefulset and set partition to update the highest ordinal pod": {
			kubeObjects: []runtime.Object{
				generate.StatefulSet(generate.StatefulSetConfig{
					Name:            "es-test-data",
					Replicas:        ptr.Int32(3),
					Version:         "6.1.1",
					Image:           esImageRepo + ":6.1.1",
					CurrentRevision: "a",
					CurrentReplicas: 3,
					ReadyReplicas:   3,
				}),
			},
			navObjects: []runtime.Object{
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-0", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-1", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-2", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
			},
			cluster: generate.Cluster(generate.ClusterConfig{
				Name:    "test",
				Version: "6.1.2",
				Health:  &greenHealth,
				NodePools: []v1alpha1.ElasticsearchClusterNodePool{
					nodePoolWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
				},
			}),
			nodePool:     nodePoolPtrWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
			shouldUpdate: true,
			expectedStatefulSet: generate.StatefulSet(generate.StatefulSetConfig{
				Name:            "es-test-data",
				Replicas:        ptr.Int32(3),
				Version:         "6.1.1",
				Image:           esImageRepo + ":6.1.2",
				CurrentRevision: "a",
				CurrentReplicas: 3,
				Partition:       ptr.Int32(2),
				ReadyReplicas:   3,
			}),
			err: false,
		},
		"should update statefulset and set partition to update the second highest ordinal pod": {
			kubeObjects: []runtime.Object{
				generate.StatefulSet(generate.StatefulSetConfig{
					Name:            "es-test-data",
					Replicas:        ptr.Int32(3),
					Version:         "6.1.1",
					Image:           esImageRepo + ":6.1.2",
					CurrentRevision: "a",
					CurrentReplicas: 2,
					UpdatedReplicas: 1,
					Partition:       ptr.Int32(2),
					ReadyReplicas:   3,
				}),
			},
			navObjects: []runtime.Object{
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-0", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-1", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-2", Cluster: "test", NodePool: "data", Version: "6.1.2"}),
			},
			cluster: generate.Cluster(generate.ClusterConfig{
				Name:    "test",
				Version: "6.1.2",
				Health:  &greenHealth,
				NodePools: []v1alpha1.ElasticsearchClusterNodePool{
					nodePoolWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
				},
			}),
			nodePool:     nodePoolPtrWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
			shouldUpdate: true,
			expectedStatefulSet: generate.StatefulSet(generate.StatefulSetConfig{
				Name:            "es-test-data",
				Replicas:        ptr.Int32(3),
				Version:         "6.1.1",
				Image:           esImageRepo + ":6.1.2",
				CurrentRevision: "a",
				CurrentReplicas: 2,
				UpdatedReplicas: 1,
				Partition:       ptr.Int32(1),
				ReadyReplicas:   3,
			}),
			err: false,
		},
		"should not update a red cluster": {
			kubeObjects: []runtime.Object{
				generate.StatefulSet(generate.StatefulSetConfig{
					Name:            "es-test-data",
					Replicas:        ptr.Int32(3),
					Version:         "6.1.1",
					Image:           esImageRepo + ":6.1.1",
					CurrentRevision: "a",
					ReadyReplicas:   3,
				}),
			},
			navObjects: []runtime.Object{
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-0", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-1", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-2", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
			},
			cluster: generate.Cluster(generate.ClusterConfig{
				Name:    "test",
				Version: "6.1.2",
				NodePools: []v1alpha1.ElasticsearchClusterNodePool{
					nodePoolWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
				},
				Health: &redHealth,
			}),
			nodePool:     nodePoolPtrWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
			shouldUpdate: false,
			err:          false,
		},
		"should not update a node pool with an unready replica": {
			kubeObjects: []runtime.Object{
				generate.StatefulSet(generate.StatefulSetConfig{
					Name:            "es-test-data",
					Replicas:        ptr.Int32(3),
					Version:         "6.1.1",
					Image:           esImageRepo + ":6.1.1",
					CurrentRevision: "a",
					ReadyReplicas:   2,
				}),
			},
			navObjects: []runtime.Object{
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-0", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-1", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-2", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
			},
			cluster: generate.Cluster(generate.ClusterConfig{
				Name:    "test",
				Version: "6.1.2",
				NodePools: []v1alpha1.ElasticsearchClusterNodePool{
					nodePoolWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
				},
				Health: &greenHealth,
			}),
			nodePool:     nodePoolPtrWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
			shouldUpdate: false,
			err:          false,
		},
		"should set the updated version annotation on the statefulset when update is completed": {
			kubeObjects: []runtime.Object{
				generate.StatefulSet(generate.StatefulSetConfig{
					Name:            "es-test-data",
					Replicas:        ptr.Int32(3),
					Version:         "6.1.1",
					Image:           esImageRepo + ":6.1.2",
					CurrentRevision: "b",
					UpdateRevision:  "b",
					CurrentReplicas: 3,
					Partition:       ptr.Int32(2),
					ReadyReplicas:   3,
				}),
			},
			navObjects: []runtime.Object{
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-0", Cluster: "test", NodePool: "data", Version: "6.1.2"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-1", Cluster: "test", NodePool: "data", Version: "6.1.2"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-2", Cluster: "test", NodePool: "data", Version: "6.1.2"}),
			},
			cluster: generate.Cluster(generate.ClusterConfig{
				Name:    "test",
				Version: "6.1.2",
				NodePools: []v1alpha1.ElasticsearchClusterNodePool{
					nodePoolWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
				},
				Health: &greenHealth,
			}),
			nodePool:     nodePoolPtrWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
			shouldUpdate: true,
			expectedStatefulSet: generate.StatefulSet(generate.StatefulSetConfig{
				Name:            "es-test-data",
				Replicas:        ptr.Int32(3),
				Version:         "6.1.2",
				Image:           esImageRepo + ":6.1.2",
				CurrentRevision: "b",
				UpdateRevision:  "b",
				CurrentReplicas: 3,
				Partition:       ptr.Int32(2),
				ReadyReplicas:   3,
			}),
			err: false,
		},
		"should not update the next pod if the one before it hasn't finished updating": {
			kubeObjects: []runtime.Object{
				generate.StatefulSet(generate.StatefulSetConfig{
					Name:            "es-test-data",
					Replicas:        ptr.Int32(3),
					Version:         "6.1.1",
					Image:           esImageRepo + ":6.1.2",
					CurrentRevision: "a",
					ReadyReplicas:   3,
					CurrentReplicas: 1,
					UpdatedReplicas: 2,
				}),
			},
			navObjects: []runtime.Object{
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-0", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-1", Cluster: "test", NodePool: "data", Version: "6.1.1"}),
				generate.Pilot(generate.PilotConfig{Name: "es-test-data-2", Cluster: "test", NodePool: "data", Version: "6.1.2"}),
			},
			cluster: generate.Cluster(generate.ClusterConfig{
				Name:    "test",
				Version: "6.1.2",
				NodePools: []v1alpha1.ElasticsearchClusterNodePool{
					nodePoolWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
				},
				Health: &greenHealth,
			}),
			nodePool:     nodePoolPtrWithNameReplicasRoles("data", 3, v1alpha1.ElasticsearchRoleData),
			shouldUpdate: false,
			err:          false,
		},
	}
	for name, test := range tests {
		t.Run(name, func(t *testing.T) {
			fixture := &framework.StateFixture{
				T:                t,
				KubeObjects:      test.kubeObjects,
				NavigatorObjects: test.navObjects,
			}
			fixture.Start()
			defer fixture.Stop()

			state := fixture.State()
			updateVersion := &UpdateVersion{
				Cluster:  test.cluster,
				NodePool: test.nodePool,
			}
			err := updateVersion.Execute(state)
			if err != nil && !test.err {
				t.Errorf("Expected no error but got: %v", err)
			}
			if err == nil && test.err {
				t.Errorf("Expected error but got none")
			}
			actions := fixture.KubeClient().Actions()
			updateFound := false
			for _, action := range actions {
				if action.Matches("update", "statefulsets") {
					updateFound = true
				}
			}
			if !test.shouldUpdate && updateFound {
				t.Errorf("Update to statefulset performed when it should not have")
			}
			if test.shouldUpdate && !updateFound {
				t.Errorf("Update to statefulset not performed when it should have been")
			}
			if test.expectedStatefulSet != nil {
				actualStatefulSet, err := fixture.KubeClient().AppsV1beta1().StatefulSets(test.expectedStatefulSet.Namespace).Get(test.expectedStatefulSet.Name, metav1.GetOptions{})
				if err != nil {
					t.Errorf("Got error when retrieving statefulset: %v", err)
					t.Fail()
				}
				if !reflect.DeepEqual(test.expectedStatefulSet, actualStatefulSet) {
					t.Errorf("Expected did not equal actual: %s", pretty.Diff(actualStatefulSet, test.expectedStatefulSet))
				}
			}
		})
	}
}


text:
from pyface.tasks.traits_editor import TraitsEditor
from traits.api import Bool

# ============= standard library imports ========================
# ============= local library imports  ==========================
from pychron.loggable import Loggable

def grouped_name(names, delimiter='-'):
    s = names[0]
    e = names[-1]
    if s != e:
        if all([delimiter in x for x in names]):
            prev = None
            for x in names:
                nx = x.split(delimiter)
                h, t = delimiter.join(nx[:-1]), nx[-1]

                if prev and prev != h:
                    break
                prev = h
            else:
                s = names[0]
                e = names[-1].split(delimiter)[-1]

        s = '{} - {}'.format(s, e)

    return s

try:
    class BaseTraitsEditor(TraitsEditor, Loggable):
        dirty = Bool(False)
        _destroyed = False

        def __init__(self, *args, **kw):
            super().__init__(*args, **kw)
            self.init_logger()

        def prepare_destroy(self):
            pass

        def destroy(self):
            self._destroyed = True
            self.prepare_destroy()
            super().destroy()

except TypeError:
    # documentation auto doc hack
    class BaseTraitsEditor:
        pass
# ============= EOF =============================================


text:
package org.apache.hadoop.hbase.mob;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseInterfaceAudience;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.master.cleaner.BaseHFileCleanerDelegate;
import org.apache.hadoop.hbase.util.CommonFSUtils;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * {@link BaseHFileCleanerDelegate} that prevents cleaning HFiles from a mob region keeps a map of
 * table name strings to mob region name strings over the life of a JVM instance. if there's churn
 * of unique table names we'll eat memory until Master restart.
 */
@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
public class ManualMobMaintHFileCleaner extends BaseHFileCleanerDelegate {

  private static final Logger LOG = LoggerFactory.getLogger(ManualMobMaintHFileCleaner.class);

  // We need to avoid making HRegionInfo objects for every table we check.
  private static final ConcurrentMap<TableName, String> MOB_REGIONS = new ConcurrentHashMap<>();

  @Override
  public boolean isFileDeletable(FileStatus fStat) {
    try {
      // if its a directory, then it can be deleted
      if (fStat.isDirectory()) {
        return true;
      }

      Path file = fStat.getPath();

      // we need the table and region to determine if this is from a mob region
      // we don't need to worry about hfilelink back references, because the hfilelink cleaner will
      // retain them.
      Path family = file.getParent();
      Path region = family.getParent();
      Path table = region.getParent();

      TableName tableName = CommonFSUtils.getTableName(table);

      String mobRegion = MOB_REGIONS.get(tableName);
      if (mobRegion == null) {
        String tmp = MobUtils.getMobRegionInfo(tableName).getEncodedName();
        if (tmp == null) {
          LOG.error("couldn't determine mob region for table {} keeping files just in case.",
            tableName);
          return false;
        }
        mobRegion = MOB_REGIONS.putIfAbsent(tableName, tmp);
        // a return of null means that tmp is now in the map for future lookups.
        if (mobRegion == null) {
          mobRegion = tmp;
        }
        LOG.debug("Had to calculate name of mob region for table {} and it is {}", tableName,
          mobRegion);
      }

      boolean ret = !mobRegion.equals(region.getName());
      if (LOG.isDebugEnabled() && !ret) {
        LOG.debug("Keeping file '{}' because it is from mob dir", fStat.getPath());
      }
      return ret;
    } catch (RuntimeException e) {
      LOG.error("Failed to determine mob status of '{}', keeping it just in case.", fStat.getPath(),
        e);
      return false;
    }
  }

}


text:
package net.katagaitai.phpscan.ast.nodes;

import lombok.Getter;
import lombok.Setter;
import net.katagaitai.phpscan.ast.Visitor;

/**
 * Holds a goto statement.
 *
 *

 * e.g.
 *
 *

 * goto START;
 */
public class GotoStatement extends Statement {
	@Setter
	@Getter
	private Identifier label;

	public GotoStatement(int start, int end, AST ast, Identifier label) {
		super(start, end, ast);

		if (label == null) {
			throw new IllegalArgumentException();
		}
		setLabel(label);
	}

	public GotoStatement(AST ast) {
		super(ast);
	}

	public void toString(StringBuffer buffer, String tab) {
		buffer.append(tab).append("<GotoStatement"); //$NON-NLS-1$
		appendInterval(buffer);
		buffer.append(">\n"); //$NON-NLS-1$
		label.toString(buffer, TAB + tab);
		buffer.append("\n").append(tab).append("</GotoStatement>"); //$NON-NLS-1$ //$NON-NLS-2$
	}

	@Override
	public Object accept(Visitor visitor) {
		return visitor.visit(this);
	}

}


text:
from .scan import ScanStore
from .recorder import ScanRecorder


text:
var WriteJson = require("../lib");

// Write the same file synchronously
WriteJson(__dirname + "/test.json", { "hello": "mars" });

// 4 space indent and new line at the end
WriteJson(__dirname + "/test.json", { "hello": "mars" }, {
    new_line: true
  , space: 4
});

// Write test.json asynchronously
WriteJson(__dirname + "/test.json", { "hello": "world" }, function (err, data) {
    console.log(err || "written");
});