text:
using System;

namespace Itinero.Algorithms.Restrictions
{
    /// <summary>
    /// Represents restriction collection.
    /// </summary>
    public sealed class RestrictionCollection
    {
        private readonly Func<RestrictionCollection, uint, bool> _update;

        /// <summary>
        /// Creates a new restriction collection.
        /// </summary>
        public RestrictionCollection(Func<RestrictionCollection, uint, bool> update)
        {
            _update = update;
            _restrictions = new Restriction[64];
            _count = 0;
            _vertex = Constants.NO_VERTEX;
        }

        private Restriction[] _restrictions;
        private int _count;
        private uint _vertex;

        /// <summary>
        /// Updates this restriction collection.
        /// </summary>
        /// <return>Returns true if there are restrictions.</return>
        public bool Update(uint vertex)
        {
            if (_vertex == vertex)
            {
                return true;
            }

            _vertex = vertex;

            return _update(this, _vertex);
        }

        /// <summary>
        /// Returns the restriction at the given index.
        /// </summary>
        public Restriction this[int i]
        {
            get
            {
                return _restrictions[i];
            }
        }

        /// <summary>
        /// Gets the # of restrictions.
        /// </summary>
        public int Count
        {
            get
            {
                return _count;
            }
        }

        /// <summary>
        /// Returns the vertex.
        /// </summary>
        public uint Vertex
        {
            get
            {
                return _vertex;
            }
        }

        /// <summary>
        /// Adds a new restriction.
        /// </summary>
        public void Add(Restriction restriction)
        {
            if (_count >= _restrictions.Length)
            {
                Array.Resize(ref _restrictions, _restrictions.Length * 2);
            }
            _restrictions[_count] = restriction;
            _count++;
        }

        /// <summary>
        /// Clears all restrictions.
        /// </summary>
        public void Clear()
        {
            _count = 0;
        }
    }
}


text:
#include "SkBitmap.h"
#include "SkChunkAlloc.h"
#include "SkGPipe.h"
#include "SkPicture.h"
#include "SkTDArray.h"

class SkCanvas;
class SkMatrix;

class PipeController : public SkGPipeController {
public:
    PipeController(SkCanvas* target, SkPicture::InstallPixelRefProc proc = NULL);
    virtual ~PipeController();
    void* requestBlock(size_t minRequest, size_t* actual) override;
    void notifyWritten(size_t bytes) override;
protected:
    const void* getData() { return (const char*) fBlock + fBytesWritten; }
    SkGPipeReader fReader;
private:
    void* fBlock;
    size_t fBlockSize;
    size_t fBytesWritten;
    SkGPipeReader::Status fStatus;
};

////////////////////////////////////////////////////////////////////////////////

class TiledPipeController : public PipeController {
public:
    TiledPipeController(const SkBitmap&, SkPicture::InstallPixelRefProc proc = NULL,
                        const SkMatrix* initialMatrix = NULL);
    virtual ~TiledPipeController() {};
    void notifyWritten(size_t bytes) override;
    int numberOfReaders() const override { return NumberOfTiles; }
private:
    enum {
        NumberOfTiles = 10
    };
    SkGPipeReader fReaders[NumberOfTiles - 1];
    SkBitmap fBitmaps[NumberOfTiles];
    typedef PipeController INHERITED;
};

////////////////////////////////////////////////////////////////////////////////

/**
 * Borrowed (and modified) from SkDeferredCanvas.cpp::DeferredPipeController.
 * Allows playing back from multiple threads, but does not do the threading itself.
 */
class ThreadSafePipeController : public SkGPipeController {
public:
    ThreadSafePipeController(int numberOfReaders);
    void* requestBlock(size_t minRequest, size_t* actual) override;
    void notifyWritten(size_t bytes) override;
    int numberOfReaders() const override { return fNumberOfReaders; }

    /**
     * Play the stored drawing commands to the specified canvas. If SkGPipeWriter::startRecording
     * used the flag SkGPipeWriter::kSimultaneousReaders_Flag, this can be called from different
     * threads simultaneously.
     */
    void draw(SkCanvas*);
private:
    enum {
        kMinBlockSize = 4096
    };
    struct PipeBlock {
        PipeBlock(void* block, size_t bytes) { fBlock = block, fBytes = bytes; }
        // Stream of draw commands written by the SkGPipeWriter. Allocated by fAllocator, which will
        // handle freeing it.
        void* fBlock;
        // Number of bytes that were written to fBlock.
        size_t fBytes;
    };
    void* fBlock;
    size_t fBytesWritten;
    SkChunkAlloc fAllocator;
    SkTDArray<PipeBlock> fBlockList;
    int fNumberOfReaders;
};

redpajama_set_name: RedPajamaGithub


text:
#pragma once
#include <aws/ecs/ECS_EXPORTS.h>
#include <aws/core/utils/memory/stl/AWSVector.h>
#include <aws/core/utils/memory/stl/AWSString.h>
#include <utility>

namespace Aws
{
template<typename RESULT_TYPE>
class AmazonWebServiceResult;

namespace Utils
{
namespace Json
{
  class JsonValue;
} // namespace Json
} // namespace Utils
namespace ECS
{
namespace Model
{
  class AWS_ECS_API ListTasksResult
  {
  public:
    ListTasksResult();
    ListTasksResult(const Aws::AmazonWebServiceResult<Aws::Utils::Json::JsonValue>& result);
    ListTasksResult& operator=(const Aws::AmazonWebServiceResult<Aws::Utils::Json::JsonValue>& result);

    /**
     *
The list of task ARN entries for the ListTasks request.

     */
    inline const Aws::Vector<Aws::String>& GetTaskArns() const{ return m_taskArns; }

    /**
     *
The list of task ARN entries for the ListTasks request.

     */
    inline void SetTaskArns(const Aws::Vector<Aws::String>& value) { m_taskArns = value; }

    /**
     *
The list of task ARN entries for the ListTasks request.

     */
    inline void SetTaskArns(Aws::Vector<Aws::String>&& value) { m_taskArns = std::move(value); }

    /**
     *
The list of task ARN entries for the ListTasks request.

     */
    inline ListTasksResult& WithTaskArns(const Aws::Vector<Aws::String>& value) { SetTaskArns(value); return *this;}

    /**
     *
The list of task ARN entries for the ListTasks request.

     */
    inline ListTasksResult& WithTaskArns(Aws::Vector<Aws::String>&& value) { SetTaskArns(std::move(value)); return *this;}

    /**
     *
The list of task ARN entries for the ListTasks request.

     */
    inline ListTasksResult& AddTaskArns(const Aws::String& value) { m_taskArns.push_back(value); return *this; }

    /**
     *
The list of task ARN entries for the ListTasks request.

     */
    inline ListTasksResult& AddTaskArns(Aws::String&& value) { m_taskArns.push_back(std::move(value)); return *this; }

    /**
     *
The list of task ARN entries for the ListTasks request.

     */
    inline ListTasksResult& AddTaskArns(const char* value) { m_taskArns.push_back(value); return *this; }

    /**
     *
The nextToken value to include in a future
     * ListTasks request. When the results of a ListTasks
     * request exceed maxResults, this value can be used to retrieve the
     * next page of results. This value is null when there are no more
     * results to return.

     */
    inline const Aws::String& GetNextToken() const{ return m_nextToken; }

    /**
     *
The nextToken value to include in a future
     * ListTasks request. When the results of a ListTasks
     * request exceed maxResults, this value can be used to retrieve the
     * next page of results. This value is null when there are no more
     * results to return.

     */
    inline void SetNextToken(const Aws::String& value) { m_nextToken = value; }

    /**
     *
The nextToken value to include in a future
     * ListTasks request. When the results of a ListTasks
     * request exceed maxResults, this value can be used to retrieve the
     * next page of results. This value is null when there are no more
     * results to return.

     */
    inline void SetNextToken(Aws::String&& value) { m_nextToken = std::move(value); }

    /**
     *
The nextToken value to include in a future
     * ListTasks request. When the results of a ListTasks
     * request exceed maxResults, this value can be used to retrieve the
     * next page of results. This value is null when there are no more
     * results to return.

     */
    inline void SetNextToken(const char* value) { m_nextToken.assign(value); }

    /**
     *
The nextToken value to include in a future
     * ListTasks request. When the results of a ListTasks
     * request exceed maxResults, this value can be used to retrieve the
     * next page of results. This value is null when there are no more
     * results to return.

     */
    inline ListTasksResult& WithNextToken(const Aws::String& value) { SetNextToken(value); return *this;}

    /**
     *
The nextToken value to include in a future
     * ListTasks request. When the results of a ListTasks
     * request exceed maxResults, this value can be used to retrieve the
     * next page of results. This value is null when there are no more
     * results to return.

     */
    inline ListTasksResult& WithNextToken(Aws::String&& value) { SetNextToken(std::move(value)); return *this;}

    /**
     *
The nextToken value to include in a future
     * ListTasks request. When the results of a ListTasks
     * request exceed maxResults, this value can be used to retrieve the
     * next page of results. This value is null when there are no more
     * results to return.

     */
    inline ListTasksResult& WithNextToken(const char* value) { SetNextToken(value); return *this;}

  private:

    Aws::Vector<Aws::String> m_taskArns;

    Aws::String m_nextToken;
  };

} // namespace Model
} // namespace ECS
} // namespace Aws


text:
import { observable, autorun, action } from 'mobx'
import { trimTime } from '../utils'
import axios from 'axios'
import { API } from '../constants'

class RegisterStore {
	@observable error
	@observable pending = false
	@observable registerFailed = false

	@action async register (user) {
		if (this.pending)
			return

		user.birthday = trimTime(user.birthday.toJSON())

		this.pending = true
		this.registerFailed = false

		try {
			await axios.post(`${API}/users`, user)
		} catch (e) {
			const response = e.response

			if (!response) {
				this.error = "Something went wrong."
				console.error(e.message)
			} else
				this.error = response.data.message

			this.registerFailed = true
		} finally {
			this.pending = false
		}
	}

	@action clear () {
		this.error = ""
		this.pending = false
		this.registerFailed = false
	}
}

export default new RegisterStore


text:
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')

describe "AuthlogicFacebook" do
  it "fails" do
    fail "hey buddy, you should probably rename this file and start specing for real"
  end
end


text:
\section{Bundles}
\label{stack:bundles}
\TODO
\input{bundle/Core}
\input{bundle/InformationBus}


text:
var aircraft = {
	list:{
		"chopper":{
			name:"chopper",
			cost:900,
			pixelWidth:40,
			pixelHeight:40,
			pixelOffsetX:20,
			pixelOffsetY:20,
			weaponType:"heatseeker",
			radius:18,
			sight:4,
			canAttack:true,
			canAttackLand:true,
			canAttackAir:true,
    	    hitPoints:50,
			speed:25,
			turnSpeed:4,
			pixelShadowHeight:40,
			spriteImages:[
				{name:"fly",count:4,directions:8}
			],
		},
		"wraith":{
			name:"wraith",
			cost:600,
			pixelWidth:30,
			pixelHeight:30,
			canAttack:true,
			canAttackLand:false,
			canAttackAir:true,
			weaponType:"fireball",
			pixelOffsetX:15,
			pixelOffsetY:15,
			radius:15,
			sight:8,
			speed:40,
			turnSpeed:4,
    	    hitPoints:50,
			pixelShadowHeight:40,
			spriteImages:[
				{name:"fly",count:1,directions:8}
			],
		}
	},
	defaults:{
		type:"aircraft",
		animationIndex:0,
		direction:0,
		directions:8,
		action:"fly",
        selected:false,
		selectable:true,
		orders:{type:"float"},
		moveTo:function(destination){
			// Find out where we need to turn to get to destination
			var newDirection = findAngle(destination,this,this.directions);
			// Calculate difference between new direction and current direction
			var difference = angleDiff(this.direction,newDirection,this.directions);
			// Calculate amount that aircraft can turn per animation cycle
			var turnAmount = this.turnSpeed*game.turnSpeedAdjustmentFactor;
			if (Math.abs(difference)>turnAmount){
				this.direction = wrapDirection(this.direction+turnAmount*Math.abs(difference)/difference,this.directions);
			} else {
				var movement = this.speed*game.speedAdjustmentFactor;
				var angleRadians = -(Math.round(this.direction)/this.directions)*2*Math.PI ;
				this.lastMovementX = - (movement*Math.sin(angleRadians));
				this.lastMovementY = - (movement*Math.cos(angleRadians));
				this.x = (this.x +this.lastMovementX);
				this.y = (this.y +this.lastMovementY);
			}
		},
		processOrders:function(){
			this.lastMovementX = 0;
		          this.lastMovementY = 0;
			switch (this.orders.type){
				case "move":
					// Move towards destination and stop when close by
					if ((Math.pow(this.orders.to.x-this.x,2) + Math.pow(this.orders.to.y-this.y,2))<Math.pow(this.radius/game.gridSize,2)) {
						this.orders = {type:"float"};
		               } else {
		                  	this.moveTo(this.orders.to);
		               }
					break;
			}
		},
		animate:function(){
			if (this.life>this.hitPoints*0.4){
				this.lifeCode = "healthy";
			} else if (this.life <= 0){
				this.lifeCode = "dead";
				game.remove(this);
				return;
			} else {
				this.lifeCode = "damaged";
			}
			switch (this.action){
				case "fly":

					var direction = wrapDirection(Math.round(this.direction),this.directions);
				 	this.imageList = this.spriteArray["fly-"+ direction];
					this.imageOffset = this.imageList.offset + this.animationIndex;
					this.animationIndex++;
					if (this.animationIndex>=this.imageList.count){
						this.animationIndex = 0;
					}
				break;
			}
		},
		drawLifeBar:function(){
			var x = this.drawingX;
		    var y = this.drawingY - 2*game.lifeBarHeight;
			game.foregroundContext.fillStyle = (this.lifeCode == "healthy")?game.healthBarHealthyFillColor:game.healthBarDamagedFillColor;
			game.foregroundContext.fillRect(x,y,this.pixelWidth*this.life/this.hitPoints,game.lifeBarHeight)
			game.foregroundContext.strokeStyle = game.healthBarBorderColor;
			game.foregroundContext.lineWidth = 1;
			game.foregroundContext.strokeRect(x,y,this.pixelWidth,game.lifeBarHeight)
		},
		drawSelection:function(){
			var x = this.drawingX + this.pixelOffsetX;
		    var y = this.drawingY + this.pixelOffsetY;
			game.foregroundContext.strokeStyle = game.selectionBorderColor;
			game.foregroundContext.lineWidth = 2;
			game.foregroundContext.beginPath();
			game.foregroundContext.arc(x,y,this.radius,0,Math.PI*2,false);
			game.foregroundContext.stroke();
			game.foregroundContext.fillStyle = game.selectionFillColor;
			game.foregroundContext.fill();

			game.foregroundContext.beginPath();
			game.foregroundContext.arc(x,y+this.pixelShadowHeight,4,0,Math.PI*2,false);
			game.foregroundContext.stroke();

			game.foregroundContext.beginPath();
			game.foregroundContext.moveTo(x,y);
			game.foregroundContext.lineTo(x,y+this.pixelShadowHeight);
			game.foregroundContext.stroke();
		},
		draw:function(){
			var x = (this.x*game.gridSize)-game.offsetX-this.pixelOffsetX + this.lastMovementX*game.drawingInterpolationFactor*game.gridSize;
		    var y = (this.y*game.gridSize)-game.offsetY-this.pixelOffsetY-this.pixelShadowHeight + this.lastMovementY*game.drawingInterpolationFactor*game.gridSize;
			this.drawingX = x;
			this.drawingY = y;
			if (this.selected){
				this.drawSelection();
				this.drawLifeBar();
			}
			var colorIndex = (this.team == "blue")?0:1;
			var colorOffset = colorIndex*this.pixelHeight;
			var shadowOffset = this.pixelHeight*2; // The aircraft shadow is on the second row of the sprite sheet

			game.foregroundContext.drawImage(this.spriteSheet, this.imageOffset*this.pixelWidth,colorOffset,this.pixelWidth,this.pixelHeight,x,y,this.pixelWidth,this.pixelHeight);
			game.foregroundContext.drawImage(this.spriteSheet, this.imageOffset*this.pixelWidth,shadowOffset,this.pixelWidth,this.pixelHeight,x,y+this.pixelShadowHeight,this.pixelWidth,this.pixelHeight);
		}
	},
	load:loadItem,
	add:addItem,
}


text:
var namespaceblackjack_1_1cmake_1_1process_1_1cache =
[
    [ "CacheEntry", "namespaceblackjack_1_1cmake_1_1process_1_1cache_1_1_cache_entry.html", null ],
    [ "CacheFile", "namespaceblackjack_1_1cmake_1_1process_1_1cache_1_1_cache_file.html", null ]
];


module_name_cp: Log

no_log_file_available_yet: No log file available yet. When any logged messages will be available, they will appear here.

cannot_read_log: Error while reading log file from disk.

message: Message

code: Code

ip: IP

url: URL

timestamp: Timestamp

stack: Stack

view_stack: View stacktrace

close: Close

total_log_records: Total log records


text:
package eu.atos.sla.parser.json;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import eu.atos.sla.parser.ParserException;
import eu.atos.sla.parser.data.wsag.Agreement;
import eu.atos.sla.parser.data.wsag.Template;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/tools-test-context.xml")
public class TestJSONParser {
	private static Logger logger = LoggerFactory.getLogger(TestJSONParser.class);

	@Test
	public void testAgreementJSONParser() {
		File file = new File("../samples/agreement06.json");
		boolean error = false;
		try {
			String serializedData = getStringFromInputStream(new FileInputStream(file));
			AgreementParser agreementParser = new AgreementParser();
			Agreement wsagObject = agreementParser.getWsagObject(serializedData);
			logger.debug("Readed agreement:"+wsagObject);
			String wsagData = agreementParser.getWsagAsSerializedData(serializedData);
			agreementParser.getSerializedData(wsagData);
		} catch (FileNotFoundException e1) {
			logger.error("", e1);
		} catch (ParserException e) {
			error = true;
			logger.error("", e);
		}

		assert(!error);
	}

	@Test
	public void testTemplateJSONParser() {
		//TODO missing template JSON file
		File file = new File("../samples/template01.json");
		boolean error = false;
		try {
			String serializedData = getStringFromInputStream(new FileInputStream(file));
			TemplateParser templateParser = new TemplateParser();
			Template wsagObject = templateParser.getWsagObject(serializedData);
			logger.info("Readed template:"+wsagObject);
			String wsagData = templateParser.getWsagAsSerializedData(serializedData);
			logger.info(templateParser.getSerializedData(wsagData));
		} catch (FileNotFoundException e1) {
			logger.error("", e1);
		} catch (ParserException e) {
			error = true;
			logger.error("", e);
		}

		assert(!error);
	}

	private String getStringFromInputStream(InputStream is) {

		BufferedReader br = null;
		StringBuilder sb = new StringBuilder();

		String line;
		try {

			br = new BufferedReader(new InputStreamReader(is));
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		return sb.toString();

	}
}


text:
require 'patina/collections'

module Patina::Collections
  # Shared validation methods for collections to ensure a common interface when
  # error conditions are encountered.
  module Validation
    private

    def error_types
      Bronze::Collections::Collection::Errors
    end # method error_types

    def validate_attributes attributes
      return build_errors.add(error_types.data_missing) if attributes.nil?

      unless attributes.is_a?(Hash)
        return build_errors.add(
          error_types.data_invalid,
          :attributes => attributes
        ) # end errors
      end # unless

      []
    end # method validate_attributes

    def validate_id id
      if id.nil?
        return build_errors.add(error_types.primary_key_missing, :key => :id)
      end # if

      []
    end # method validate_id
  end # module
end # module


text:
package pd

import (
	"crypto/tls"
	"crypto/x509"
	"io/ioutil"
	"net/url"
	"strings"
	"sync"
	"time"

	"github.com/opentracing/opentracing-go"
	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/kvproto/pkg/pdpb"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

// Client is a PD (Placement Driver) client.
// It should not be used after calling Close().
type Client interface {
	// GetClusterID gets the cluster ID from PD.
	GetClusterID(ctx context.Context) uint64
	// GetTS gets a timestamp from PD.
	GetTS(ctx context.Context) (int64, int64, error)
	// GetTSAsync gets a timestamp from PD, without block the caller.
	GetTSAsync(ctx context.Context) TSFuture
	// GetRegion gets a region and its leader Peer from PD by key.
	// The region may expire after split. Caller is responsible for caching and
	// taking care of region change.
	// Also it may return nil if PD finds no Region for the key temporarily,
	// client should retry later.
	GetRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error)
	// GetPrevRegion gets the previous region and its leader Peer of the region where the key is located.
	GetPrevRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error)
	// GetRegionByID gets a region and its leader Peer from PD by id.
	GetRegionByID(ctx context.Context, regionID uint64) (*metapb.Region, *metapb.Peer, error)
	// GetStore gets a store from PD by store id.
	// The store may expire later. Caller is responsible for caching and taking care
	// of store change.
	GetStore(ctx context.Context, storeID uint64) (*metapb.Store, error)
	// GetAllStores gets all stores from pd.
	// The store may expire later. Caller is responsible for caching and taking care
	// of store change.
	GetAllStores(ctx context.Context) ([]*metapb.Store, error)
	// Update GC safe point. TiKV will check it and do GC themselves if necessary.
	// If the given safePoint is less than the current one, it will not be updated.
	// Returns the new safePoint after updating.
	UpdateGCSafePoint(ctx context.Context, safePoint uint64) (uint64, error)
	// Close closes the client.
	Close()
}

type tsoRequest struct {
	start    time.Time
	ctx      context.Context
	done     chan error
	physical int64
	logical  int64
}

const (
	pdTimeout             = 3 * time.Second
	updateLeaderTimeout   = time.Second // Use a shorter timeout to recover faster from network isolation.
	maxMergeTSORequests   = 10000
	maxInitClusterRetries = 100
)

var (
	// errFailInitClusterID is returned when failed to load clusterID from all supplied PD addresses.
	errFailInitClusterID = errors.New("[pd] failed to get cluster id")
	// errClosing is returned when request is canceled when client is closing.
	errClosing = errors.New("[pd] closing")
	// errTSOLength is returned when the number of response timestamps is inconsistent with request.
	errTSOLength = errors.New("[pd] tso length in rpc response is incorrect")
)

type client struct {
	urls        []string
	clusterID   uint64
	tsoRequests chan *tsoRequest

	connMu struct {
		sync.RWMutex
		clientConns map[string]*grpc.ClientConn
		leader      string
	}

	tsDeadlineCh  chan deadline
	checkLeaderCh chan struct{}

	wg     sync.WaitGroup
	ctx    context.Context
	cancel context.CancelFunc

	security SecurityOption
}

// SecurityOption records options about tls
type SecurityOption struct {
	CAPath   string
	CertPath string
	KeyPath  string
}

// NewClient creates a PD client.
func NewClient(pdAddrs []string, security SecurityOption) (Client, error) {
	log.Infof("[pd] create pd client with endpoints %v", pdAddrs)
	ctx, cancel := context.WithCancel(context.Background())
	c := &client{
		urls:          addrsToUrls(pdAddrs),
		tsoRequests:   make(chan *tsoRequest, maxMergeTSORequests),
		tsDeadlineCh:  make(chan deadline, 1),
		checkLeaderCh: make(chan struct{}, 1),
		ctx:           ctx,
		cancel:        cancel,
		security:      security,
	}
	c.connMu.clientConns = make(map[string]*grpc.ClientConn)

	if err := c.initClusterID(); err != nil {
		return nil, err
	}
	if err := c.updateLeader(); err != nil {
		return nil, err
	}
	log.Infof("[pd] init cluster id %v", c.clusterID)

	c.wg.Add(3)
	go c.tsLoop()
	go c.tsCancelLoop()
	go c.leaderLoop()

	return c, nil
}

func (c *client) updateURLs(members []*pdpb.Member) {
	urls := make([]string, 0, len(members))
	for _, m := range members {
		urls = append(urls, m.GetClientUrls()...)
	}
	c.urls = urls
}

func (c *client) initClusterID() error {
	ctx, cancel := context.WithCancel(c.ctx)
	defer cancel()
	for i := 0; i < maxInitClusterRetries; i++ {
		for _, u := range c.urls {
			members, err := c.getMembers(ctx, u)
			if err != nil || members.GetHeader() == nil {
				log.Errorf("[pd] failed to get cluster id: %v", err)
				continue
			}
			c.clusterID = members.GetHeader().GetClusterId()
			return nil
		}

		time.Sleep(time.Second)
	}

	return errors.WithStack(errFailInitClusterID)
}

func (c *client) updateLeader() error {
	for _, u := range c.urls {
		ctx, cancel := context.WithTimeout(c.ctx, updateLeaderTimeout)
		members, err := c.getMembers(ctx, u)
		cancel()
		if err != nil || members.GetLeader() == nil || len(members.GetLeader().GetClientUrls()) == 0 {
			select {
			case <-c.ctx.Done():
				return errors.WithStack(err)
			default:
				continue
			}
		}
		c.updateURLs(members.GetMembers())
		return c.switchLeader(members.GetLeader().GetClientUrls())
	}
	return errors.Errorf("failed to get leader from %v", c.urls)
}

func (c *client) getMembers(ctx context.Context, url string) (*pdpb.GetMembersResponse, error) {
	cc, err := c.getOrCreateGRPCConn(url)
	if err != nil {
		return nil, err
	}
	members, err := pdpb.NewPDClient(cc).GetMembers(ctx, &pdpb.GetMembersRequest{})
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return members, nil
}

func (c *client) switchLeader(addrs []string) error {
	// FIXME: How to safely compare leader urls? For now, only allows one client url.
	addr := addrs[0]

	c.connMu.RLock()
	oldLeader := c.connMu.leader
	c.connMu.RUnlock()

	if addr == oldLeader {
		return nil
	}

	log.Infof("[pd] leader switches to: %v, previous: %v", addr, oldLeader)
	if _, err := c.getOrCreateGRPCConn(addr); err != nil {
		return err
	}

	c.connMu.Lock()
	defer c.connMu.Unlock()
	c.connMu.leader = addr
	return nil
}

func (c *client) getOrCreateGRPCConn(addr string) (*grpc.ClientConn, error) {
	c.connMu.RLock()
	conn, ok := c.connMu.clientConns[addr]
	c.connMu.RUnlock()
	if ok {
		return conn, nil
	}

	opt := grpc.WithInsecure()
	if len(c.security.CAPath) != 0 {

		certificates := []tls.Certificate{}
		if len(c.security.CertPath) != 0 && len(c.security.KeyPath) != 0 {
			// Load the client certificates from disk
			certificate, err := tls.LoadX509KeyPair(c.security.CertPath, c.security.KeyPath)
			if err != nil {
				return nil, errors.Errorf("could not load client key pair: %s", err)
			}
			certificates = append(certificates, certificate)
		}

		// Create a certificate pool from the certificate authority
		certPool := x509.NewCertPool()
		ca, err := ioutil.ReadFile(c.security.CAPath)
		if err != nil {
			return nil, errors.Errorf("could not read ca certificate: %s", err)
		}

		// Append the certificates from the CA
		if !certPool.AppendCertsFromPEM(ca) {
			return nil, errors.New("failed to append ca certs")
		}

		creds := credentials.NewTLS(&tls.Config{
			Certificates: certificates,
			RootCAs:      certPool,
		})

		opt = grpc.WithTransportCredentials(creds)
	}
	u, err := url.Parse(addr)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	cc, err := grpc.Dial(u.Host, opt)
	if err != nil {
		return nil, errors.WithStack(err)
	}
	c.connMu.Lock()
	defer c.connMu.Unlock()
	if old, ok := c.connMu.clientConns[addr]; ok {
		cc.Close()
		return old, nil
	}

	c.connMu.clientConns[addr] = cc
	return cc, nil
}

func (c *client) leaderLoop() {
	defer c.wg.Done()

	ctx, cancel := context.WithCancel(c.ctx)
	defer cancel()

	for {
		select {
		case <-c.checkLeaderCh:
		case <-time.After(time.Minute):
		case <-ctx.Done():
			return
		}

		if err := c.updateLeader(); err != nil {
			log.Errorf("[pd] failed updateLeader: %v", err)
		}
	}
}

type deadline struct {
	timer  <-chan time.Time
	done   chan struct{}
	cancel context.CancelFunc
}

func (c *client) tsCancelLoop() {
	defer c.wg.Done()

	ctx, cancel := context.WithCancel(c.ctx)
	defer cancel()

	for {
		select {
		case d := <-c.tsDeadlineCh:
			select {
			case <-d.timer:
				log.Error("tso request is canceled due to timeout")
				d.cancel()
			case <-d.done:
			case <-ctx.Done():
				return
			}
		case <-ctx.Done():
			return
		}
	}
}

func (c *client) tsLoop() {
	defer c.wg.Done()

	loopCtx, loopCancel := context.WithCancel(c.ctx)
	defer loopCancel()

	var requests []*tsoRequest
	var opts []opentracing.StartSpanOption
	var stream pdpb.PD_TsoClient
	var cancel context.CancelFunc

	for {
		var err error

		if stream == nil {
			var ctx context.Context
			ctx, cancel = context.WithCancel(loopCtx)
			stream, err = c.leaderClient().Tso(ctx)
			if err != nil {
				select {
				case <-loopCtx.Done():
					return
				default:
				}
				log.Errorf("[pd] create tso stream error: %v", err)
				c.ScheduleCheckLeader()
				cancel()
				c.revokeTSORequest(errors.WithStack(err))
				select {
				case <-time.After(time.Second):
				case <-loopCtx.Done():
					return
				}
				continue
			}
		}

		select {
		case first := <-c.tsoRequests:
			requests = append(requests, first)
			pending := len(c.tsoRequests)
			for i := 0; i < pending; i++ {
				requests = append(requests, <-c.tsoRequests)
			}
			done := make(chan struct{})
			dl := deadline{
				timer:  time.After(pdTimeout),
				done:   done,
				cancel: cancel,
			}
			select {
			case c.tsDeadlineCh <- dl:
			case <-loopCtx.Done():
				return
			}
			opts = extractSpanReference(requests, opts[:0])
			err = c.processTSORequests(stream, requests, opts)
			close(done)
			requests = requests[:0]
		case <-loopCtx.Done():
			cancel()
			return
		}

		if err != nil {
			select {
			case <-loopCtx.Done():
				return
			default:
			}
			log.Errorf("[pd] getTS error: %v", err)
			c.ScheduleCheckLeader()
			cancel()
			stream, cancel = nil, nil
		}
	}
}

func extractSpanReference(requests []*tsoRequest, opts []opentracing.StartSpanOption) []opentracing.StartSpanOption {
	for _, req := range requests {
		if span := opentracing.SpanFromContext(req.ctx); span != nil {
			opts = append(opts, opentracing.ChildOf(span.Context()))
		}
	}
	return opts
}

func (c *client) processTSORequests(stream pdpb.PD_TsoClient, requests []*tsoRequest, opts []opentracing.StartSpanOption) error {
	if len(opts) > 0 {
		span := opentracing.StartSpan("pdclient.processTSORequests", opts...)
		defer span.Finish()
	}

	start := time.Now()
	req := &pdpb.TsoRequest{
		Header: c.requestHeader(),
		Count:  uint32(len(requests)),
	}

	if err := stream.Send(req); err != nil {
		err = errors.WithStack(err)
		c.finishTSORequest(requests, 0, 0, err)
		return err
	}
	resp, err := stream.Recv()
	if err != nil {
		err = errors.WithStack(err)
		c.finishTSORequest(requests, 0, 0, err)
		return err
	}
	requestDuration.WithLabelValues("tso").Observe(time.Since(start).Seconds())
	if resp.GetCount() != uint32(len(requests)) {
		err = errors.WithStack(errTSOLength)
		c.finishTSORequest(requests, 0, 0, err)
		return err
	}

	physical, logical := resp.GetTimestamp().GetPhysical(), resp.GetTimestamp().GetLogical()
	// Server returns the highest ts.
	logical -= int64(resp.GetCount() - 1)
	c.finishTSORequest(requests, physical, logical, nil)
	return nil
}

func (c *client) finishTSORequest(requests []*tsoRequest, physical, firstLogical int64, err error) {
	for i := 0; i < len(requests); i++ {
		if span := opentracing.SpanFromContext(requests[i].ctx); span != nil {
			span.Finish()
		}
		requests[i].physical, requests[i].logical = physical, firstLogical+int64(i)
		requests[i].done <- err
	}
}

func (c *client) revokeTSORequest(err error) {
	n := len(c.tsoRequests)
	for i := 0; i < n; i++ {
		req := <-c.tsoRequests
		req.done <- err
	}
}

func (c *client) Close() {
	c.cancel()
	c.wg.Wait()

	c.revokeTSORequest(errors.WithStack(errClosing))

	c.connMu.Lock()
	defer c.connMu.Unlock()
	for _, cc := range c.connMu.clientConns {
		if err := cc.Close(); err != nil {
			log.Errorf("[pd] failed close grpc clientConn: %v", err)
		}
	}
}

func (c *client) leaderClient() pdpb.PDClient {
	c.connMu.RLock()
	defer c.connMu.RUnlock()

	return pdpb.NewPDClient(c.connMu.clientConns[c.connMu.leader])
}

func (c *client) ScheduleCheckLeader() {
	select {
	case c.checkLeaderCh <- struct{}{}:
	default:
	}
}

func (c *client) GetClusterID(context.Context) uint64 {
	return c.clusterID
}

// For testing use.
func (c *client) GetLeaderAddr() string {
	c.connMu.RLock()
	defer c.connMu.RUnlock()
	return c.connMu.leader
}

// For testing use. It should only be called when the client is closed.
func (c *client) GetURLs() []string {
	return c.urls
}

var tsoReqPool = sync.Pool{
	New: func() interface{} {
		return &tsoRequest{
			done: make(chan error, 1),
		}
	},
}

func (c *client) GetTSAsync(ctx context.Context) TSFuture {
	if span := opentracing.SpanFromContext(ctx); span != nil {
		span = opentracing.StartSpan("GetTSAsync", opentracing.ChildOf(span.Context()))
		ctx = opentracing.ContextWithSpan(ctx, span)
	}
	req := tsoReqPool.Get().(*tsoRequest)
	req.start = time.Now()
	req.ctx = ctx
	req.physical = 0
	req.logical = 0
	c.tsoRequests <- req

	return req
}

// TSFuture is a future which promises to return a TSO.
type TSFuture interface {
	// Wait gets the physical and logical time, it would block caller if data is not available yet.
	Wait() (int64, int64, error)
}

func (req *tsoRequest) Wait() (physical int64, logical int64, err error) {
	// If tso command duration is observed very high, the reason could be it
	// takes too long for Wait() be called.
	cmdDuration.WithLabelValues("tso_async_wait").Observe(time.Since(req.start).Seconds())
	select {
	case err = <-req.done:
		err = errors.WithStack(err)
		defer tsoReqPool.Put(req)
		if err != nil {
			cmdFailedDuration.WithLabelValues("tso").Observe(time.Since(req.start).Seconds())
			return 0, 0, err
		}
		physical, logical = req.physical, req.logical
		cmdDuration.WithLabelValues("tso").Observe(time.Since(req.start).Seconds())
		return
	case <-req.ctx.Done():
		return 0, 0, errors.WithStack(req.ctx.Err())
	}
}

func (c *client) GetTS(ctx context.Context) (physical int64, logical int64, err error) {
	resp := c.GetTSAsync(ctx)
	return resp.Wait()
}

func (c *client) GetRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error) {
	if span := opentracing.SpanFromContext(ctx); span != nil {
		span = opentracing.StartSpan("pdclient.GetRegion", opentracing.ChildOf(span.Context()))
		defer span.Finish()
	}
	start := time.Now()
	defer func() { cmdDuration.WithLabelValues("get_region").Observe(time.Since(start).Seconds()) }()

	ctx, cancel := context.WithTimeout(ctx, pdTimeout)
	resp, err := c.leaderClient().GetRegion(ctx, &pdpb.GetRegionRequest{
		Header:    c.requestHeader(),
		RegionKey: key,
	})
	cancel()

	if err != nil {
		cmdFailedDuration.WithLabelValues("get_region").Observe(time.Since(start).Seconds())
		c.ScheduleCheckLeader()
		return nil, nil, errors.WithStack(err)
	}
	return resp.GetRegion(), resp.GetLeader(), nil
}

func (c *client) GetPrevRegion(ctx context.Context, key []byte) (*metapb.Region, *metapb.Peer, error) {
	if span := opentracing.SpanFromContext(ctx); span != nil {
		span = opentracing.StartSpan("pdclient.GetPrevRegion", opentracing.ChildOf(span.Context()))
		defer span.Finish()
	}
	start := time.Now()
	defer func() { cmdDuration.WithLabelValues("get_prev_region").Observe(time.Since(start).Seconds()) }()

	ctx, cancel := context.WithTimeout(ctx, pdTimeout)
	resp, err := c.leaderClient().GetPrevRegion(ctx, &pdpb.GetRegionRequest{
		Header:    c.requestHeader(),
		RegionKey: key,
	})
	cancel()

	if err != nil {
		cmdFailedDuration.WithLabelValues("get_prev_region").Observe(time.Since(start).Seconds())
		c.ScheduleCheckLeader()
		return nil, nil, errors.WithStack(err)
	}
	return resp.GetRegion(), resp.GetLeader(), nil
}

func (c *client) GetRegionByID(ctx context.Context, regionID uint64) (*metapb.Region, *metapb.Peer, error) {
	if span := opentracing.SpanFromContext(ctx); span != nil {
		span = opentracing.StartSpan("pdclient.GetRegionByID", opentracing.ChildOf(span.Context()))
		defer span.Finish()
	}
	start := time.Now()
	defer func() { cmdDuration.WithLabelValues("get_region_byid").Observe(time.Since(start).Seconds()) }()

	ctx, cancel := context.WithTimeout(ctx, pdTimeout)
	resp, err := c.leaderClient().GetRegionByID(ctx, &pdpb.GetRegionByIDRequest{
		Header:   c.requestHeader(),
		RegionId: regionID,
	})
	cancel()

	if err != nil {
		cmdFailedDuration.WithLabelValues("get_region_byid").Observe(time.Since(start).Seconds())
		c.ScheduleCheckLeader()
		return nil, nil, errors.WithStack(err)
	}
	return resp.GetRegion(), resp.GetLeader(), nil
}

func (c *client) GetStore(ctx context.Context, storeID uint64) (*metapb.Store, error) {
	if span := opentracing.SpanFromContext(ctx); span != nil {
		span = opentracing.StartSpan("pdclient.GetStore", opentracing.ChildOf(span.Context()))
		defer span.Finish()
	}
	start := time.Now()
	defer func() { cmdDuration.WithLabelValues("get_store").Observe(time.Since(start).Seconds()) }()

	ctx, cancel := context.WithTimeout(ctx, pdTimeout)
	resp, err := c.leaderClient().GetStore(ctx, &pdpb.GetStoreRequest{
		Header:  c.requestHeader(),
		StoreId: storeID,
	})
	cancel()

	if err != nil {
		cmdFailedDuration.WithLabelValues("get_store").Observe(time.Since(start).Seconds())
		c.ScheduleCheckLeader()
		return nil, errors.WithStack(err)
	}
	store := resp.GetStore()
	if store == nil {
		return nil, errors.New("[pd] store field in rpc response not set")
	}
	if store.GetState() == metapb.StoreState_Tombstone {
		return nil, nil
	}
	return store, nil
}

func (c *client) GetAllStores(ctx context.Context) ([]*metapb.Store, error) {
	if span := opentracing.SpanFromContext(ctx); span != nil {
		span = opentracing.StartSpan("pdclient.GetAllStores", opentracing.ChildOf(span.Context()))
		defer span.Finish()
	}
	start := time.Now()
	defer func() { cmdDuration.WithLabelValues("get_all_stores").Observe(time.Since(start).Seconds()) }()

	ctx, cancel := context.WithTimeout(ctx, pdTimeout)
	resp, err := c.leaderClient().GetAllStores(ctx, &pdpb.GetAllStoresRequest{
		Header: c.requestHeader(),
	})
	cancel()

	if err != nil {
		cmdFailedDuration.WithLabelValues("get_all_stores").Observe(time.Since(start).Seconds())
		c.ScheduleCheckLeader()
		return nil, errors.WithStack(err)
	}
	stores := resp.GetStores()
	return stores, nil
}

func (c *client) UpdateGCSafePoint(ctx context.Context, safePoint uint64) (uint64, error) {
	if span := opentracing.SpanFromContext(ctx); span != nil {
		span = opentracing.StartSpan("pdclient.UpdateGCSafePoint", opentracing.ChildOf(span.Context()))
		defer span.Finish()
	}
	start := time.Now()
	defer func() { cmdDuration.WithLabelValues("update_gc_safe_point").Observe(time.Since(start).Seconds()) }()

	ctx, cancel := context.WithTimeout(ctx, pdTimeout)
	resp, err := c.leaderClient().UpdateGCSafePoint(ctx, &pdpb.UpdateGCSafePointRequest{
		Header:    c.requestHeader(),
		SafePoint: safePoint,
	})
	cancel()

	if err != nil {
		cmdFailedDuration.WithLabelValues("update_gc_safe_point").Observe(time.Since(start).Seconds())
		c.ScheduleCheckLeader()
		return 0, errors.WithStack(err)
	}
	return resp.GetNewSafePoint(), nil
}

func (c *client) requestHeader() *pdpb.RequestHeader {
	return &pdpb.RequestHeader{
		ClusterId: c.clusterID,
	}
}

func addrsToUrls(addrs []string) []string {
	// Add default schema "http://" to addrs.
	urls := make([]string, 0, len(addrs))
	for _, addr := range addrs {
		if strings.Contains(addr, "://") {
			urls = append(urls, addr)
		} else {
			urls = append(urls, "http://"+addr)
		}
	}
	return urls
}


text:
// Copyright (c) 2012, Joshua Burke
// All rights reserved.
//
// See LICENSE for more information.

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

using Frost.Collections;
using Frost.Effects;
using Frost.Management.Contracts;
using Frost.Formatting;
using Frost.Surfacing;

namespace Frost.Management
{
	namespace Contracts
	{
		[ContractClassFor(typeof(IResourceHelpers))]
		internal abstract class IResourceHelpersContract
			: IResourceHelpers
		{
			public void Copy(Rectangle fromRegion, Canvas fromTarget, Canvas toTarget)
			{
				Contract.Requires(fromTarget != null);
				Contract.Requires(toTarget != null);
			}

			public void Copy(byte[] fromRgbaData, Canvas toTarget)
			{
				Contract.Requires(fromRgbaData != null);
				Contract.Requires(toTarget != null);
			}

			public void Copy(Canvas fromTarget, Canvas toTarget)
			{
				Contract.Requires(fromTarget != null);
				Contract.Requires(toTarget != null);
			}

			public IEnumerable<ISurface2D> GetPages(SurfaceUsage usage)
			{
				Contract.Ensures(Contract.Result<IEnumerable<ISurface2D>>() != null);

				throw new NotSupportedException();
			}

			public abstract event Action<IEnumerable<Canvas>> CanvasInvalidated;

			public GlyphOutline GetGlyphOutline(
				IndexedRange glyphRange,
				bool isVertical,
				bool isRightToLeft,
				FontId fontId,
				List<TextShaper.Glyph> glyphs)
			{
				Contract.Requires(!(isVertical && isRightToLeft));
				Contract.Requires(glyphs != null);

				throw new NotSupportedException();
			}

			public FontMetrics GetFontMetrics(FontId fontId)
			{
				throw new NotSupportedException();
			}

			public abstract void RegisterEffect<T>() where T : Effect, new();

			public abstract Effect<T> FindEffect<T>()
				where T : struct, IEffectSettings, IEquatable<T>;

			public abstract void UnregisterEffect<T>();

			public Size PageSize
			{
				set
				{
					Contract.Requires(Check.IsPositive(value.Width));
					Contract.Requires(Check.IsPositive(value.Height));
				}
			}

			public Canvas.ResolvedContext ResolveCanvas(Canvas target)
			{
				Contract.Requires(target != null);

				throw new NotSupportedException();
			}

			public void ForgetCanvas(Canvas target)
			{
				Contract.Requires(target != null);
			}
		}
	}

	[ContractClass(typeof(IResourceHelpersContract))]
	public interface IResourceHelpers
	{
		Size PageSize { set; }

		void Copy(Rectangle fromRegion, Canvas fromTarget, Canvas toTarget);

		void Copy(byte[] fromRgbaData, Canvas toTarget);

		void Copy(Canvas fromTarget, Canvas toTarget);

		IEnumerable<ISurface2D> GetPages(SurfaceUsage usage);

		Canvas.ResolvedContext ResolveCanvas(Canvas target);

		void ForgetCanvas(Canvas target);

		GlyphOutline GetGlyphOutline(
			IndexedRange glyphRange,
			bool isVertical,
			bool isRightToLeft,
			FontId fontId,
			List<TextShaper.Glyph> glyphs);

		FontMetrics GetFontMetrics(FontId fontId);

		void RegisterEffect<T>() where T : Effect, new();

		Effect<T> FindEffect<T>() where T : struct, IEffectSettings, IEquatable<T>;

		void UnregisterEffect<T>();
		event Action<IEnumerable<Canvas>> CanvasInvalidated;
	}
}


text:
package org.hellofdevelop.hc_dic.model;

import com.google.gson.Gson;

public class SkillProgress extends SkillBase {

	public int mLevel;

	public SkillProgress(SkillBase skillBase) {
		super(skillBase);
	}

	@Override
	public String toString() {
		Gson gson = new Gson();
		return gson.toJson(this);
	}

}


text:
namespace U_ICU_NAMESPACE {
class BreakIterator;
class Collator;
class DecimalFormat;
class SimpleDateFormat;
class TimeZone;
}

namespace v8 {
namespace internal {

template <typename T>
class Handle;

class DateFormat {
 public:
  // Create a formatter for the specificied locale and options. Returns the
  // resolved settings for the locale / options.
  static icu::SimpleDateFormat* InitializeDateTimeFormat(
      Isolate* isolate,
      Handle<String> locale,
      Handle<JSObject> options,
      Handle<JSObject> resolved);

  // Unpacks date format object from corresponding JavaScript object.
  static icu::SimpleDateFormat* UnpackDateFormat(Isolate* isolate,
                                                 Handle<JSObject> obj);

  // Release memory we allocated for the DateFormat once the JS object that
  // holds the pointer gets garbage collected.
  static void DeleteDateFormat(const v8::WeakCallbackInfo<void>& data);

  // Layout description.
  static const int kSimpleDateFormat = JSObject::kHeaderSize;
  static const int kSize = kSimpleDateFormat + kPointerSize;

 private:
  DateFormat();
};

class NumberFormat {
 public:
  // Create a formatter for the specificied locale and options. Returns the
  // resolved settings for the locale / options.
  static icu::DecimalFormat* InitializeNumberFormat(
      Isolate* isolate,
      Handle<String> locale,
      Handle<JSObject> options,
      Handle<JSObject> resolved);

  // Unpacks number format object from corresponding JavaScript object.
  static icu::DecimalFormat* UnpackNumberFormat(Isolate* isolate,
                                                Handle<JSObject> obj);

  // Release memory we allocated for the NumberFormat once the JS object that
  // holds the pointer gets garbage collected.
  static void DeleteNumberFormat(const v8::WeakCallbackInfo<void>& data);

  // Layout description.
  static const int kDecimalFormat = JSObject::kHeaderSize;
  static const int kSize = kDecimalFormat + kPointerSize;

 private:
  NumberFormat();
};

class Collator {
 public:
  // Create a collator for the specificied locale and options. Returns the
  // resolved settings for the locale / options.
  static icu::Collator* InitializeCollator(
      Isolate* isolate,
      Handle<String> locale,
      Handle<JSObject> options,
      Handle<JSObject> resolved);

  // Unpacks collator object from corresponding JavaScript object.
  static icu::Collator* UnpackCollator(Isolate* isolate, Handle<JSObject> obj);

  // Release memory we allocated for the Collator once the JS object that holds
  // the pointer gets garbage collected.
  static void DeleteCollator(const v8::WeakCallbackInfo<void>& data);

  // Layout description.
  static const int kCollator = JSObject::kHeaderSize;
  static const int kSize = kCollator + kPointerSize;

 private:
  Collator();
};

class V8BreakIterator {
 public:
  // Create a BreakIterator for the specificied locale and options. Returns the
  // resolved settings for the locale / options.
  static icu::BreakIterator* InitializeBreakIterator(
      Isolate* isolate,
      Handle<String> locale,
      Handle<JSObject> options,
      Handle<JSObject> resolved);

  // Unpacks break iterator object from corresponding JavaScript object.
  static icu::BreakIterator* UnpackBreakIterator(Isolate* isolate,
                                                 Handle<JSObject> obj);

  // Release memory we allocated for the BreakIterator once the JS object that
  // holds the pointer gets garbage collected.
  static void DeleteBreakIterator(const v8::WeakCallbackInfo<void>& data);

  // Layout description.
  static const int kBreakIterator = JSObject::kHeaderSize;
  static const int kUnicodeString = kBreakIterator + kPointerSize;
  static const int kSize = kUnicodeString + kPointerSize;

 private:
  V8BreakIterator();
};

const UChar* GetUCharBufferFromFlat(const String::FlatContent& flat,
                                    std::unique_ptr<uc16[]>* dest,
                                    int32_t length);
MUST_USE_RESULT Object* LocaleConvertCase(Handle<String> s, Isolate* isolate,
                                          bool is_to_upper, const char* lang);
MUST_USE_RESULT Object* ConvertToLower(Handle<String> s, Isolate* isolate);
MUST_USE_RESULT Object* ConvertToUpper(Handle<String> s, Isolate* isolate);
MUST_USE_RESULT Object* ConvertCase(Handle<String> s, bool is_upper,
                                    Isolate* isolate);

// ICUTimezoneCache calls out to ICU for TimezoneCache
// functionality in a straightforward way.
class ICUTimezoneCache : public base::TimezoneCache {
 public:
  ICUTimezoneCache();

  ~ICUTimezoneCache() override;

  const char* LocalTimezone(double time_ms) override;

  double DaylightSavingsOffset(double time_ms) override;

  double LocalTimeOffset() override;

  void Clear() override;

 private:
  icu::TimeZone* GetTimeZone();

  bool GetOffsets(double time_ms, int32_t* raw_offset, int32_t* dst_offset);

  icu::TimeZone* timezone_;

  static const int32_t kMaxTimezoneChars = 100;
  char timezone_name_[kMaxTimezoneChars];
  char dst_timezone_name_[kMaxTimezoneChars];
};

}  // namespace internal
}  // namespace v8

#endif  // V8_I18N_H_


text:
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/uprobes.h>
#include <linux/namei.h>

#include "trace_probe.h"

#define UPROBE_EVENT_SYSTEM	"uprobes"

/*
 * uprobe event core functions
 */
struct trace_uprobe;
struct uprobe_trace_consumer {
	struct uprobe_consumer		cons;
	struct trace_uprobe		*tu;
};

struct trace_uprobe {
	struct list_head		list;
	struct ftrace_event_class	class;
	struct ftrace_event_call	call;
	struct uprobe_trace_consumer	*consumer;
	struct inode			*inode;
	char				*filename;
	unsigned long			offset;
	unsigned long			nhit;
	unsigned int			flags;	/* For TP_FLAG_* */
	ssize_t				size;	/* trace entry size */
	unsigned int			nr_args;
	struct probe_arg		args[];
};

#define SIZEOF_TRACE_UPROBE(n)			\
	(offsetof(struct trace_uprobe, args) +	\
	(sizeof(struct probe_arg) * (n)))

static int register_uprobe_event(struct trace_uprobe *tu);
static void unregister_uprobe_event(struct trace_uprobe *tu);

static DEFINE_MUTEX(uprobe_lock);
static LIST_HEAD(uprobe_list);

static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs);

/*
 * Allocate new trace_uprobe and initialize it (including uprobes).
 */
static struct trace_uprobe *
alloc_trace_uprobe(const char *group, const char *event, int nargs)
{
	struct trace_uprobe *tu;

	if (!event || !is_good_name(event))
		return ERR_PTR(-EINVAL);

	if (!group || !is_good_name(group))
		return ERR_PTR(-EINVAL);

	tu = kzalloc(SIZEOF_TRACE_UPROBE(nargs), GFP_KERNEL);
	if (!tu)
		return ERR_PTR(-ENOMEM);

	tu->call.class = &tu->class;
	tu->call.name = kstrdup(event, GFP_KERNEL);
	if (!tu->call.name)
		goto error;

	tu->class.system = kstrdup(group, GFP_KERNEL);
	if (!tu->class.system)
		goto error;

	INIT_LIST_HEAD(&tu->list);
	return tu;

error:
	kfree(tu->call.name);
	kfree(tu);

	return ERR_PTR(-ENOMEM);
}

static void free_trace_uprobe(struct trace_uprobe *tu)
{
	int i;

	for (i = 0; i < tu->nr_args; i++)
		traceprobe_free_probe_arg(&tu->args[i]);

	iput(tu->inode);
	kfree(tu->call.class->system);
	kfree(tu->call.name);
	kfree(tu->filename);
	kfree(tu);
}

static struct trace_uprobe *find_probe_event(const char *event, const char *group)
{
	struct trace_uprobe *tu;

	list_for_each_entry(tu, &uprobe_list, list)
		if (strcmp(tu->call.name, event) == 0 &&
		    strcmp(tu->call.class->system, group) == 0)
			return tu;

	return NULL;
}

/* Unregister a trace_uprobe and probe_event: call with locking uprobe_lock */
static void unregister_trace_uprobe(struct trace_uprobe *tu)
{
	list_del(&tu->list);
	unregister_uprobe_event(tu);
	free_trace_uprobe(tu);
}

/* Register a trace_uprobe and probe_event */
static int register_trace_uprobe(struct trace_uprobe *tu)
{
	struct trace_uprobe *old_tp;
	int ret;

	mutex_lock(&uprobe_lock);

	/* register as an event */
	old_tp = find_probe_event(tu->call.name, tu->call.class->system);
	if (old_tp)
		/* delete old event */
		unregister_trace_uprobe(old_tp);

	ret = register_uprobe_event(tu);
	if (ret) {
		pr_warning("Failed to register probe event(%d)\n", ret);
		goto end;
	}

	list_add_tail(&tu->list, &uprobe_list);

end:
	mutex_unlock(&uprobe_lock);

	return ret;
}

/*
 * Argument syntax:
 *  - Add uprobe: p[:[GRP/]EVENT] PATH:SYMBOL[+offs] [FETCHARGS]
 *
 *  - Remove uprobe: -:[GRP/]EVENT
 */
static int create_trace_uprobe(int argc, char **argv)
{
	struct trace_uprobe *tu;
	struct inode *inode;
	char *arg, *event, *group, *filename;
	char buf[MAX_EVENT_NAME_LEN];
	struct path path;
	unsigned long offset;
	bool is_delete;
	int i, ret;

	inode = NULL;
	ret = 0;
	is_delete = false;
	event = NULL;
	group = NULL;

	/* argc must be >= 1 */
	if (argv[0][0] == '-')
		is_delete = true;
	else if (argv[0][0] != 'p') {
		pr_info("Probe definition must be started with 'p', 'r' or" " '-'.\n");
		return -EINVAL;
	}

	if (argv[0][1] == ':') {
		event = &argv[0][2];
		arg = strchr(event, '/');

		if (arg) {
			group = event;
			event = arg + 1;
			event[-1] = '\0';

			if (strlen(group) == 0) {
				pr_info("Group name is not specified\n");
				return -EINVAL;
			}
		}
		if (strlen(event) == 0) {
			pr_info("Event name is not specified\n");
			return -EINVAL;
		}
	}
	if (!group)
		group = UPROBE_EVENT_SYSTEM;

	if (is_delete) {
		if (!event) {
			pr_info("Delete command needs an event name.\n");
			return -EINVAL;
		}
		mutex_lock(&uprobe_lock);
		tu = find_probe_event(event, group);

		if (!tu) {
			mutex_unlock(&uprobe_lock);
			pr_info("Event %s/%s doesn't exist.\n", group, event);
			return -ENOENT;
		}
		/* delete an event */
		unregister_trace_uprobe(tu);
		mutex_unlock(&uprobe_lock);
		return 0;
	}

	if (argc < 2) {
		pr_info("Probe point is not specified.\n");
		return -EINVAL;
	}
	if (isdigit(argv[1][0])) {
		pr_info("probe point must be have a filename.\n");
		return -EINVAL;
	}
	arg = strchr(argv[1], ':');
	if (!arg)
		goto fail_address_parse;

	*arg++ = '\0';
	filename = argv[1];
	ret = kern_path(filename, LOOKUP_FOLLOW, &path);
	if (ret)
		goto fail_address_parse;

	ret = strict_strtoul(arg, 0, &offset);
	if (ret)
		goto fail_address_parse;

	inode = igrab(path.dentry->d_inode);

	argc -= 2;
	argv += 2;

	/* setup a probe */
	if (!event) {
		char *tail = strrchr(filename, '/');
		char *ptr;

		ptr = kstrdup((tail ? tail + 1 : filename), GFP_KERNEL);
		if (!ptr) {
			ret = -ENOMEM;
			goto fail_address_parse;
		}

		tail = ptr;
		ptr = strpbrk(tail, ".-_");
		if (ptr)
			*ptr = '\0';

		snprintf(buf, MAX_EVENT_NAME_LEN, "%c_%s_0x%lx", 'p', tail, offset);
		event = buf;
		kfree(tail);
	}

	tu = alloc_trace_uprobe(group, event, argc);
	if (IS_ERR(tu)) {
		pr_info("Failed to allocate trace_uprobe.(%d)\n", (int)PTR_ERR(tu));
		ret = PTR_ERR(tu);
		goto fail_address_parse;
	}
	tu->offset = offset;
	tu->inode = inode;
	tu->filename = kstrdup(filename, GFP_KERNEL);

	if (!tu->filename) {
		pr_info("Failed to allocate filename.\n");
		ret = -ENOMEM;
		goto error;
	}

	/* parse arguments */
	ret = 0;
	for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
		/* Increment count for freeing args in error case */
		tu->nr_args++;

		/* Parse argument name */
		arg = strchr(argv[i], '=');
		if (arg) {
			*arg++ = '\0';
			tu->args[i].name = kstrdup(argv[i], GFP_KERNEL);
		} else {
			arg = argv[i];
			/* If argument name is omitted, set "argN" */
			snprintf(buf, MAX_EVENT_NAME_LEN, "arg%d", i + 1);
			tu->args[i].name = kstrdup(buf, GFP_KERNEL);
		}

		if (!tu->args[i].name) {
			pr_info("Failed to allocate argument[%d] name.\n", i);
			ret = -ENOMEM;
			goto error;
		}

		if (!is_good_name(tu->args[i].name)) {
			pr_info("Invalid argument[%d] name: %s\n", i, tu->args[i].name);
			ret = -EINVAL;
			goto error;
		}

		if (traceprobe_conflict_field_name(tu->args[i].name, tu->args, i)) {
			pr_info("Argument[%d] name '%s' conflicts with "
				"another field.\n", i, argv[i]);
			ret = -EINVAL;
			goto error;
		}

		/* Parse fetch argument */
		ret = traceprobe_parse_probe_arg(arg, &tu->size, &tu->args[i], false, false);
		if (ret) {
			pr_info("Parse error at argument[%d]. (%d)\n", i, ret);
			goto error;
		}
	}

	ret = register_trace_uprobe(tu);
	if (ret)
		goto error;
	return 0;

error:
	free_trace_uprobe(tu);
	return ret;

fail_address_parse:
	if (inode)
		iput(inode);

	pr_info("Failed to parse address.\n");

	return ret;
}

static void cleanup_all_probes(void)
{
	struct trace_uprobe *tu;

	mutex_lock(&uprobe_lock);
	while (!list_empty(&uprobe_list)) {
		tu = list_entry(uprobe_list.next, struct trace_uprobe, list);
		unregister_trace_uprobe(tu);
	}
	mutex_unlock(&uprobe_lock);
}

/* Probes listing interfaces */
static void *probes_seq_start(struct seq_file *m, loff_t *pos)
{
	mutex_lock(&uprobe_lock);
	return seq_list_start(&uprobe_list, *pos);
}

static void *probes_seq_next(struct seq_file *m, void *v, loff_t *pos)
{
	return seq_list_next(v, &uprobe_list, pos);
}

static void probes_seq_stop(struct seq_file *m, void *v)
{
	mutex_unlock(&uprobe_lock);
}

static int probes_seq_show(struct seq_file *m, void *v)
{
	struct trace_uprobe *tu = v;
	int i;

	seq_printf(m, "p:%s/%s", tu->call.class->system, tu->call.name);
	seq_printf(m, " %s:0x%p", tu->filename, (void *)tu->offset);

	for (i = 0; i < tu->nr_args; i++)
		seq_printf(m, " %s=%s", tu->args[i].name, tu->args[i].comm);

	seq_printf(m, "\n");
	return 0;
}

static const struct seq_operations probes_seq_op = {
	.start	= probes_seq_start,
	.next	= probes_seq_next,
	.stop	= probes_seq_stop,
	.show	= probes_seq_show
};

static int probes_open(struct inode *inode, struct file *file)
{
	if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC))
		cleanup_all_probes();

	return seq_open(file, &probes_seq_op);
}

static ssize_t probes_write(struct file *file, const char __user *buffer,
			    size_t count, loff_t *ppos)
{
	return traceprobe_probes_write(file, buffer, count, ppos, create_trace_uprobe);
}

static const struct file_operations uprobe_events_ops = {
	.owner		= THIS_MODULE,
	.open		= probes_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
	.write		= probes_write,
};

/* Probes profiling interfaces */
static int probes_profile_seq_show(struct seq_file *m, void *v)
{
	struct trace_uprobe *tu = v;

	seq_printf(m, "  %s %-44s %15lu\n", tu->filename, tu->call.name, tu->nhit);
	return 0;
}

static const struct seq_operations profile_seq_op = {
	.start	= probes_seq_start,
	.next	= probes_seq_next,
	.stop	= probes_seq_stop,
	.show	= probes_profile_seq_show
};

static int profile_open(struct inode *inode, struct file *file)
{
	return seq_open(file, &profile_seq_op);
}

static const struct file_operations uprobe_profile_ops = {
	.owner		= THIS_MODULE,
	.open		= profile_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= seq_release,
};

/* uprobe handler */
static void uprobe_trace_func(struct trace_uprobe *tu, struct pt_regs *regs)
{
	struct uprobe_trace_entry_head *entry;
	struct ring_buffer_event *event;
	struct ring_buffer *buffer;
	u8 *data;
	int size, i, pc;
	unsigned long irq_flags;
	struct ftrace_event_call *call = &tu->call;

	tu->nhit++;

	local_save_flags(irq_flags);
	pc = preempt_count();

	size = sizeof(*entry) + tu->size;

	event = trace_current_buffer_lock_reserve(&buffer, call->event.type,
						  size, irq_flags, pc);
	if (!event)
		return;

	entry = ring_buffer_event_data(event);
	entry->ip = uprobe_get_swbp_addr(task_pt_regs(current));
	data = (u8 *)&entry[1];
	for (i = 0; i < tu->nr_args; i++)
		call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset);

	if (!filter_current_check_discard(buffer, call, entry, event))
		trace_buffer_unlock_commit(buffer, event, irq_flags, pc);
}

/* Event entry printers */
static enum print_line_t
print_uprobe_event(struct trace_iterator *iter, int flags, struct trace_event *event)
{
	struct uprobe_trace_entry_head *field;
	struct trace_seq *s = &iter->seq;
	struct trace_uprobe *tu;
	u8 *data;
	int i;

	field = (struct uprobe_trace_entry_head *)iter->ent;
	tu = container_of(event, struct trace_uprobe, call.event);

	if (!trace_seq_printf(s, "%s: (", tu->call.name))
		goto partial;

	if (!seq_print_ip_sym(s, field->ip, flags | TRACE_ITER_SYM_OFFSET))
		goto partial;

	if (!trace_seq_puts(s, ")"))
		goto partial;

	data = (u8 *)&field[1];
	for (i = 0; i < tu->nr_args; i++) {
		if (!tu->args[i].type->print(s, tu->args[i].name,
					     data + tu->args[i].offset, field))
			goto partial;
	}

	if (trace_seq_puts(s, "\n"))
		return TRACE_TYPE_HANDLED;

partial:
	return TRACE_TYPE_PARTIAL_LINE;
}

static int probe_event_enable(struct trace_uprobe *tu, int flag)
{
	struct uprobe_trace_consumer *utc;
	int ret = 0;

	if (!tu->inode || tu->consumer)
		return -EINTR;

	utc = kzalloc(sizeof(struct uprobe_trace_consumer), GFP_KERNEL);
	if (!utc)
		return -EINTR;

	utc->cons.handler = uprobe_dispatcher;
	utc->cons.filter = NULL;
	ret = uprobe_register(tu->inode, tu->offset, &utc->cons);
	if (ret) {
		kfree(utc);
		return ret;
	}

	tu->flags |= flag;
	utc->tu = tu;
	tu->consumer = utc;

	return 0;
}

static void probe_event_disable(struct trace_uprobe *tu, int flag)
{
	if (!tu->inode || !tu->consumer)
		return;

	uprobe_unregister(tu->inode, tu->offset, &tu->consumer->cons);
	tu->flags &= ~flag;
	kfree(tu->consumer);
	tu->consumer = NULL;
}

static int uprobe_event_define_fields(struct ftrace_event_call *event_call)
{
	int ret, i;
	struct uprobe_trace_entry_head field;
	struct trace_uprobe *tu = (struct trace_uprobe *)event_call->data;

	DEFINE_FIELD(unsigned long, ip, FIELD_STRING_IP, 0);
	/* Set argument names as fields */
	for (i = 0; i < tu->nr_args; i++) {
		ret = trace_define_field(event_call, tu->args[i].type->fmttype,
					 tu->args[i].name,
					 sizeof(field) + tu->args[i].offset,
					 tu->args[i].type->size,
					 tu->args[i].type->is_signed,
					 FILTER_OTHER);

		if (ret)
			return ret;
	}
	return 0;
}

#define LEN_OR_ZERO		(len ? len - pos : 0)
static int __set_print_fmt(struct trace_uprobe *tu, char *buf, int len)
{
	const char *fmt, *arg;
	int i;
	int pos = 0;

	fmt = "(%lx)";
	arg = "REC->" FIELD_STRING_IP;

	/* When len=0, we just calculate the needed length */

	pos += snprintf(buf + pos, LEN_OR_ZERO, "\"%s", fmt);

	for (i = 0; i < tu->nr_args; i++) {
		pos += snprintf(buf + pos, LEN_OR_ZERO, " %s=%s",
				tu->args[i].name, tu->args[i].type->fmt);
	}

	pos += snprintf(buf + pos, LEN_OR_ZERO, "\", %s", arg);

	for (i = 0; i < tu->nr_args; i++) {
		pos += snprintf(buf + pos, LEN_OR_ZERO, ", REC->%s",
				tu->args[i].name);
	}

	return pos;	/* return the length of print_fmt */
}
#undef LEN_OR_ZERO

static int set_print_fmt(struct trace_uprobe *tu)
{
	char *print_fmt;
	int len;

	/* First: called with 0 length to calculate the needed length */
	len = __set_print_fmt(tu, NULL, 0);
	print_fmt = kmalloc(len + 1, GFP_KERNEL);
	if (!print_fmt)
		return -ENOMEM;

	/* Second: actually write the @print_fmt */
	__set_print_fmt(tu, print_fmt, len + 1);
	tu->call.print_fmt = print_fmt;

	return 0;
}

#ifdef CONFIG_PERF_EVENTS
/* uprobe profile handler */
static void uprobe_perf_func(struct trace_uprobe *tu, struct pt_regs *regs)
{
	struct ftrace_event_call *call = &tu->call;
	struct uprobe_trace_entry_head *entry;
	struct hlist_head *head;
	u8 *data;
	int size, __size, i;
	int rctx;

	__size = sizeof(*entry) + tu->size;
	size = ALIGN(__size + sizeof(u32), sizeof(u64));
	size -= sizeof(u32);
	if (WARN_ONCE(size > PERF_MAX_TRACE_SIZE, "profile buffer not large enough"))
		return;

	preempt_disable();

	entry = perf_trace_buf_prepare(size, call->event.type, regs, &rctx);
	if (!entry)
		goto out;

	entry->ip = uprobe_get_swbp_addr(task_pt_regs(current));
	data = (u8 *)&entry[1];
	for (i = 0; i < tu->nr_args; i++)
		call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset);

	head = this_cpu_ptr(call->perf_events);
	perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head);

 out:
	preempt_enable();
}
#endif	/* CONFIG_PERF_EVENTS */

static
int trace_uprobe_register(struct ftrace_event_call *event, enum trace_reg type, void *data)
{
	struct trace_uprobe *tu = (struct trace_uprobe *)event->data;

	switch (type) {
	case TRACE_REG_REGISTER:
		return probe_event_enable(tu, TP_FLAG_TRACE);

	case TRACE_REG_UNREGISTER:
		probe_event_disable(tu, TP_FLAG_TRACE);
		return 0;

#ifdef CONFIG_PERF_EVENTS
	case TRACE_REG_PERF_REGISTER:
		return probe_event_enable(tu, TP_FLAG_PROFILE);

	case TRACE_REG_PERF_UNREGISTER:
		probe_event_disable(tu, TP_FLAG_PROFILE);
		return 0;
#endif
	default:
		return 0;
	}
	return 0;
}

static int uprobe_dispatcher(struct uprobe_consumer *con, struct pt_regs *regs)
{
	struct uprobe_trace_consumer *utc;
	struct trace_uprobe *tu;

	utc = container_of(con, struct uprobe_trace_consumer, cons);
	tu = utc->tu;
	if (!tu || tu->consumer != utc)
		return 0;

	if (tu->flags & TP_FLAG_TRACE)
		uprobe_trace_func(tu, regs);

#ifdef CONFIG_PERF_EVENTS
	if (tu->flags & TP_FLAG_PROFILE)
		uprobe_perf_func(tu, regs);
#endif
	return 0;
}

static struct trace_event_functions uprobe_funcs = {
	.trace		= print_uprobe_event
};

static int register_uprobe_event(struct trace_uprobe *tu)
{
	struct ftrace_event_call *call = &tu->call;
	int ret;

	/* Initialize ftrace_event_call */
	INIT_LIST_HEAD(&call->class->fields);
	call->event.funcs = &uprobe_funcs;
	call->class->define_fields = uprobe_event_define_fields;

	if (set_print_fmt(tu) < 0)
		return -ENOMEM;

	ret = register_ftrace_event(&call->event);
	if (!ret) {
		kfree(call->print_fmt);
		return -ENODEV;
	}
	call->flags = 0;
	call->class->reg = trace_uprobe_register;
	call->data = tu;
	ret = trace_add_event_call(call);

	if (ret) {
		pr_info("Failed to register uprobe event: %s\n", call->name);
		kfree(call->print_fmt);
		unregister_ftrace_event(&call->event);
	}

	return ret;
}

static void unregister_uprobe_event(struct trace_uprobe *tu)
{
	/* tu->event is unregistered in trace_remove_event_call() */
	trace_remove_event_call(&tu->call);
	kfree(tu->call.print_fmt);
	tu->call.print_fmt = NULL;
}

/* Make a trace interface for controling probe points */
static __init int init_uprobe_trace(void)
{
	struct dentry *d_tracer;

	d_tracer = tracing_init_dentry();
	if (!d_tracer)
		return 0;

	trace_create_file("uprobe_events", 0644, d_tracer,
				    NULL, &uprobe_events_ops);
	/* Profile interface */
	trace_create_file("uprobe_profile", 0444, d_tracer,
				    NULL, &uprobe_profile_ops);
	return 0;
}

fs_initcall(init_uprobe_trace);


text:
package com.ibm.ws.massive.esa;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileAttribute;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipException;

import org.apache.aries.util.VersionRange;
import org.osgi.framework.Version;

import com.ibm.ws.massive.LoginInfo;
import com.ibm.ws.massive.LoginInfoEntry;
import com.ibm.ws.massive.RepositoryException;
import com.ibm.ws.massive.esa.ManifestHeaderProcessor.GenericMetadata;
import com.ibm.ws.massive.esa.internal.EsaManifest;
import com.ibm.ws.massive.resources.AppliesToProcessor;
import com.ibm.ws.massive.resources.EsaResource;
import com.ibm.ws.massive.resources.EsaResource.InstallPolicy;
import com.ibm.ws.massive.resources.ImageDetails;
import com.ibm.ws.massive.resources.MassiveResource.AttachmentResource;
import com.ibm.ws.massive.resources.MassiveResource.AttachmentType;
import com.ibm.ws.massive.resources.MassiveResource.DisplayPolicy;
import com.ibm.ws.massive.resources.MassiveResource.LicenseType;
import com.ibm.ws.massive.resources.MassiveResource.Visibility;
import com.ibm.ws.massive.resources.Provider;
import com.ibm.ws.massive.resources.RepositoryResourceCreationException;
import com.ibm.ws.massive.resources.RepositoryResourceException;
import com.ibm.ws.massive.resources.RepositoryResourceUpdateException;
import com.ibm.ws.massive.resources.UploadStrategy;
import com.ibm.ws.massive.sa.client.model.Asset;
import com.ibm.ws.massive.upload.RepositoryArchiveEntryNotFoundException;
import com.ibm.ws.massive.upload.RepositoryArchiveIOException;
import com.ibm.ws.massive.upload.RepositoryArchiveInvalidEntryException;
import com.ibm.ws.massive.upload.RepositoryUploader;
import com.ibm.ws.massive.upload.internal.MassiveUploader;

/**
 *

 * This class contains methods for working with ESAs inside MaaSive.
 *

 */
public class MassiveEsa extends MassiveUploader implements RepositoryUploader<EsaResource> {

    /**  */
    private static final String JAVA_FILTER_KEY = "JavaSE";

    /**  */
    private static final String VERSION_FILTER_KEY = "version";

    /**  */
    private static final String OSGI_EE_NAMESPACE_ID = "osgi.ee";

    /**  */
    private static final String REQUIRE_CAPABILITY_HEADER_NAME = "Require-Capability";

    /** Map of symbolic name to asset to make finding other features easy */
    private final Map<EsaIdentifier, EsaResource> allFeatures;

    private final Logger logger = Logger.getLogger(MassiveEsa.class.getName());

    /**
     * Construct a new instance and load all of the existing features inside MaaSive.
     *
     * @param userId The userId to use to connect to Massive
     * @param password The password to use to connect to Massive
     * @param apiKey The API key to use to connect to Massive
     * @throws RepositoryException
     */
    public MassiveEsa(LoginInfoEntry loginInfo)
        throws RepositoryException {
        super(loginInfo);

        /*
         * Find all of the features that are already in MaaSive so we can set the enabled
         * information for them
         */
        Collection<EsaResource> allEsas = EsaResource.getAllFeatures(new LoginInfo(_loginInfoResource));
        this.allFeatures = new HashMap<EsaIdentifier, EsaResource>();
        for (EsaResource res : allEsas) {
            // All features must provide a single symbolic name so no
            // null/size check
            logger.log(Level.FINE, "Resource features " + res.getProvideFeature());
            EsaIdentifier identifier = new EsaIdentifier(res.getProvideFeature(), res.getVersion(), res.getAppliesTo());
            allFeatures.put(identifier, res);
        }
    }

    /**
     * This method will add a collection of ESAs into MaaSive
     *
     * @param esas The ESAs to add
     * @return the new {@link EsaResource}s added to massive (will not included any resources that
     *         were modified as a result of this operation)
     * @throws ZipException
     * @throws RepositoryResourceCreationException
     * @throws RepositoryResourceUpdateException
     */
    public Collection<EsaResource> addEsasToMassive(Collection<File> esas, UploadStrategy strategy) throws RepositoryException {
        Collection<EsaResource> resources = new HashSet<EsaResource>();
        for (File esa : esas) {
            EsaResource resource = uploadFile(esa, strategy, null);
            resources.add(resource);
        }

        return resources;
    }

    /*
     * (non-Javadoc)
     *
     * @see com.ibm.ws.massive.upload.RepositoryUploader#canUploadFile(java.io.File)
     */
    @Override
    public boolean canUploadFile(File assetFile) {
        return assetFile.getName().endsWith(".esa");
    }

    /*
     * (non-Javadoc)
     *
     * @see com.ibm.ws.massive.upload.RepositoryUploader#uploadFile(java.io.File,
     * com.ibm.ws.massive.resources.UploadStrategy)
     */
    @Override
    public EsaResource uploadFile(File esa, UploadStrategy strategy, String contentUrl) throws RepositoryException {

        ArtifactMetadata artifactMetadata = explodeArtifact(esa);

        // Read the meta data from the esa
        EsaManifest feature;
        try {
            feature = EsaManifest
                    .constructInstance(esa);
        } catch (IOException e) {
            throw new RepositoryArchiveIOException(e.getMessage(), esa, e);
        }

        /*
         * First see if we already have this feature in MaaSive, note this means we can only have
         * one version of the asset in MaaSive at a time
         */
        EsaResource resource = new EsaResource(_loginInfoResource);
        String symbolicName = feature.getSymbolicName();
        String version = feature.getVersion().toString();
        String appliesTo = feature.getHeader("IBM-AppliesTo");
        EsaIdentifier identifier = new EsaIdentifier(symbolicName, version, appliesTo);

        // Massive assets are always English, find the best name
        String subsystemName = feature.getHeader("Subsystem-Name",
                                                 Locale.ENGLISH);
        String shortName = feature.getIbmShortName();
        String metadataName = artifactMetadata != null ? artifactMetadata.getName() : null;
        final String name;

        /*
         * We want to be able to override the name in the built ESA with a value supplied in the
         * metadata so use this in preference of what is in the ESA so that we can correct any typos
         * post-GM
         */
        if (metadataName != null && !metadataName.isEmpty()) {
            name = metadataName;
        } else if (subsystemName != null && !subsystemName.isEmpty()) {
            name = subsystemName;
        } else if (shortName != null && !shortName.isEmpty()) {
            name = shortName;
        } else {
            // symbolic name is always set
            name = symbolicName;
        }

        resource.setName(name);
        String shortDescription = null;
        if (artifactMetadata != null) {
            shortDescription = artifactMetadata.getShortDescription();
            resource.setDescription(artifactMetadata.getLongDescription());
        }
        if (shortDescription == null) {
            shortDescription = feature.getHeader("Subsystem-Description", Locale.ENGLISH);
        }
        resource.setShortDescription(shortDescription);
        resource.setVersion(version);

        //Add icon files
        processIcons(esa, feature, resource);

        String provider = feature.getHeader("Subsystem-Vendor");
        if (provider != null && !provider.isEmpty()) {
            Provider massiveProvider = new Provider();
            massiveProvider.setName(provider);
            if ("IBM".equals(provider)) {
                massiveProvider.setUrl("http://www.ibm.com");
            }
            resource.setProvider(massiveProvider);
        } else {
            // Massive breaks completely if the provider is not filled in so
            // make sure it is!
            throw new InvalidParameterException("Subsystem-Vendor must be set in the manifest headers");
        }

        // Add custom attributes for WLP
        resource.setProvideFeature(symbolicName);
        resource.setAppliesTo(feature.getHeader("IBM-AppliesTo"));
        Visibility visibility = feature.getVisibility();
        resource.setVisibility(visibility);

        /*
         * Two things affect the display policy - the visibility and the install policy. If a
         * private auto feature is set to manual install we need to make it visible so people know
         * that it exists and can be installed
         */
        DisplayPolicy displayPolicy;
        DisplayPolicy webDisplayPolicy;

        if (visibility == Visibility.PUBLIC) {
            displayPolicy = DisplayPolicy.VISIBLE;
            webDisplayPolicy = DisplayPolicy.VISIBLE;
        } else {
            displayPolicy = DisplayPolicy.HIDDEN;
            webDisplayPolicy = DisplayPolicy.HIDDEN;
        }

        if (feature.isAutoFeature()) {
            resource.setProvisionCapability(feature.getHeader("IBM-Provision-Capability"));
            String IBMInstallPolicy = feature.getHeader("IBM-Install-Policy");

            // Default InstallPolicy is set to MANUAL
            InstallPolicy installPolicy;
            if (IBMInstallPolicy != null && ("when-satisfied".equals(IBMInstallPolicy))) {
                installPolicy = InstallPolicy.WHEN_SATISFIED;
            } else {
                installPolicy = InstallPolicy.MANUAL;
                // As discussed above set the display policy to visible for any manual auto features
                displayPolicy = DisplayPolicy.VISIBLE;
                webDisplayPolicy = DisplayPolicy.VISIBLE;
            }
            resource.setInstallPolicy(installPolicy);
        }

        // if we are dealing with a beta feature hide it otherwise apply the
        // display policies from above
        if (isBeta(resource.getAppliesTo())) {
            resource.setWebDisplayPolicy(DisplayPolicy.HIDDEN);
        } else {
            resource.setWebDisplayPolicy(webDisplayPolicy);
        }

        // Always set displayPolicy
        resource.setDisplayPolicy(displayPolicy);

        // handle required iFixes
        String requiredFixes = feature.getHeader("IBM-Require-Fix");
        if (requiredFixes != null && !requiredFixes.isEmpty()) {
            String[] fixes = requiredFixes.split(",");
            for (String fix : fixes) {
                fix = fix.trim();
                if (!fix.isEmpty()) {
                    resource.addRequireFix(fix);
                }
            }
        }

        resource.setShortName(shortName);

        // Calculate which features this relies on
        for (String requiredFeature : feature.getRequiredFeatures()) {
            resource.addRequireFeature(requiredFeature);
        }

        // feature.supersededBy is a comma-separated list of shortNames. Add
        // each of the elements to either supersededBy or supersededByOptional.
        String supersededBy = feature.getSupersededBy();
        if (supersededBy != null && !supersededBy.trim().isEmpty()) {
            String[] supersededByArray = supersededBy.split(",");
            for (String f : supersededByArray) {
                // If one of the elements is surrounded by [square brackets] then we
                // strip the brackets off and treat it as optional
                if (f.startsWith("[")) {
                    f = f.substring(1, f.length() - 1);
                    resource.addSupersededByOptional(f);
                } else {
                    resource.addSupersededBy(f);
                }
            }
        }

        String attachmentName = symbolicName + ".esa";
        addContent(resource, esa, attachmentName, artifactMetadata, contentUrl);

        // Set the license type if we're using the feature terms agreement so that we know later
        // that there won't be a license information file.
        String subsystemLicense = feature.getHeader("Subsystem-License");
        if (subsystemLicense != null && subsystemLicense.equals("http://www.ibm.com/licenses/wlp-featureterms-v1")) {
            resource.setLicenseType(LicenseType.UNSPECIFIED);
        }

        if (artifactMetadata != null) {
            attachLicenseData(artifactMetadata, resource);
        }

        // Now look for LI, LA files inside the .esa
        // We expect to find them in wlp/lafiles/LI_{Locale} or /LA_{Locale}
        try {
            processLAandLI(esa, resource, feature);
        } catch (IOException e) {
            throw new RepositoryArchiveIOException(e.getMessage(), esa, e);
        }
        resource.setLicenseId(feature.getHeader("Subsystem-License"));

        setJavaRequirements(esa, resource);

        // Publish to massive
        try {
            resource.uploadToMassive(strategy);
        } catch (RepositoryException re) {
            throw re;
        }
        this.allFeatures.put(identifier, resource);
        return resource;
    }

    protected static boolean isBeta(String appliesTo) {
        // Use the appliesTo string to determine whether a feature is a Beta or a regular feature.
        // Beta features are of the format:
        // "com.ibm.websphere.appserver; productVersion=2014.8.0.0; productInstallType=Archive",
        if (appliesTo == null) {
            return false;
        } else {
            String regex = ".*productVersion=" + AppliesToProcessor.BETA_REGEX;
            boolean matches = appliesTo.matches(regex);
            return matches;
        }
    }

    private void processIcons(File esa, EsaManifest feature, EsaResource resource) throws RepositoryException {
        //checking icon file
        int size = 0;
        String current = "";
        String sizeString = "";
        String iconName = "";
        ImageDetails details = null;
        String subsystemIcon = feature.getHeader("Subsystem-Icon");

        if (subsystemIcon != null) {
            subsystemIcon = subsystemIcon.replaceAll("\\s", "");

            StringTokenizer s = new StringTokenizer(subsystemIcon, ",");
            while (s.hasMoreTokens()) {
                current = s.nextToken();

                if (current.contains(";")) { //if the icon has an associated size
                    StringTokenizer t = new StringTokenizer(current, ";");
                    while (t.hasMoreTokens()) {
                        sizeString = t.nextToken();

                        if (sizeString.contains("size=")) {
                            String sizes[] = sizeString.split("size=");
                            size = Integer.parseInt(sizes[sizes.length - 1]);
                            details = new ImageDetails();
                            details.setWidth(size);
                            details.setHeight(size);
                        } else {
                            iconName = sizeString;
                        }
                    }

                } else {
                    iconName = current;
                }

                File icon = this.extractFileFromArchive(esa.getAbsolutePath(), iconName).getExtractedFile();
                if (icon.exists()) {
                    AttachmentResource at = resource.addAttachment(icon, AttachmentType.THUMBNAIL);
                    if (details != null) {
                        at.setImageDetails(details);
                    }
                } else {
                    throw new RepositoryArchiveEntryNotFoundException("Icon does not exist", esa, iconName);
                }
                details = null;
            }
        }
    }

    /**
     * Utility method to delete all the features in MaaSive.
     *
     * @throws IOException
     */
    public void deleteAllFeatures() throws RepositoryResourceException {
        Iterator<EsaResource> featureIterator = this.allFeatures
                .values().iterator();
        while (featureIterator.hasNext()) {
            EsaResource featureResource = featureIterator.next();
            logger.log(Level.INFO, "Deleting " + featureResource.get_id());
            featureResource.delete();
            featureIterator.remove();
        }
    }

    /**
     * Utility method to delete certain features in MaaSive. All versions of the feature will be
     * deleted.
     *
     * @param featureNames The feature symbolic names or IBM short names to delete
     * @throws RepositoryResourceCreationException
     * @throws RepositoryResourceUpdateException
     */
    public void deleteFeatures(Collection<String> featureNames)
            throws IOException, RepositoryException {
        Iterator<EsaResource> featureIterator = this.allFeatures
                .values().iterator();

        /*
         * When we delete a feature we will also need to remove any enabling information that
         * reference it so keep track of which other assets we will need to update at the end.
         */
        //Collection<EsaResource> assetsForUpdating = new HashSet<EsaResource>();
        while (featureIterator.hasNext()) {
            EsaResource featureResource = featureIterator.next();
            String symbolicName = featureResource.getProvideFeature();
            if (featureNames.contains(symbolicName)
                || featureNames.contains(featureResource.getShortName())) {
                //   removeEnablingInformationForFeature(featureResource, assetsForUpdating);
                featureResource.delete();
                featureIterator.remove();
            }
        }

        /*
         * There is an edge case where the removeEnablingInformationForFeature method is over
         * zealous at removing enabling information so run through the add just in case it needs
         * adding back in. This will also process all of the assets that we need to update in
         * Massive. First make sure we haven't deleted the assets we thought needed updating!
         */
        //assetsForUpdating.retainAll(this.allFeatures.values());
        //  addEnablingInformation(assetsForUpdating);

    }

    /**
     * Returns the first feature with the supplied symbolic name. Unlike {@link #getAllFeatures()}
     * this returns the complete feature with attachments.
     *
     * @param symbolicName The symbolic name of the feature to get
     * @return The Asset representing the feature in MaaSive or null if one doesn't
     *         exist
     * @throws IOException
     * @throws RepositoryException
     */
    public EsaResource getFeature(String symbolicName) throws IOException, RepositoryException {
        EsaResource summaryResource = findFeature(symbolicName);
        if (summaryResource != null) {
            return EsaResource.getEsa(_loginInfoResource, summaryResource.get_id());
        } else {
            return null;
        }
    }

    /**
     * Returns the feature with the supplied symbolic name and version. Unlike
     * {@link #getAllFeatures()} this returns the complete feature with attachments.
     *
     * @param symbolicName The symbolic name of the feature to get
     * @param version The version of the feature to get
     * @return The EsaResource representing the feature in Massive or null if one
     *         doesn't exist
     * @throws IOException
     * @throws RepositoryException
     */
    public EsaResource getFeature(String symbolicName, String version, String appliesTo) throws IOException, RepositoryException {
        EsaResource summaryResource = this.allFeatures.get(new EsaIdentifier(symbolicName, version, appliesTo));
        if (summaryResource != null) {
            return EsaResource.getEsa(_loginInfoResource, summaryResource.get_id());
        } else {
            return null;
        }
    }

    /**
     * Returns all features inside Massive. Note that these are just the summary of features
     * returned from Massive's GET all request. Most of the fields will be filled in but the
     * attachments will not be. In order to get the attachments as well call
     * {@link #getFeature(String)}.
     *
     * @return A collection of {@link Asset}s representing the features in Massive or an empty
     *         collection if none exists
     */
    public Collection<EsaResource> getAllFeatures() {
        /*
         * A map's values() collection is backed by the map so if someone clears it we'll be in
         * trouble... stop them!
         */
        return Collections.unmodifiableCollection(this.allFeatures.values());
    }

    /**
     * Finds a feature with the provided symbolic name
     *
     * @param symbolicName The symbolic name to look for, must not be null
     * @return the first EsaResource with the provided symbolicName that is found or
     *         null if none is found
     */
    private EsaResource findFeature(String symbolicName) {
        for (EsaResource esa : this.allFeatures.values()) {
            if (symbolicName.equals(esa.getProvideFeature())) {
                return esa;
            }
        }
        return null;
    }

    @Override
    protected void checkRequiredProperties(ArtifactMetadata artifact) throws RepositoryArchiveInvalidEntryException {
        checkPropertySet(PROP_DESCRIPTION, artifact);
    }

    /**
     * This class holds identification information about an ESA to uniquely identify it and
     * implements equals and hash code so it can be used as a lookup key.
     */
    private static class EsaIdentifier {

        private final String symbolicName;
        private final String version;
        private final String appliesTo;

        /**
         * @param symbolicName The symbolic name of the ESA
         * @param version The version of the ESA
         */
        public EsaIdentifier(String symbolicName, String version, String appliesTo) {
            this.symbolicName = symbolicName;
            this.version = version;
            this.appliesTo = appliesTo;
        }

        /*
         * (non-Javadoc)
         *
         * @see java.lang.Object#hashCode()
         */
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((symbolicName == null) ? 0 : symbolicName.hashCode());
            result = prime * result + ((version == null) ? 0 : version.hashCode());
            result = prime * result + ((appliesTo == null) ? 0 : appliesTo.hashCode());
            return result;
        }

        /*
         * (non-Javadoc)
         *
         * @see java.lang.Object#equals(java.lang.Object)
         */
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            EsaIdentifier other = (EsaIdentifier) obj;
            if (symbolicName == null) {
                if (other.symbolicName != null)
                    return false;
            } else if (!symbolicName.equals(other.symbolicName))
                return false;
            if (version == null) {
                if (other.version != null)
                    return false;
            } else if (!version.equals(other.version))
                return false;
            if (appliesTo == null) {
                if (other.appliesTo != null)
                    return false;
            } else if (!appliesTo.equals(other.appliesTo))
                return false;
            return true;
        }

    }

    /**
     * Look in the esa for bundles with particular java version requirements. Create an aggregate
     * requirement of the esa as a whole, and write the data into the supplied resource
     *
     * @param esa
     * @param resource
     * @throws RepositoryException If there are any IOExceptions reading the esa, or if the the
     *             bundles have conflicting Java version requirements.
     */
    private static void setJavaRequirements(File esa, EsaResource resource) throws RepositoryException {

        Map<String, String> bundleRequirements = new HashMap<String, String>();
        Path zipfile = esa.toPath();
        VersionRange esaRange = null;

        try (final FileSystem zipSystem = FileSystems.newFileSystem(zipfile, null)) {

            class BundleFinder extends SimpleFileVisitor<Path> {
                ArrayList<Path> bundles = new ArrayList<Path>();
                // Bundles should be jars in the root of the zip
                PathMatcher bundleMatcher = zipSystem.getPathMatcher("glob:/*.jar");

                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attr) {
                    if (bundleMatcher.matches(file)) {
                        bundles.add(file);
                    }
                    return FileVisitResult.CONTINUE;
                }
            }

            Iterable<Path> roots = zipSystem.getRootDirectories();
            BundleFinder finder = new BundleFinder();
            for (Path root : roots) {
                // Bundles should be in the root of the zip, so depth is 1
                Files.walkFileTree(root, new HashSet<FileVisitOption>(), 1, finder);
            }

            for (Path bundle : finder.bundles) {

                // Need to extract the bundles to read their manifest, can't find a way to do this in place.
                Path extractedJar = Files.createTempFile(null, ".jar", new FileAttribute[0]);
                extractedJar.toFile().deleteOnExit();
                Files.copy(bundle, extractedJar, StandardCopyOption.REPLACE_EXISTING);

                Manifest bundleJarManifest = null;
                try (JarFile bundleJar = new JarFile(extractedJar.toFile())) {
                    bundleJarManifest = bundleJar.getManifest();
                }
                Attributes bundleManifestAttrs = bundleJarManifest.getMainAttributes();
                String requireCapabilityAttr = bundleManifestAttrs.getValue(REQUIRE_CAPABILITY_HEADER_NAME);
                if (requireCapabilityAttr == null) {
                    continue;
                }

                // The Require-Capability attribute will look a little like this:
                // Require-Capability: osgi.ee; filter:="(&(osgi.ee=JavaSE)(version>=1.7))"
                List<GenericMetadata> requirementMetadata = ManifestHeaderProcessor.parseRequirementString(requireCapabilityAttr);
                GenericMetadata eeVersionMetadata = null;
                for (GenericMetadata metaData : requirementMetadata) {
                    if (metaData.getNamespace().equals(OSGI_EE_NAMESPACE_ID)) {
                        eeVersionMetadata = metaData;
                        break;
                    }
                }

                if (eeVersionMetadata == null) {
                    // No version requirements, go to the next bundle
                    continue;
                }

                Map<String, String> dirs = eeVersionMetadata.getDirectives();
                for (Entry<String, String> directive : dirs.entrySet()) {
                    String key = directive.getKey();
                    if (!key.equals("filter")) {
                        continue;
                    }

                    Map<String, String> filter = null;
                    filter = ManifestHeaderProcessor.parseFilter(dirs.get(key));

                    // The interesting filter should contain osgi.ee=JavaSE and version=XX
                    if (!(filter.containsKey(OSGI_EE_NAMESPACE_ID) && filter.get(OSGI_EE_NAMESPACE_ID).equals(JAVA_FILTER_KEY)
                    && filter.containsKey(VERSION_FILTER_KEY))) {
                        continue; // Uninteresting filter
                    }

                    // Store the raw filter to add to the resource later.
                    bundleRequirements.put(bundle.getFileName().toString(), directive.getValue());

                    VersionRange range = ManifestHeaderProcessor.parseVersionRange(filter.get(VERSION_FILTER_KEY));
                    if (esaRange == null) {
                        esaRange = range;
                    } else {
                        VersionRange newRange = range.intersect(esaRange);
                        if (newRange == null) {
                            // VersionRange.intersect returns null if the ranges are incompatible
                            throw new RepositoryException("ESA " + zipfile.getFileName() + " is invalid, two bundles " +
                                                          "require incompatible JavaSE versions");
                        }
                        esaRange = newRange;
                    }

                    // Assume there is only one Java version filter, so stop looking
                    break;
                }

            }

        } catch (IOException e) {
            // Any IOException means that the version info isn't reliable, so only thing to do is ditch out.
            throw new RepositoryArchiveIOException(e.getMessage(), esa, e);
        }

        ArrayList<String> rawRequirements = new ArrayList<String>();
        for (Entry<String, String> bundleRequirement : bundleRequirements.entrySet()) {
            rawRequirements.add(bundleRequirement.getKey() + ": " + bundleRequirement.getValue());
        }
        if (rawRequirements.size() == 0) {
            rawRequirements = null;
        }

        String minimum = null;
        String maximum = null;
        if (esaRange != null) {
            Version minimumVersion = esaRange.getMinimumVersion();
            if (minimumVersion != null) {
                minimum = minimumVersion.toString();
            }
            Version maximumVersion = esaRange.getMaximumVersion();
            if (maximumVersion != null) {
                maximum = maximumVersion.toString();
            }
        }

        validateJavaVersionRange(esaRange);

        resource.setJavaSEVersionRequirements(minimum, maximum, rawRequirements);

    }

    /**
     * Check that the version range in this ESA is acceptable. Acceptable is defined by what the
     * tooling can deal with, and what is currently known about Liberty.
     *
     * @param range
     * @throws RepositoryException if the version range is unexpected
     */
    private static void validateJavaVersionRange(VersionRange range) throws RepositoryException {
        if (range == null) {
            // This is fine. The ESA is implicitly valid on whatever liberty supports.
            return;
        }
        Version max = range.getMaximumVersion();
        Version min = range.getMinimumVersion();
        if (max != null) {
            // If this is the case then the ESA should have specified an exact required version.
            // If the min version is either not specified or the min version != max version, then
            // treat it as badly formed, as a range is not currently expected, and there is no logic to
            // handle displaying an ESA like this.
            if (min == null || !min.equals(max)) {
                throw new RepositoryException("Unexpected upper bound to Java version range");
            }
        }

        if (min == null) {
            throw new RepositoryException("Lower bound to Java version range shouldn't be empty");
        }

        Version version6 = new Version(1, 6, 0);
        Version version7 = new Version(1, 7, 0);
        if (min.equals(version6) || min.equals(version7)) {
            return;
        }

        throw new RepositoryException("Lower bound to Java version range is expected to be either Java 6 or Java 7."
                                      + " Actually was " + min);

    }

}