package ca.mcgill.sis.dmas.kam1n0.app;

import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import com.google.common.collect.ImmutableMap;
import com.sun.mail.imap.AppendUID;

import ca.mcgill.sis.dmas.kam1n0.AppController;
import ca.mcgill.sis.dmas.kam1n0.AppPlatform;
import ca.mcgill.sis.dmas.kam1n0.UserController;
import ca.mcgill.sis.dmas.kam1n0.AppPlatform.Prioritize;
import ca.mcgill.sis.dmas.kam1n0.app.adata.BinaryDataUnit;
import ca.mcgill.sis.dmas.kam1n0.app.adata.FunctionCommentWrapper;
import ca.mcgill.sis.dmas.kam1n0.app.adata.FunctionDataUnit;
import ca.mcgill.sis.dmas.kam1n0.app.user.UserInfo;
import ca.mcgill.sis.dmas.kam1n0.app.util.MVCUtils;
import ca.mcgill.sis.dmas.kam1n0.app.util.ModelAndFragment;
import ca.mcgill.sis.dmas.kam1n0.commons.defs.Architecture.ArchitectureType;
import ca.mcgill.sis.dmas.kam1n0.framework.disassembly.AsmLineNormalizationResource;
import ca.mcgill.sis.dmas.kam1n0.framework.disassembly.AsmLineNormalizer;
import ca.mcgill.sis.dmas.kam1n0.framework.disassembly.NormalizationSetting;
import ca.mcgill.sis.dmas.kam1n0.framework.storage.Binary;
import ca.mcgill.sis.dmas.kam1n0.framework.storage.Comment;
import ca.mcgill.sis.dmas.kam1n0.framework.storage.Function;

/**
 * All the APP services follow path:
 *
 * \/app_type\/app_id\/other_request...
 *
 */
public abstract class ApplicationHandler {

	protected ApplicationMeta meta = null;
	private static Logger logger = LoggerFactory.getLogger(ApplicationHandler.class);

	public ApplicationHandler(ApplicationMeta meta) {
		this.meta = meta;
	}

	@Autowired
	protected AppController controller;

	@Autowired
	protected UserController userController;

	@Autowired
	private ApplicationInfoValidatorUpdated validator;

	@Autowired
	private BCryptPasswordEncoder bCryptPasswordEncoder;

	public final static String FRAG_APP_PLUG = "apps/app_plugin";
	public final static String FRAG_APP_TITLE = "apps/app_title";
	public final static String FRAG_APP_BIN_LIST = "apps/app_binary_list";
	public final static String FRAG_APP_FUNC_FLOW = "apps/app_func_flow";
	public final static String FRAG_APP_FUNC_TEXT = "apps/app_func_text";

	public ModelAndView getHomeModelAndViewImpl(long appId) {
		ApplicationInfoSummary summary = meta.getInfoSummary(appId);
		return MVCUtils.wrapAuthenticatedHomePage(summary.basicInfo.applicationType + '/',
				Long.toString(summary.basicInfo.appId), getAppTileFragment(summary),
				getBinaryListFragment(summary.basicInfo));
	}

	public final ModelAndFragment getAppTileFragment(ApplicationInfoSummary summary) {
		return new ModelAndFragment(FRAG_APP_TITLE, summary);
	}

	public final ModelAndFragment getBinaryListFragment(ApplicationInfo info) {
		return new ModelAndFragment(FRAG_APP_BIN_LIST, info);
	}

	public List<Binary> getBinaries(long appId) {
		return meta.getBinaries(appId);
	}

	public List<Function> getFunctions(long appId, long binaryId) {
		return meta.getFunctions(appId, binaryId);
	}

	public final static String getHomePath(String appType, long appId) {
		if (!appType.startsWith("/"))
			appType = "/" + appType;
		return appType + "/" + Long.toString(appId) + "/home";
	}

	public final static String getPrefixPath(String appType, long appId) {
		if (!appType.startsWith("/"))
			appType = "/" + appType;
		return appType + "/" + Long.toString(appId) + "/";
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/", method = RequestMethod.GET)
	public final ModelAndView getAppPluginHome(@PathVariable("appId") long appId) {
		ApplicationInfoSummary summary = meta.getInfoSummary(appId);
		return MVCUtils.wrapAuthenticatedHomePage(summary.basicInfo.applicationType + '/',
				Long.toString(summary.basicInfo.appId), new ModelAndFragment(FRAG_APP_PLUG, summary));
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/home", method = RequestMethod.GET)
	public final ModelAndView getHomeModelAndView(@PathVariable("appId") long appId) {
		return getHomeModelAndViewImpl(appId);
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/info", method = RequestMethod.GET)
	@ResponseBody
	public final ApplicationInfo getApplicationInfo(@PathVariable("appId") long appId) {
		return this.meta.getInfo(appId);
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/func_info", method = RequestMethod.GET)
	@ResponseBody
	public final List<FunctionDataUnit> getFunctionInfos(@PathVariable("appId") long appId,
			@RequestParam("id") long binaryId) {
		return this.meta.getFunctions(appId, binaryId).stream().map(func -> new FunctionDataUnit(func, true))
				.collect(Collectors.toList());
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/bin_info", method = RequestMethod.GET)
	@ResponseBody
	public final List<BinaryDataUnit> getBinInfos(@PathVariable("appId") long appId) {
		return this.getBinaries(appId).stream().map(BinaryDataUnit::new).collect(Collectors.toList());
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/func_flow", method = RequestMethod.GET)
	@ResponseBody
	public final FunctionDataUnit getFunctionFlow(@PathVariable("appId") long appId,
			@RequestParam("id") long functionId) {
		return this.meta.getFunction(appId, functionId);
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/func_flow_show", method = RequestMethod.GET)
	public final ModelAndView showFunctionFlow(@PathVariable("appId") long appId) {
		try {
			ApplicationInfoSummary summary = meta.getInfoSummary(appId);
			return MVCUtils.wrapAuthenticatedRenderer(new ModelAndFragment(FRAG_APP_FUNC_FLOW, summary));
		} catch (Exception e) {
			logger.error("Failed creating func flow view.", e);
			return errorMV(e.getMessage());
		}
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/func_text_show", method = RequestMethod.GET)
	public final ModelAndView showFunctionText(@PathVariable("appId") long appId) {
		try {
			ApplicationInfoSummary summary = meta.getInfoSummary(appId);
			return MVCUtils.wrapAuthenticatedRenderer(new ModelAndFragment(FRAG_APP_FUNC_TEXT, summary));
		} catch (Exception e) {
			logger.error("Failed creating func flow view.", e);
			return errorMV(e.getMessage());
		}
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/func_comment", method = RequestMethod.GET)
	@ResponseBody
	public final List<FunctionCommentWrapper> getComment(@PathVariable("appId") long appId,
			@RequestParam("fid") long functionId) {
		try {
			List<FunctionCommentWrapper> comments = meta.getComment(appId, functionId);
			return comments;
		} catch (Exception e) {
			logger.error("Failed get commnet.", e);
			return null;
		}
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/func_comment", method = RequestMethod.POST)
	@ResponseBody
	public final Map<String, Object> putComment(@PathVariable("appId") long appId,
			@RequestParam("functionId") long functionId, @RequestParam("functionOffset") String functionOffset,
			@RequestParam("date") String date, @RequestParam("comment") String content) {
		try {

			if (content.length() > 0 && !meta.checkFunc(appId, functionId)) {
				return ImmutableMap.of("error", "The function for your comment is not in this repository.");
			}

			Comment comment = new Comment();
			comment.functionId = functionId;
			comment.functionOffset = functionOffset.trim();
			if (date.trim().length() > 0)
				comment.date = Long.parseLong(date.trim());
			else
				comment.date = new Date().getTime();
			comment.comment = content;
			comment.userName = UserController.findUserName();
			meta.putComment(appId, comment);
			return ImmutableMap.of("result", comment);
		} catch (Exception e) {
			logger.error("Failed put commnet.", e);
			return ImmutableMap.of("error", "Failed to update the comment.");
		}
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/arch_rest", method = RequestMethod.GET)
	@ResponseBody
	public final AsmLineNormalizationResource getArchitectureResources(@RequestParam("arch") ArchitectureType arch) {
		return AsmLineNormalizationResource.retrieve(arch);
	}

	@Prioritize
	@RequestMapping(value = "/{appId:.+}/del", method = RequestMethod.POST)
	@ResponseBody
	public final Map<String, Object> delete(@PathVariable("appId") long appId, String appName, String pwd) {
		try {
			UserInfo info = UserController.findUser();
			if (!bCryptPasswordEncoder.matches(pwd, info.credential))
				return ImmutableMap.of("error", "You entered the wrong password. Please retry.");
			ApplicationInfo appInfo = getApplicationInfo(appId);
			if (appInfo == null)
				return ImmutableMap.of("error", "The application that you are trying to delete is non-existed.");
			if (!appInfo.name.equals(appName))
				return ImmutableMap.of("error", "You entered the wrong application name. Please retry.");
			meta.delete(appId);
			Set<String> all_old = Stream.concat(appInfo.users_read.stream(), appInfo.users_wirte.stream())
					.collect(Collectors.toSet());
			userController.removeAccessibleApp(appId, all_old);
			return ImmutableMap.of();
		} catch (Exception e) {
			logger.error("Failed delete application.", e);
			return ImmutableMap.of("error", "Failed to delete the application.");
		}
	}

	@Prioritize
	@GetMapping("/{appId:.+}/editApp")
	public ModelAndView showApplicationFormEdit(@PathVariable("appId") long appId, Model model) {
		try {
			ApplicationInfo info = getApplicationInfo(appId);
			model.addAttribute("confObj", info);
			model.addAttribute("applicationTypes", AppPlatform.appTypes.keySet());
			model.addAttribute("appConfForm", info.configuration.createView());
			model.addAttribute("edit", true);
			return MVCUtils.wrapAuthenticatedHomePage("Edit an application.", "Please edit the required details.",
					new ModelAndFragment(AppController.FRAG_APP_CONF, model));
		} catch (Exception e) {
			logger.error("Failed to create application form..", e);
			return MVCUtils.errorMV("Failed to create application form.");
		}
	}

	@Prioritize
	@PostMapping("/{appId:.+}/editApp")
	public ModelAndView submitApplicationFormEdit(@PathVariable("appId") long appId,
			@RequestParam(value = "reads", defaultValue = "") List<String> reads,
			@RequestParam(value = "writes", defaultValue = "") List<String> writes,
			@ModelAttribute("confObj") final ApplicationInfo info, BindingResult bindingResult, Model model) {
		try {
			validator.validate(info, bindingResult);
			if (bindingResult.hasErrors()) {
				if (info.configuration != null)
					model.addAttribute("appConfForm", info.configuration.createView());
				model.addAttribute("confObj", info);
				model.addAttribute("applicationTypes", AppPlatform.appTypes.keySet());
				model.addAttribute("edit", true);
				return MVCUtils.wrapAuthenticatedHomePage("Edit an application.", "Please edit the following errors.",
						new ModelAndFragment(AppController.FRAG_APP_CONF, model));
			}
			ApplicationInfo oldInfo = getApplicationInfo(appId);
			if (oldInfo == null) {
				logger.error("A user is trying to update an non-existed app. {} {}", UserController.findUser(), appId);
				return MVCUtils.errorMV("Error", "The app you are trying to update is non-existed.");
			}
			info.users_wirte = writes.stream().filter(val -> val.trim().length() > 0)
					.collect(Collectors.toCollection(HashSet::new));
			info.users_read = reads.stream().filter(val -> val.trim().length() > 0)
					.collect(Collectors.toCollection(HashSet::new));
			info.users_read.removeAll(info.users_wirte);
			controller.updateApplicationInstance(info);
			model.addAttribute("edit", true);

			Set<String> all_old = Stream.concat(oldInfo.users_read.stream(), oldInfo.users_wirte.stream())
					.collect(Collectors.toSet());
			userController.removeAccessibleApp(appId, all_old);
			Set<String> all_new = Stream.concat(info.users_read.stream(), info.users_wirte.stream())
					.collect(Collectors.toSet());
			userController.addAccessibleApp(appId, all_new);

			return MVCUtils.redirectMV("/userHome");
		} catch (Exception e) {
			logger.error("Failed to create application.", e);
			return MVCUtils.errorMV("Failed to create application {}", e.getMessage());
		}
	}

	public final static ModelAndView errorMV(String message, Object... param) {
		return MVCUtils.errorMV(message, param);
	}

	public final static ModelAndView succeedMV(String message, Object... param) {
		return MVCUtils.succeedMV(message, param);
	}

	@InitBinder
	public void initBinder(WebDataBinder webDataBinder, HttpServletRequest servletRequest) {
		controller.initBinder(webDataBinder, servletRequest);
	}

}


package pl.betoncraft.betonquest.objectives;

import org.bukkit.Bukkit;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.enchantment.EnchantItemEvent;
import pl.betoncraft.betonquest.BetonQuest;
import pl.betoncraft.betonquest.Instruction;
import pl.betoncraft.betonquest.api.Objective;
import pl.betoncraft.betonquest.exceptions.InstructionParseException;
import pl.betoncraft.betonquest.item.QuestItem;
import pl.betoncraft.betonquest.utils.PlayerConverter;

import java.util.List;
import java.util.Locale;

/**
 * Requires the player to enchant an item.
 */
public class EnchantObjective extends Objective implements Listener {

    private final QuestItem item;
    private final List<EnchantmentData> enchantments;

    public EnchantObjective(final Instruction instruction) throws InstructionParseException {
        super(instruction);
        template = ObjectiveData.class;
        item = instruction.getQuestItem();
        enchantments = instruction.getList(EnchantmentData::convert);
        if (enchantments.isEmpty()) {
            throw new InstructionParseException("Not enough arguments");
        }
    }

    @EventHandler(ignoreCancelled = true)
    public void onEnchant(final EnchantItemEvent event) {
        final String playerID = PlayerConverter.getID(event.getEnchanter());
        if (!containsPlayer(playerID)) {
            return;
        }
        if (!item.compare(event.getItem())) {
            return;
        }
        for (final EnchantmentData enchant : enchantments) {
            if (!event.getEnchantsToAdd().keySet().contains(enchant.getEnchantment())
                    || event.getEnchantsToAdd().get(enchant.getEnchantment()) < enchant.getLevel()) {
                return;
            }
        }
        if (checkConditions(playerID)) {
            completeObjective(playerID);
        }
    }

    @Override
    public void start() {
        Bukkit.getPluginManager().registerEvents(this, BetonQuest.getInstance());
    }

    @Override
    public void stop() {
        HandlerList.unregisterAll(this);
    }

    @Override
    public String getDefaultDataInstruction() {
        return "";
    }

    @Override
    public String getProperty(final String name, final String playerID) {
        return "";
    }

    public static class EnchantmentData {

        private final Enchantment enchantment;
        private final int level;

        public EnchantmentData(final Enchantment enchantment, final int level) {
            this.enchantment = enchantment;
            this.level = level;
        }

        @SuppressWarnings("deprecation")
        public static EnchantmentData convert(final String string) throws InstructionParseException {
            final String[] parts = string.split(":");
            if (parts.length != 2) {
                throw new InstructionParseException("Could not parse enchantment: " + string);
            }
            final Enchantment enchantment = Enchantment.getByName(parts[0].toUpperCase(Locale.ROOT));
            if (enchantment == null) {
                throw new InstructionParseException("Enchantment type '" + parts[0] + "' does not exist");
            }
            final int level;
            try {
                level = Integer.parseInt(parts[1]);
            } catch (NumberFormatException e) {
                throw new InstructionParseException("Could not parse enchantment level: " + string, e);
            }
            return new EnchantmentData(enchantment, level);
        }

        public Enchantment getEnchantment() {
            return enchantment;
        }

        public int getLevel() {
            return level;
        }
    }

}


<?php
/** Cron jobs class, designed to be run every minute with internal handling of periods
 * - Tracks start/stop times
 * - Controls overlaps
 * - Launches cronjobs from multiple objects
 */
class CronJobs {
	/** Crontab line to add
	 * @param string $main_php
	 * 	Main file to be executed
	 * @return string
	 */
	public static function crontab($main_php, $argument=''){
		$main_php = realpath($main_php);

		$sched = '* * * * *';
		$cd = dirname($main_php);
		$php = '/usr/bin/env php';
		$script = basename($main_php);
		return "$sched	cd '$cd' && $php '$script' $argument";
		}

	/** Cron data storage file
	 * @var string
	 */
	protected $_cronfile;

	/** Cron jobs
	 * @var _CronJobMethod[]
	 */
	protected $_jobs;

	/** Cron jobs metadata
	 * @var _CronJobMeta[]
	 */
	protected $_jobs_meta;

	function __construct($cronfile){
		$this->_cronfile = $cronfile;
		}

	/** Get all jobs. If metadata is available, it's loaded
	 * @return _CronJobMethod[] array( job-name => job-meta )
	 */
	function get_jobs($of_class = null){
		if (is_null($this->_jobs_meta))
			$this->_cronfile_load();

		$jobs = array();
		foreach ($this->_jobs as $job){
			if (isset($this->_jobs_meta[$job->fullname]))
				$job->meta = $this->_jobs_meta[$job->fullname];

			if (is_null($of_class) || $of_class == get_class($job->object))
				$jobs[$job->fullname] = $job;
			}
		return $jobs;
		}

	/** Check whether the cron is configured (executed within the last hour)
	 * @return bool
	 */
	function is_configured(){
		if (!file_exists($this->_cronfile))
			return false;
		return (time() - filemtime($this->_cronfile)) < (60*10);
		}

	/** Load the contents of the cronfile
	 */
	protected function _cronfile_load(){
		$data = array(
			'_jobs_meta' => array(),
			);
		if (file_exists($this->_cronfile))
			$data = unserialize(file_get_contents($this->_cronfile));
		$this->_jobs_meta = $data['_jobs_meta'];
		}

	/** Store the contents of the cronfile
	 */
	protected function _cronfile_save(){
		$data = serialize(array(
			'_jobs_meta' => $this->_jobs_meta,
			));
		file_put_contents($this->_cronfile, $data);
		@chmod($this->_cronfile, 0666); # don't bark, just do the job
		}

	/** Update the cronfile with one job's result
	 * @param _CronJobMethod $job
	 */
	protected function _cronfile_update(_CronJobMethod $job){
		$this->_cronfile_load();
		$job->meta->exec_last = time();
		$this->_jobs_meta[$job->fullname] = $job->meta;
		$this->_cronfile_save();
		}

	const JOB_PREFIX = 'cronjob_';

	/** Register a class which implements ICronJobs interface
	 * Methods from this class which are public and starts with JOB_PREFIX are run as jobs.
	 * @param ICronJobs $object
	 */
	public function register(ICronJobs $object){
		$c = new ReflectionClass($object);
		$c_methods = $c->getMethods(ReflectionMethod::IS_PUBLIC);

		foreach ($c_methods as $method)
			if (strncmp($method->name, self::JOB_PREFIX, strlen(self::JOB_PREFIX)) == 0){
				$job_fullname = get_class($object).'::'.$method->name;
				$this->_jobs[$job_fullname] = new _CronJobMethod($job_fullname, $object, $method);
				}
		return false;
		}

	/** Execute a cronjob
	 * @param _CronJobMethod $job
	 * 	The job object to execute
	 * @param array $job_args
	 * 	Optional arguments list to pass
	 * @param bool $check_scheduled
	 * 	Whether to check if the job is scheduled
	 * @return _CronJobReport|null
	 */
	protected function _job_execute(_CronJobMethod $job, $job_args = array(), $check_scheduled = true){
		$this->_cronfile_load();
		# Get meta
		if (!isset($this->_jobs_meta[$job->fullname])){
			$meta = new _CronJobMeta;
			$meta->context = $job->object;
			$meta->exec_first = time();
			$this->_jobs_meta[$job->fullname] = $meta;
			}
		$job->meta = $this->_jobs_meta[$job->fullname];

		# Is scheduled?
		if ($check_scheduled && !$job->scheduled())
			return null;

		# Prepare
		$job->meta->exec_count++;
		$job->meta->exec_last = time();
		$job->meta->running = true;
		$job->meta->last_error = null;
		$job->meta->last_result = null;
		$this->_cronfile_update($job);

		# Execute it!
		$report = new _CronJobReport($job);
		try { $report->result = $job->execute($job_args); }
			catch (CronJobException $e){
			$report->exception = $e;
			$report->error = $e->getMessage();
			$job->meta->last_error = $report->error;
			}

		# Report success
		$job->meta->running = false;
		$job->meta->last_result = $report->result;
		$this->_cronfile_update($job);
		return $report;
		}

	/** Jobs sort callback
	 */
	protected static function _jobs_sort_cb(_CronJobMethod $a, _CronJobMethod $b){
		if ($a->crondoc_weight == $b->crondoc_weight)
			return 0;
		return  ($a->crondoc_weight < $b->crondoc_weight)? -1 : 1;
		}

	/** Execute the next job, destuctively iterating thru the list of all jobs
	 * @return _CronJobReport
	 * @return NULL on no more jobs
	 */
	function run_next(){
		uasort($this->_jobs, array('CronJobs', '_jobs_sort_cb'));

		$this->_cronfile_load();
		while (!is_null($job = array_shift($this->_jobs))){
			$report = $this->_job_execute($job);
			if (is_null($report))
				continue;
			return $report;
			}
		$this->_cronfile_save();
		return null;
		}
	}

/** Implement this interface to declare cronjobs collection
 *
 * Create cron jobs by implementing public methods starting with JOB_PREFIX
 * Each method can return an array which is printed in JSON.
 * Also it can throw CronJobException to report an error
 *
 * You can store persistent metadata in this class' properties.
 */
interface ICronJobs {
	}

class CronJobException extends Exception {}

/** Report of a cronjob execution
 */
class _CronJobReport {
	function __construct(_CronJobMethod $job){
		$this->job = $job;
		$this->fullname = $job->fullname;
		}

	/** Full name of the method been executed
	 * @var string
	 */
	public $fullname;

	/** Cronjob object
	 * @var _CronJobMethod
	 */
	public $job;

	/** Caught exception, if any
	 * @var Exception
	 */
	public $exception;

	/** Error string, if any.
	 * On exception, contains its string
	 * @var string|null
	 */
	public $error;

	/** Method's return value
	 * @var array
	 */
	public $result;
	}

/** CronJob metadata, persisted into the cronfile
 */
class _CronJobMeta {
	/** Persistent context
	 * @var ICronJobs
	 */
	public $context;

	/** First execution timestamp
	 * @var int
	 */
	public $exec_first = null;

	/** Last execution timestamp
	 * @var int|null
	 */
	public $exec_last = null;

	/** The number of launches
	 * @var int
	 */
	public $exec_count = 0;

	/** Whether the job is still running (overlap control)
	 * @var bool
	 */
	public $running = false;

	/** The result of the last execution, if not error
	 * @var array
	 */
	public $last_result = array();

	/** Error string, if the last CronJob execution ended-up with an error
	 * @var string
	 */
	public $last_error = null;
	}

/** A single cron jobs method parsing & handling.
 * A cronjob is a method within a subclass of CronJobs.
 * All metadata is given via the doccomment's @cron tag.
 * Format:
 * 	@cron <set>: <value>
 * Examples:
 * 	@cron period: 10m
 * 	@cron if: return $GLOBALS['config']['run_me']==1
 */
class _CronJobMethod {
	/** Fully-qualified task name: class::method
	 * @var string
	 */
	public $fullname;

	/** Cron collection object
	 * @var ICronJobs
	 */
	public $object;

	/** Cron task method
	 * @var ReflectionMethod
	 */
	public $method;

	/** Job-local metadata
	 * @var _CronJobMeta|null
	 */
	public $meta;

	function __construct($fullname, $object, $method){
		$this->fullname = $fullname;
		$this->object = $object;
		$this->method = $method;
		# Parse the doccomment
		preg_match_all('~@cron +([^:]+) *: *(.*) *$~imuS', $this->method->getDocComment(), $matches, PREG_SET_ORDER);
		foreach ($matches as $m){
			$method = "_docparse_{$m[1]}";
			$value = $m[2];
			$this->$method($value);
			}
		}

	/** Get job description from DocComment
	 * @return string
	 */
	function get_description(){
		$descr = $this->method->getDocComment();

		if (FALSE === $p = strpos($descr, '@'))
			$p = strlen($descr);

		$descr = substr($descr, 0, $p);
		$descr = preg_replace('~^\s*[/\*]+\s*~im', '', $descr);
		return trim($descr);
		}

	/** Check whether this cronjob should be run now
	 * @return bool
	 */
	function scheduled(){
		# Still running jobs
		if ($this->meta->running){
			# One exception: if it's running for more than the current time limit - it's probably dead
			if ((time() - $this->meta->exec_last) < ini_get('max_execution_time'))
				return false; # yep, really is still running
			}

		# @cron if:
		if (!is_null($this->crondoc_if))
			if (!$this->crondoc_if)
				return false;

		# @cron period:
		if (!is_null($this->crondoc_period)){
			if (is_null($this->meta->exec_last))
				return true;
			return ( time()+3 - $this->meta->exec_last ) >= $this->crondoc_period;
			}

		return true;
		}

	/** Execute the method
	 * @param array $args
	 * 	Optional method arguments
	 * @return mixed
	 */
	function execute(array $args = array()){
		return $this->method->invokeArgs($this->object, $args);
		}

	/** DocComment(period): Run schedule
	 * @var int|null seconds
	 */
	public $crondoc_period = null;

	/** DocComment(if): evalled condition
	 * @var bool|null
	 */
	public $crondoc_if = null;

	/** DocComment(weight): execution order. The lower - the earlier
	 * @var int
	 */
	public $crondoc_weight = 0;

	protected $TIME_UNITS = array(
		's' => 1,
		'm' => 60,
		'h' => 3600,
		'd' => 86400,
		);

	/** Parse DocComment(@cron period)
	 * The cronjob gets executed every <period> seconds
	 * Values:
	 * 	\d+[smhd]	The number of minutes, hours, days.
	 */
	protected function _docparse_period($value){
		$n = (float)$value;
		$unit = trim(substr($value, strlen($n)));
		$mult = $this->TIME_UNITS[ $unit ];

		if (!is_null($mult)) # found
			$this->crondoc_period = (int)($n * $mult);
		}

	/** Parse DocComment(@cron if)
	 * This cronjob gets executed only when the PHP expression returns true
	 * Values:
	 * 	<php-code>
	 */
	protected function _docparse_if($value){
		$this->crondoc_if = eval($value.';');
		}

	/** Parse DocComment (@cron weight)
	 * Lower weights are run earlier
	 * Values:
	 * 	<int>
	 */
	protected function _docparse_weight($value){
		$this->crondoc_weight = (int)$value;
		}
	}


### 前言

该文章将通过一个小demo将讲述Redis中的string类型命令。demo将以springboot为后台框架快速开发，iview前端框架进行简单的页面设计，为了方便就不使用DB存储数据了，直接采用Redis作为存储。

文中不会讲述springboot用法及项目搭建部分。直接根据功能方面进行讲述，穿插string命令操作说明。

如果需要详细了解该项目的其他部分，请点击下方项目Github地址

项目Github地址：[https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-string](https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-string)

### 案例

demo功能是记录日志，整个demo的大致页面如下

![](https://github.com/rainbowda/learnWay/blob/master/learnRedis/img/case-string/%E5%A4%A7%E8%87%B4%E9%A1%B5%E9%9D%A2.png?raw=true)

#### 准备工作

首先定义一个key的前缀，已经存储自增id的key

```java
private static final String MY_LOG_REDIS_KEY_PREFIX = "myLog:";
private static final String MY_LOG_REDIS_ID_KEY = "myLogID";
```

日志相关的key将会以myLog:1、myLog:2、myLog:3的形式存储

redis操作对象

```java
private RedisTemplate redisTemplate;
//string 命令操作对象
private ValueOperations valueOperations;
```

#### 新增

先来看看gif图吧

![](https://github.com/rainbowda/learnWay/blob/master/learnRedis/img/case-string/%E6%96%B0%E5%A2%9E.gif?raw=true)

来看看后台的方法

```java
@RequestMapping(value = "/addMyLog",method = RequestMethod.POST)
public boolean addMyLog(@RequestBody JSONObject myLog){
    //获取自增id
    Long myLogId = valueOperations.increment(MY_LOG_REDIS_ID_KEY, 1);

    String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

    myLog.put("id",myLogId);
    myLog.put("createDate", date);
    myLog.put("updateDate", date);
    //将数据写到redis中
    valueOperations.set(MY_LOG_REDIS_KEY_PREFIX+myLogId, myLog.toString());

    return true;
}
```
从上面代码可以看出有两个操作redis的地方

> valueOperations.increment(MY_LOG_REDIS_ID_KEY, 1);
>
> valueOperations.set(MY_LOG_REDIS_KEY_PREFIX+myLogId, myLog.toString());

##### 命令介绍

valueOperations.increment其实就相当于Redis中的INCR、INCRBY、INCRBYFLOAT、DECR、DECRBY

###### INCR
> INCR key

对存储在指定key的数值执行原子的加1操作。没有对应的key则设置为0，再相加
###### INCRBY
> INCRBY key increment

其实和INCR类似，不同的是这个命令可以指定具体加多少
###### INCRBYFLOAT
> INCRBYFLOAT key increment

也是类似的，不同的是加的数值是浮点数
```
incrbyfloat incrByFloatKey 5.11
incrbyfloat incrByFloatKey 5.22
```
执行结果如下

![](https://github.com/rainbowda/learnWay/blob/master/learnRedis/img/string/incrbyfloat%E5%91%BD%E4%BB%A4%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C.png?raw=true)

下面是java代码
```java
@Test
public void incrByFloat() {
    System.out.println(jedis.incrByFloat("incrByFloatKey", 5.11));

    System.out.println(redisTemplate.opsForValue().increment("incrByFloatKey", 5.22));
}
```
与INCR相反的命令有DECR和DECRBY，这里就不做介绍了。

------

valueOperations.set就是对应Redis的SET命令了，相关联的还有SETEX、SETNX和PSETEX。需要注意的是set在Redis版本2.6.12 提供了`EX` 、`PX`  、`NX`  、`XX`参数用于取代SETEX、SETNX和PSETEX，后续版本可能会移除SETEX、SETNX和PSETEX命令。下面是官网的原话

> Since the [SET](https://redis.io/commands/set) command options can replace [SETNX](https://redis.io/commands/setnx), [SETEX](https://redis.io/commands/setex), [PSETEX](https://redis.io/commands/psetex), it is possible that in future versions of Redis these three commands will be deprecated and finally removed.

###### SET
> SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

设置键key对应value
参数选项
> EX seconds – 设置键key的过期时间，单位时秒
PX milliseconds – 设置键key的过期时间，单位时毫秒
NX – 只有键key不存在的时候才会设置key的值
XX – 只有键key存在的时候才会设置key的值

###### SETRANGE
> SETRANGE key offset value

替换从指定长度开始的字符
```
set setRangeKey "Hello World"
setrange setRangeKey 6 "Redis"
get setRangeKey
```
执行结果如下

![](https://github.com/rainbowda/learnWay/blob/master/learnRedis/img/string/setrange%E5%91%BD%E4%BB%A4%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C.png?raw=true)

下面是java代码
```java
@Test
public void setRange() {
    jedis.set("setRangeKey", "Hello World");

    jedis.setrange("setRangeKey", 6 , "Redis");
    System.out.println(jedis.get("setRangeKey"));

    //spring
    redisTemplate.opsForValue().set("setRangeKey", "learyRedis", 6);
    System.out.println(redisTemplate.opsForValue().get("setRangeKey"));
}
```
###### MSET
> MSET key value [key value ...]

同时设置多个key、value
######　MSETNX
> MSETNX key value [key value ...]

同时设置多个key、value，key存在则忽略

#### 查询

接着写个查询方法，将新增的内容查询出来

```java
@RequestMapping(value = "/getMyLog",method = RequestMethod.GET)
public List getMyLog(){
    //获取mylog的keys
    Set myLogKeys = redisTemplate.keys("myLog:*");
    return  valueOperations.multiGet(myLogKeys);
}
```

方法中的两行都涉及到了Redis操作，先是通过keys命令获取`myLog:*`相关的key集合，然后通过multiGet方法（也就是mget命令）获取记录。

##### 命令介绍

###### KEYS
> KEYS pattern

查找所有符合给定模式pattern（正则表达式）的 key
###### GET
> GET key

获取key对应的value
```
set getKey getValue
get getKey
```
执行结果如下

![](https://github.com/rainbowda/learnWay/blob/master/learnRedis/img/string/set%E5%92%8Cget%E5%91%BD%E4%BB%A4%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C.png?raw=true)

###### GETRANGE
> GETRANGE key start end

获取start到end之间的字符
```
set getRangeKey "Hello learyRedis"
getrange getRangeKey 6 -1
getrange getRangeKey 0 -12
```
执行结果如下

![](https://github.com/rainbowda/learnWay/blob/master/learnRedis/img/string/getrange%E5%91%BD%E4%BB%A4%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C.png?raw=true)

###### GETSET
> GETSET key value

设置key对应的新value且返回原来key对应的value
```
getset getSetKey newValue
set getSetKey value
getset getSetKey newValue
get getSetKey
```
执行结果如下

![](https://github.com/rainbowda/learnWay/blob/master/learnRedis/img/string/getset%E5%91%BD%E4%BB%A4%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C.png?raw=true)

###### MGET
> MGET key [key ...]

返回所有指定的key的value
```
mset mGetKey1 mGetValue1 mGetKey2 mGetValue2 mGetKey3 mGetValue3
mget mGetKey1 mGetKey2 mGetKey3 mGetKey4
```
执行结果如下

![](https://github.com/rainbowda/learnWay/blob/master/learnRedis/img/string/mset%E5%92%8Cmget%E5%91%BD%E4%BB%A4%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C.png?raw=true)

#### 更新

![](https://github.com/rainbowda/learnWay/blob/master/learnRedis/img/case-string/%E6%9B%B4%E6%96%B0.gif?raw=true)

来看看代码

```java
@RequestMapping(value = "/updateMyLog",method = RequestMethod.POST)
public boolean updateMyLog(@RequestBody JSONObject myLog){
    String myLogId = myLog.getString("id");
    myLog.put("updateDate", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

    valueOperations.set(MY_LOG_REDIS_KEY_PREFIX+myLogId, myLog.toString());
    return true;
}
```

这里的set在新增方法里面讲述过,那么来看看APPEND、STRLEN命令吧

#####  命令介绍

###### APPEND
> APPEND key value

在value的尾部追加新值

redis客户端执行的命令如下
```
append appendKey append
append appendKey Value
get appendKey
```
执行结果如下

![](https://github.com/rainbowda/learnWay/blob/master/learnRedis/img/string/append%E5%91%BD%E4%BB%A4%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%89%A7%E8%A1%8C%E7%BB%93%E6%9E%9C.png?raw=true)

###### STRLEN
> STRLEN key

返回value的长度

#### 删除

![](https://github.com/rainbowda/learnWay/blob/master/learnRedis/img/case-string/%E5%88%A0%E9%99%A4.gif?raw=true)

代码如下

```java
@RequestMapping(value = "/delMyLog/{id}", method = RequestMethod.DELETE)
public boolean delMyLog(@PathVariable  String id){
    return redisTemplate.delete(MY_LOG_REDIS_KEY_PREFIX + id);
}
```

可以看到代码中只用了delete方法，对应着Redis的DEL命令（属于基本命令）

##### 命令介绍

###### DEL
> DEL key [key ...]
删除key

### BIT相关命令

bit命令有SETBIT、GETBIT、BITCOUNT、BITFIELD、BITOP、BITPOS这些。
命令这里就不做介绍了，直接讲述bit相关的案例。
> Pattern: real time metrics using bitmaps
> BITOP is a good complement to the pattern documented in the BITCOUNT command documentation. Different bitmaps can be combined in order to obtain a target bitmap where the population counting operation is performed.
>
> See the article called "Fast easy realtime metrics using Redis bitmaps" for a interesting use cases.

案例地址[Fast easy realtime metrics using Redis bitmaps](http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps)
网上译文也有许多，有需要的百度或者google即可

这里大概讲述下使用位图法统计日登入用户数、周连续登入用户数和月连续登入用户数

> 位图法就是bitmap的缩写，所谓bitmap，就是用每一位来存放某种状态，适用于大规模数据，但数据状态又不是很多的情况。通常是用来判断某个数据存不存在的。 ------来自百度百科

就好像java中int有4个字节，也就是32位。当32位全为1时，也就是int的最大值。

位只能被设置位0或者1，也就是二进制。

java中可以用BitSet来操作位的相关操作

##### 场景

有一万个用户，id从1到10000，根据当前是否上线，来设置在第id位上是否为1或者0。通过每天的记录来统计用户连续上线的情况。

##### 分析

一号有id为5、3、1的上线了,二号有id为5、4、3的上线了，三号有id为3、2、1的上线了。存储的数据如下

```
序号：5 4 3 2 1 0
一号：1 0 1 0 1 0
二号：1 1 1 0 0 0
三号：0 0 1 1 1 0
```

那么我们只有将三天的数据进行与操作就可以知道，三天连续上线的有哪些了，与操作的结果如下

```
序号：5 4 3 2 1 0
结果：0 0 1 0 0 0
```

很明显是id为3的用户连续登入3天。

##### 代码

先定义一些常量

```java
//存储的key前缀
private static final String ONLINE_KEY_PREFIX = "online:";
//天数
private static final int DAY_NUM = 30;
//用户数量
private static final int PEOPLE_NUM = 10000;
```

然后模拟一个月的数据

```java

public void createData() {
    //用来保证线程执行完在进行后面的操作
    CountDownLatch countDownLatch = new CountDownLatch(DAY_NUM);

    int poolSize = Runtime.getRuntime().availableProcessors() * 2;
    ThreadPoolExecutor executor = new ThreadPoolExecutor(poolSize, poolSize, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(DAY_NUM-poolSize));
    //DAY_NUM天
    for (int i = 1; i <= DAY_NUM; i++) {
        int finalI = i;
        executor.execute(() -> {
            //假设有PEOPLE_NUM个用户
            for (int j = 1; j <= PEOPLE_NUM; j++) {
                redisTemplate.opsForValue().setBit(ONLINE_KEY_PREFIX + finalI, j, Math.random() > 0.1);
            }
            countDownLatch.countDown();
        });
    }

    //等待线程全部执行完成
    try {
        countDownLatch.await();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
```

最后是统计

```java
public void calActive(int day) {
    if (day < 0 || day > DAY_NUM){
        throw new IllegalArgumentException("传入的天数不能小于0或者大于30天!");
    }

    long calStart = System.currentTimeMillis();
    BitSet active = new BitSet();
    active.set(0, PEOPLE_NUM);
    for (int i = 1; i <= day; i++) {
        BitSet bitSet = BitSet.valueOf(jedis.get((ONLINE_KEY_PREFIX + i).getBytes()));
        active.and(bitSet);
    }
    long calEnd = System.currentTimeMillis();
    System.out.println(day + "天的上线用户" + active.cardinality() + ",花费时长:" + (calEnd - calStart));
}
```

测试方法

```java
@Test
public void daliyActive() {
    /**
     *模拟数据
     */
    createData();

    /**
     * 开始统计
     */
    //1
    calActive(1);

    //7
    calActive(7);

    //15
    calActive(15);

    //30
    calActive(30);
}
```

测试结果

```
1天的上线用户9015,花费时长:0
7天的上线用户4817,花费时长:0
15天的上线用户2115,花费时长:0
30天的上线用户431,花费时长:15
```

有需要看相关代码的请点击[GITHUB地址](https://github.com/rainbowda/learnWay/blob/master/learnRedis/command/src/main/java/com/learnRedis/string/BitmapCase.java)

### 其他

关于其他相关的命令可以查看下方地址

[string全命令](https://github.com/rainbowda/learnWay/blob/master/learnRedis/command/src/main/java/com/learnRedis/string/README.md)

[Redis基本命令](https://github.com/rainbowda/learnWay/blob/master/learnRedis/command/src/main/java/com/learnRedis/generic/README.md)

命令比较多，但是还是建议学习的人最好每个命令都去敲下，加深印象。
下面诗句送给每一个阅读的人。
> 纸上得来终觉浅，绝知此事要躬行。————出自《冬夜读书示子聿》


<!DOCTYPE html>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
    <title>Cax Composite Operation Demo</title>

        <button id="toggleBtn">Toggle Composite Operation</button>


<!--
Copyright (c) Aurora Studio. All rights reserved.

Licensed under the MIT License. See LICENSE in the project root for license information.
-->
<UserControl Unloaded="UserControl_Unloaded"
             x:Class="Aurora.Music.Controls.ListItems.HomeItem"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:ext="using:Microsoft.Toolkit.Uwp.UI.Extensions"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d" MaxWidth="150"
             d:DesignHeight="300"
             d:DesignWidth="400">

    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="../../Themes/Styles.xaml"/>
            </ResourceDictionary.MergedDictionaries>
            <ResourceDictionary.ThemeDictionaries>
                <ResourceDictionary x:Key="Light" Source="../../Themes/Light.xaml"/>
                <ResourceDictionary x:Key="Dark" Source="../../Themes/Dark.xaml"/>
            </ResourceDictionary.ThemeDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>

    <Grid PointerEntered="Grid_PointerEntered"
          PointerExited="Grid_PointerExited"
          PointerPressed="Grid_PointerPressed"
          PointerReleased="Grid_PointerReleased"
          PointerCanceled="Grid_PointerExited"
          PointerCaptureLost="Grid_PointerExited"
          x:Name="Root" Height="210"
          Background="Transparent">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Normal">
                    <VisualState.Setters>
                        <Setter Target="Root.Background" Value="{x:Null}"/>
                    </VisualState.Setters>
                    <Storyboard>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="PointerOver">
                    <VisualState.Setters>
                        <Setter Target="Root.Background" Value="{ThemeResource ButtonPointerOverBackgroundThemeBrush}"/>
                        <Setter Target="PlaceholderBG.Background" Value="{ThemeResource SystemControlAccentAcrylicWindowAccentLowBrush}"/>
                    </VisualState.Setters>
                    <Storyboard>
                    </Storyboard>
                </VisualState>
                <VisualState x:Name="Pressed">
                    <VisualState.Setters>
                        <Setter Target="Root.Background" Value="{ThemeResource ButtonPointerOverBackgroundThemeBrush}"/>
                        <Setter Target="PlaceholderBG.Background" Value="{ThemeResource SystemControlBackgroundBaseMediumBrush}"/>
                        <Setter Target="PlaceholderSymbol.Foreground" Value="{ThemeResource SystemControlForegroundAltMediumHighBrush}"/>
                        <Setter Target="PlaceholderText.Foreground" Value="{ThemeResource SystemControlForegroundAltMediumHighBrush}"/>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
            <VisualStateGroup x:Name="Adaptive">
                <VisualState x:Name="Wide">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="1008"/>
                    </VisualState.StateTriggers>
                </VisualState>
                <VisualState x:Name="Medium">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="641"/>
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="Artwork.Height" Value="144"/>
                        <Setter Target="Title.MaxHeight" Value="144"/>
                        <Setter Target="Title.MaxWidth" Value="144"/>
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Narrow">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0"/>
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="Artwork.Height" Value="128"/>
                        <Setter Target="Title.MaxHeight" Value="128"/>
                        <Setter Target="Title.MaxWidth" Value="128"/>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        <Grid.RowDefinitions>
            <RowDefinition Height="150"/>
            <RowDefinition Height="auto"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Image x:Name="Artwork" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="150" Stretch="Uniform"
               Source="{x:Bind DataArtwork,TargetNullValue=ms-appx:///Assets/Images/placeholder_b.png,Mode=OneWay}"
               ToolTipService.ToolTip="{x:Bind Data.Title,Mode=OneWay}" Width="150"
               Visibility="{x:Bind Placeholder(Data.InnerType), Mode=OneWay}"
               ext:FrameworkElementExtensions.EnableActualSizeBinding="True"/>
        <Border Visibility="{x:Bind NightModeEnabled}"
                HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                Height="150" Background="{ThemeResource ImageDarkOverlay}"/>
        <Grid HorizontalAlignment="Stretch"
              VerticalAlignment="Stretch"
              Grid.Row="1"
              Width="{Binding ElementName=Artwork, Path=(ext:FrameworkElementExtensions.ActualWidth)}">
            <TextBlock Style="{ThemeResource BaseTextBlockStyle}"
                       ToolTipService.ToolTip="{x:Bind Data.Title,Mode=OneWay}"
                       HorizontalAlignment="Stretch" VerticalAlignment="Top"
                       MaxLines="2" TextWrapping="WrapWholeWords"
                       TextTrimming="CharacterEllipsis"
                       Visibility="{x:Bind Placeholder(Data.InnerType), Mode=OneWay}"
                       Margin="0">
                <Run Text="{x:Bind AlbumIndicate(Data.HasMultipleItem),Mode=OneWay}"/><Run Text="{x:Bind Data.Title,Mode=OneWay}"/>
            </TextBlock>
            <FontIcon FontSize="15" Glyph="" Margin="0,3,0,0"
                      HorizontalAlignment="Left" VerticalAlignment="Top"
                      Visibility="{x:Bind Data.HasMultipleItem, Mode=OneWay}"/>
        </Grid>
        <TextBlock FontSize="13"
                   Style="{ThemeResource BodyTextBlockStyle}"
                   ToolTipService.ToolTip="{x:Bind Data.AddtionalAndDescription(Data.Addtional, Data.Description), Mode=OneWay}"
                   HorizontalAlignment="Left" VerticalAlignment="Center"
                   MaxLines="1" TextWrapping="NoWrap"
                   Grid.Row="2"
                   Width="{Binding ElementName=Artwork, Path=(ext:FrameworkElementExtensions.ActualWidth)}"
                   TextTrimming="CharacterEllipsis"
                   Visibility="{x:Bind Placeholder(Data.InnerType), Mode=OneWay}"
                   Foreground="{ThemeResource SystemControlBackgroundBaseMediumLowRevealBorderBrush}">
            <Run Text="{x:Bind Data.Addtional,Mode=OneWay}"/>
            <Run Text="·"/>
            <Run Text="{x:Bind Data.Description,Mode=OneWay}"/>
        </TextBlock>
        <Grid Grid.RowSpan="3" Height="210" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
              Background="{ThemeResource SystemControlAccentDark2AcrylicWindowAccentDark2MediumHighBrush}"
              x:Name="PlaceholderBG"
              Visibility="{x:Bind NotPlaceholder(Data.InnerType), Mode=OneWay}">
            <StackPanel Orientation="Vertical"
                        HorizontalAlignment="Center"
                        VerticalAlignment="Center">
                <FontIcon HorizontalAlignment="Center"
                          VerticalAlignment="Center"
                          FontSize="24"
                          x:Name="PlaceholderSymbol"
                          Foreground="{ThemeResource SystemControlForegroundAltHighBrush}"
                          FontFamily="Segoe MDL2 Assets"
                          Glyph="" />
                <TextBlock x:Uid="MoreButton"
                           Text="More"
                           x:Name="PlaceholderText"
                           Foreground="{ThemeResource SystemControlForegroundAltHighBrush}"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"
                           Style="{ThemeResource SubtitleTextBlockStyle}"/>
            </StackPanel>
        </Grid>
    </Grid>
</UserControl>


# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
#
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.

from aliyunsdkcore.request import RpcRequest
class DelSubAccountPermissionRequest(RpcRequest):

	def __init__(self):
		RpcRequest.__init__(self, 'cloudwf', '2017-03-28', 'DelSubAccountPermission','cloudwf')

	def get_Id(self):
		return self.get_query_params().get('Id')

	def set_Id(self,Id):
		self.add_query_param('Id',Id)


// tps (01/14/2010) : Switching from rose.h to sage3.
#include "sage3basic.h"
#include "pre.h"

#include <stdint.h>
#include <queue>
#include <fstream>
#include "replaceExpressionWithStatement.h"
#include "inlinerSupport.h"
#include "expressionTreeEqual.h"
#include "preControlFlowGraph.h"

#include "constantFolding.h"

// DQ (12/31/2005): This is OK if not declared in a header file
using namespace std;

// DQ (8/1/2005): test use of new static function to create
// Sg_File_Info object that are marked as transformations
#undef SgNULL_FILE
#define SgNULL_FILE Sg_File_Info::generateDefaultFileInfoForTransformationNode()

// Get the function definition containing a given node
SgFunctionDefinition* PRE::getFunctionDefinition(SgNode* n) {
  while (true) {
    if (isSgFunctionDefinition(n))
      return isSgFunctionDefinition(n);
    n = n->get_parent();
    assert (n);
  }
  return 0; // Should not happen
}

// Are any variables in syms modified anywhere within n, or is n a declaration
// of one of them?
// FIXME: move to inliner
bool anyOfListPotentiallyModifiedIn(const vector<SgVariableSymbol*>& syms,
                                    SgNode* n) {
  bool modified = false;
  for (vector<SgVariableSymbol*>::const_iterator j = syms.begin();
      j != syms.end(); ++j) {
    SgVarRefExp* vr = new SgVarRefExp(SgNULL_FILE, *j);
    vr->set_endOfConstruct(SgNULL_FILE);
    if (isPotentiallyModified(vr, n)) {
      modified = true;
    }
    delete vr;
    if (modified) break;
    if (isSgVariableDeclaration(n)) {
      SgVariableDeclaration* decl = isSgVariableDeclaration(n);
      for (SgInitializedNamePtrList::const_iterator i =
             decl->get_variables().begin();
           i != decl->get_variables().end(); ++i) {
        if (*i == (*j)->get_declaration()) {
          modified = true;
          break;
        }
      }
    }
    if (modified) break;
  }
  return modified;
}

class ExpressionComputedInVisitor: public AstSimpleProcessing {
  int& count;
  SgExpression* expr;

  public:
  ExpressionComputedInVisitor(int& count, SgExpression* expr):
    count(count), expr(expr) {}

  virtual void visit(SgNode* n) {
    if (isSgExpression(n) && expressionTreeEqual(isSgExpression(n), expr))
      ++count;
  }
};

// Count how many computations of expr are within root.  Same caveats as
// expressionComputedIn.
int countComputationsOfExpressionIn(SgExpression* expr, SgNode* root) {
  int count = 0;
  ExpressionComputedInVisitor(count, expr).traverse(root, postorder);
  return count;
}

// Is the expression expr computed within root?  This test is simple, and does
// not account for changes to the variables in expr.
bool expressionComputedIn(SgExpression* expr, SgNode* root) {
  return countComputationsOfExpressionIn(expr, root) != 0;
}

class ExpressionTreeEqualTo {
  SgExpression* target;

  public:
  ExpressionTreeEqualTo(SgExpression* target): target(target) {}

  bool operator()(SgExpression* x) const {
    return expressionTreeEqual(x, target);
  }
};

#define FIXPOINT_BEGIN(cfg) \
{ \
  VertexIter i = cfg.graph.vertices().begin(), \
             end = cfg.graph.vertices().end(); \
  queue<Vertex> Q; \
  for (; i != end; ++i) { \
    Q.push(*i); \
  } \
  while (!Q.empty()) { \
    Vertex v = Q.front(); \
    Q.pop(); \
    bool changed = false;

#define FIXPOINT_END(cfg, get_outflow, outflow_iter) \
    if (changed) { \
      outflow_iter j = cfg.graph.get_outflow(v).begin(),\
                   jend = cfg.graph.get_outflow(v).end(); \
      for (; j != jend; ++j) { \
        if (cfg.graph.source(*j) != v) \
          Q.push(cfg.graph.source(*j)); \
        else \
          Q.push(cfg.graph.target(*j));  \
          /* Includes case when j->source = j->target = v */ \
      } \
    } \
  } \
}

#define FIXPOINT_OUTPUT_BEGIN(pmap, cfg) \
  bool old_##pmap = pmap[v]

#define FIXPOINT_OUTPUT_END(pmap, leq, cfg) \
  assert (pmap[v] leq old_##pmap); \
  changed |= (pmap[v] != old_##pmap)

// Accumulate a certain property of all neighbors of a graph node.  Used
// internally by PRE.
template <class PropMap, class BinOp>
typename BinOp::result_type
accumulate_neighbors(
    const PRE::myControlFlowGraph& cfg,
    int v,
    const PropMap& prop,
    const vector<int>& edges,
    int (PRE::simpleGraph::*edge_to_neighbor)(int) const,
    const BinOp& combine,
    typename BinOp::result_type init,
    typename BinOp::result_type ifnone) {
  std::vector<int>::const_iterator i = edges.begin(), end = edges.end();
  if (i == end)
    return ifnone;
  else {
    for (; i != end; ++i)
      init = combine(init, prop[(cfg.graph.*edge_to_neighbor)(*i)]);
    return init;
  }
}

class ReplaceExpressionWithVarrefVisitor: public AstSimpleProcessing {
  SgExpression* expr;
  SgVarRefExp* vr;

  public:
  ReplaceExpressionWithVarrefVisitor(SgExpression* expr,
                                     SgVarRefExp* vr):
    expr(expr), vr(vr) {}

  virtual void visit(SgNode* n) {
    SgExpression* n2 = isSgExpression(n);
    if (n2 && expressionTreeEqual(n2, expr) && isSgExpression(n->get_parent())) {
      isSgExpression(n->get_parent())->replace_expression(n2, vr);
    }
  }
};

// Do partial redundancy elimination, looking for copies of one expression expr
// within the basic block root.  A control flow graph for root must be provided
// in cfg, with a map from nodes to their statements in node_statements, a map
// from edges to their CFG edge types in edge_type, and a map from edges to
// their insertion points in edge_insertion_point.  The algorithm used is that
// of Paleri, Srikant, and Shankar ("Partial redundancy elimination: a simple,
// pragmatic, and provably correct algorithm", Science of Computer Programming
// 48 (2003) 1--20).
void PRE::partialRedundancyEliminationOne( SgExpression* expr, SgBasicBlock* root, const myControlFlowGraph& cfg)
   {
  // SgBasicBlock* myFunctionBody = getFunctionDefinition(expr)->get_body();

  // DQ (3/16/2006): Added assertions
     ROSE_ASSERT(expr != NULL);
     ROSE_ASSERT(root != NULL);

     vector<SgVariableSymbol*> symbols_in_expression = SageInterface::getSymbolsUsedInExpression(expr);

     if (anyOfListPotentiallyModifiedIn(symbols_in_expression, expr))
        {
       // This expression updates its own arguments, and so is not idempotent
       // Return immediately
          return;
        }

  // Simple or not user-definable expressions
     if (isSgVarRefExp(expr)) return;
     if (isSgValueExp(expr)) return;
     if (isSgFunctionRefExp(expr)) return;
     if (isSgExprListExp(expr)) return;
     if (isSgInitializer(expr)) return;
#if 0
     if ( (isSgAddOp(expr) || isSgSubtractOp(expr)) &&
          (isSgVarRefExp(isSgBinaryOp(expr)->get_lhs_operand()) ||
           isSgValueExp(isSgBinaryOp(expr)->get_lhs_operand())) &&
          (isSgVarRefExp(isSgBinaryOp(expr)->get_rhs_operand()) ||
           isSgValueExp(isSgBinaryOp(expr)->get_rhs_operand())))
          return;
#endif

  // Expressions which do not keep a consistent value each time they are used
     if (!expressionTreeEqual(expr, expr))
          return;

  // cerr << "Trying to do PRE using expression " << expr->unparseToString() << " whose type is " << expr->sage_class_name() << endl;

     VertexIter i   = cfg.graph.vertices().begin(),
                     end = cfg.graph.vertices().end();

  // cerr << "CFG has " << distance(i, end) << " nodes" << endl;

     bool needToMakeCachevar = false;
     set<SgNode*> replacements;
     vector<pair<SgNode*, bool /* before */> > insertions;

     vector<bool> transp(cfg.graph.vertices().size()),
                  comp(cfg.graph.vertices().size()),
                  antloc(cfg.graph.vertices().size());

     vector<SgNode*> first_computation(cfg.graph.vertices().size()),
                     last_computation(cfg.graph.vertices().size());

  // Set values of local node properties
     for (i = cfg.graph.vertices().begin(); i != end; ++i)
        {
          const vector<SgNode*>& stmts = cfg.node_statements[*i];

       // Precompute test values for each statement
          vector<bool> argumentsModifiedInStatement(stmts.size());
          vector<int> expressionComputedInStatement(stmts.size());
          for (unsigned int j = 0; j < stmts.size(); ++j)
             {
               if (anyOfListPotentiallyModifiedIn(symbols_in_expression, stmts[j]))
                         argumentsModifiedInStatement[j] = true;
               expressionComputedInStatement[j] = countComputationsOfExpressionIn(expr, stmts[j]);
             }

       // Compute transp
          transp[*i] = true;
          for (unsigned int j = 0; j < stmts.size(); ++j)
               if (argumentsModifiedInStatement[j])
                    transp[*i] = false;

       // Compute comp and do local redundancy elimination
          comp[*i] = false;
          SgNode* firstComputationInChain = 0;
          bool needToInsertComputation = false;
          bool computationInsertedOrUsed = false;
       // cout << "In node " << *i << endl;
          for (unsigned int j = 0; j < stmts.size(); ++j)
             {
            // cout << "In stmt " << j << ", expressionComputedInStatement = " << expressionComputedInStatement[j]
            //      << ", argumentsModifiedInStatement = " << argumentsModifiedInStatement[j] << endl;
               if (expressionComputedInStatement[j] && !argumentsModifiedInStatement[j] && comp[*i] /* from last iter */)
                  {
                 // Do local redundancy elimination
                    if (firstComputationInChain && needToInsertComputation)
                       {
                         insertions.push_back(make_pair(firstComputationInChain, true));
                         replacements.insert(firstComputationInChain);
                         needToInsertComputation = false;
                       }
                    replacements.insert(stmts[j]);
                    computationInsertedOrUsed = true;
                    needToMakeCachevar = true;
                  }
               if (expressionComputedInStatement[j])
                  {
                    comp[*i] = true;
                    if (!firstComputationInChain)
                       {
                         firstComputationInChain = stmts[j];
                         needToInsertComputation = true;
                         if (expressionComputedInStatement[j] >= 2)
                            {
                              insertions.push_back(make_pair(stmts[j], true));
                              needToMakeCachevar = true;
                              needToInsertComputation = false;
                              computationInsertedOrUsed = true;
                              replacements.insert(stmts[j]);
                            }
                       }
                    last_computation[*i] = stmts[j];
                  }
               if (argumentsModifiedInStatement[j])
                  {
                    comp[*i] = false; // Must come after expressionComputedInStatement check
                    firstComputationInChain = 0;
                    needToInsertComputation = false;
                  }
             }

          ASSERT_always_require (!computationInsertedOrUsed || needToMakeCachevar);

       // Compute antloc
          antloc[*i] = false;
          for (unsigned int j = 0; j < stmts.size(); ++j)
             {
               if (expressionComputedInStatement[j] && !argumentsModifiedInStatement[j])
                  {
                    antloc[*i] = true;
                    first_computation[*i] = stmts[j];
                    break;
                  }
               if (argumentsModifiedInStatement[j])
                  {
                    antloc[*i] = false;
                    break;
                  }
             }
        }

// #define PRINT_PROPERTY(p) // for (i = cfg.graph.vertices().begin(); i != end; ++i) if (p[*i]) cerr << #p ": " << *i << endl;
#define PRINT_PROPERTY(p) // for (i = cfg.graph.vertices().begin(); i != end; ++i) if (p[*i]) cerr << #p ": " << *i << endl;
     PRINT_PROPERTY(transp);
     PRINT_PROPERTY(comp);
     PRINT_PROPERTY(antloc);

     int (simpleGraph::*source_ptr)(int) const = &simpleGraph::source;
     int (simpleGraph::*target_ptr)(int) const = &simpleGraph::target;

     vector<bool> avin(cfg.graph.vertices().size(), true);
     vector<bool> avout(cfg.graph.vertices().size(), true);
     FIXPOINT_BEGIN(cfg)
     FIXPOINT_OUTPUT_BEGIN(avin, cfg);
     FIXPOINT_OUTPUT_BEGIN(avout, cfg);
     avin[v] = accumulate_neighbors(cfg, v, avout,cfg.graph.in_edges(v),&simpleGraph::source, logical_and<bool>(), true, false);
     avout[v] = comp[v] || (avin[v] && transp[v]);
     FIXPOINT_OUTPUT_END(avout, <=, cfg);
     FIXPOINT_OUTPUT_END(avin, <=, cfg);
     FIXPOINT_END(cfg, out_edges, OutEdgeIter)

     PRINT_PROPERTY(avin);
     PRINT_PROPERTY(avout);

     vector<bool> antin(cfg.graph.vertices().size(), true);
     vector<bool> antout(cfg.graph.vertices().size(), true);
     FIXPOINT_BEGIN(cfg)
     FIXPOINT_OUTPUT_BEGIN(antin, cfg);
     FIXPOINT_OUTPUT_BEGIN(antout, cfg);
     antout[v] = accumulate_neighbors(cfg, v, antin, cfg.graph.out_edges(v), target_ptr, logical_and<bool>(), true, false);
     antin[v] = antloc[v] || (antout[v] && transp[v]);
     FIXPOINT_OUTPUT_END(antout, <=, cfg);
     FIXPOINT_OUTPUT_END(antin, <=, cfg);
     FIXPOINT_END(cfg, in_edges, InEdgeIter)

     PRINT_PROPERTY(antin);
     PRINT_PROPERTY(antout);

     vector<bool> safein(cfg.graph.vertices().size()), safeout(cfg.graph.vertices().size());

     for (i = cfg.graph.vertices().begin(); i != end; ++i)
        {
          safein[*i] = avin[*i] || antin[*i];
          safeout[*i] = avout[*i] || antout[*i];
        }

     PRINT_PROPERTY(safein);
     PRINT_PROPERTY(safeout);

     vector<bool> spavin(cfg.graph.vertices().size(), false);
     vector<bool> spavout(cfg.graph.vertices().size(), false);
     FIXPOINT_BEGIN(cfg)
     FIXPOINT_OUTPUT_BEGIN(spavin, cfg);
     FIXPOINT_OUTPUT_BEGIN(spavout, cfg);
     spavin[v] = safein[v] && accumulate_neighbors(cfg, v, spavout, cfg.graph.in_edges(v), source_ptr, logical_or<bool>(), false, false);
     spavout[v] = safeout[v] && (comp[v] || (spavin[v] && transp[v]));
     FIXPOINT_OUTPUT_END(spavout, >=, cfg);
     FIXPOINT_OUTPUT_END(spavin, >=, cfg);
     FIXPOINT_END(cfg, out_edges, OutEdgeIter)

     PRINT_PROPERTY(spavin);
     PRINT_PROPERTY(spavout);

     vector<bool> spantin(cfg.graph.vertices().size(), false);
     vector<bool> spantout(cfg.graph.vertices().size(), false);
     FIXPOINT_BEGIN(cfg)
     FIXPOINT_OUTPUT_BEGIN(spantin, cfg);
     FIXPOINT_OUTPUT_BEGIN(spantout, cfg);
     spantout[v] = safeout[v] && accumulate_neighbors(cfg, v, spantin, cfg.graph.out_edges(v), target_ptr, logical_or<bool>(), false, false);
     spantin[v] = safein[v] && (antloc[v] || (spantout[v] && transp[v]));
     FIXPOINT_OUTPUT_END(spantout, >=, cfg);
     FIXPOINT_OUTPUT_END(spantin, >=, cfg);
     FIXPOINT_END(cfg, in_edges, InEdgeIter)

     PRINT_PROPERTY(spantin);
     PRINT_PROPERTY(spantout);
#undef PRINT_PROPERTY

     vector<bool> node_insert(cfg.graph.vertices().size());
     vector<bool> replacef(cfg.graph.vertices().size());
     vector<bool> replacel(cfg.graph.vertices().size());

  // printf ("Intermediate test 1: needToMakeCachevar = %s \n",needToMakeCachevar ? "true" : "false");

     for (i = cfg.graph.vertices().begin(); i != end; ++i)
        {
          node_insert[*i] = comp[*i] && spantout[*i] && (!transp[*i] || !spavin[*i]);
          replacef[*i] = antloc[*i] && (spavin[*i] || (transp[*i] && spantout[*i]));
          replacel[*i] = comp[*i] && (spantout[*i] || (transp[*i] && spavin[*i]));

          if (node_insert[*i])
             {
               needToMakeCachevar = true;
               insertions.push_back(make_pair(last_computation[*i], true));
            // cerr << "Insert computation of " << expr->unparseToString() << " just before last computation in " << *i << endl;
             }

          if (replacef[*i])
             {
               needToMakeCachevar = true;
               replacements.insert(first_computation[*i]);
            // cerr << "Replace first computation of " << *i << endl;
             }

          if (replacel[*i])
             {
               needToMakeCachevar = true;
               replacements.insert(last_computation[*i]);
            // cerr << "Replace last computation of " << *i << endl;
             }
        }

     vector<bool> edge_insert(cfg.graph.edges().size());

  // printf ("Intermediate test 2: needToMakeCachevar = %s \n",needToMakeCachevar ? "true" : "false");

     EdgeIter j = cfg.graph.edges().begin(), jend = cfg.graph.edges().end();
     for (; j != jend; ++j)
        {
          edge_insert[*j] = !spavout[cfg.graph.source(*j)] && spavin[cfg.graph.target(*j)] && spantin[cfg.graph.target(*j)];

       // printf ("edge_insert[*j] = %s \n",edge_insert[*j] ? "true" : "false");
          if (edge_insert[*j])
             {
               needToMakeCachevar = true;
            // cerr << "Insert computation of " << expr->unparseToString() << " on edge from "
            //      << cfg.graph.source(*j) << " to " << cfg.graph.target(*j) << endl;
             }
        }

  // printf ("Before final test: needToMakeCachevar = %s \n",needToMakeCachevar ? "true" : "false");

  // Add cache variable if necessary
     SgVarRefExp* cachevar = 0;
     if (needToMakeCachevar)
        {
          SgName cachevarname = "cachevar__";
          cachevarname << ++SageInterface::gensym_counter;

       // printf ("Building variable name = %s \n",cachevarname.str());

          SgType* type = expr->get_type();
          if (isSgArrayType(type))
               type = new SgPointerType(isSgArrayType(type)->get_base_type());
          assert (SageInterface::isDefaultConstructible(type));
       // FIXME: assert (isAssignable(type));
          SgVariableDeclaration* decl = new SgVariableDeclaration(SgNULL_FILE, cachevarname, type, NULL);
          decl->set_definingDeclaration(decl);
          SgInitializedName* initname = decl->get_variables().back();

       // DQ (10/5/2007): Added an assertion.
          ROSE_ASSERT(initname != NULL);

          decl->addToAttachedPreprocessingInfo(
               new PreprocessingInfo(PreprocessingInfo::CplusplusStyleComment,
               (string("// Partial redundancy elimination: ") + cachevarname.str() +
               " is a cache of " + expr->unparseToString()).c_str(),
               "Compiler-Generated in PRE", 0, 0, 0, PreprocessingInfo::before));
          SgVariableSymbol* cachevarsym = new SgVariableSymbol(initname);
          decl->set_parent(root);

       // DQ (10/5/2007): Added scope (suggested by Jeremiah).
          initname->set_scope(root);

          root->get_statements().insert(root->get_statements().begin(),decl);

          root->insert_symbol(cachevarname, cachevarsym);
          cachevar = new SgVarRefExp(SgNULL_FILE, cachevarsym);
          cachevar->set_endOfConstruct(SgNULL_FILE);
        }

  // Do expression computation replacements
     for (set<SgNode*>::iterator i = replacements.begin(); i != replacements.end(); ++i)
        {
          ReplaceExpressionWithVarrefVisitor(expr, cachevar).traverse(*i, postorder);
        }

  // Do edge insertions
  // int count = 0;
     bool failAtEndOfFunction = false;
     for (j = cfg.graph.edges().begin(); j != jend; ++j)
        {
       // printf ("Build the insertion list! count = %d \n",count++);
          if (edge_insert[*j])
             {
#if 0
            // DQ (3/13/2006): Compiler warns that "src" is unused, so I have commented it out!
               Vertex src = cfg.graph.source(*j), tgt = cfg.graph.target(*j);
               cerr << "Doing insertion between " << src << " and " << tgt << endl;
#endif
               pair<SgNode*, bool> insert_point = cfg.edge_insertion_point[*j];
               if (insert_point.first)
                  {
                    insertions.push_back(insert_point);
                  }
                 else
                  {
                 // DQ (3/16/2006): This is a visited when we fixup the NULL pointer to the initializer in a SgForStatment.
                    cerr << "Warning: no insertion point found" << endl; //FIXME was assert
                    printf ("Need to figure out what to do here! cfg.edge_insertion_point[*j] = %p \n",&(cfg.edge_insertion_point[*j]));

                    failAtEndOfFunction = true;
                    ROSE_ASSERT(false);
                  }
             }
        }

  // Do within-node insertions
  // printf ("At start of loop: insertions.size() = %" PRIuPTR " \n",insertions.size());
     for (vector<pair<SgNode*, bool> >::iterator i = insertions.begin(); i != insertions.end(); ++i)
        {
          SgTreeCopy tc1, tc2;
          SgVarRefExp* cachevarCopy = isSgVarRefExp(cachevar->copy(tc1));
          ROSE_ASSERT (cachevarCopy);
          cachevarCopy->set_lvalue(true);
          SgExpression* operation = new SgAssignOp(SgNULL_FILE, cachevarCopy, isSgExpression(expr->copy(tc2)));
#if 0
          printf ("Inside of loop: insertions.size() = %" PRIuPTR " \n",insertions.size());
          printf ("\n\ni->first = %p = %s = %s \n",i->first,i->first->class_name().c_str(),i->first->unparseToString().c_str());
          printf ("operation = %p = %s = %s \n",operation,operation->class_name().c_str(),operation->unparseToString().c_str());
#endif
          if (isSgExpression(i->first) && !i->second)
             {
               SgNode* ifp = i->first->get_parent();
               SgCommaOpExp* comma = new SgCommaOpExp(SgNULL_FILE, isSgExpression(i->first), operation);
               operation->set_parent(comma);
               comma->set_parent(ifp);
               i->first->set_parent(comma);
               if (isSgForStatement(ifp))
                  {
                    isSgForStatement(ifp)->set_increment(comma);
                  }
                 else
                    if (isSgBinaryOp(ifp) && isSgBinaryOp(ifp)->get_lhs_operand() == i->first)
                       {
                         isSgBinaryOp(ifp)->set_lhs_operand(comma);
                       }
                      else
                         if (isSgBinaryOp(ifp) && isSgBinaryOp(ifp)->get_rhs_operand() == i->first)
                            {
                              isSgBinaryOp(ifp)->set_rhs_operand(comma);
                            }
                           else
                              if (isSgUnaryOp(ifp) && isSgUnaryOp(ifp)->get_operand() == i->first)
                                 {
                                   isSgUnaryOp(ifp)->set_operand(comma);
                                 }
                                else
                                 {
                                   cerr << ifp->sage_class_name() << endl;
                                   assert (!"Bad parent type for inserting comma expression");
                                 }
             }
            else
             {
               SgStatement* the_computation = new SgExprStatement(SgNULL_FILE, operation);
               operation->set_parent(the_computation);
            // printf ("In pre.C: the_computation = %p = %s \n",the_computation,the_computation->class_name().c_str());

               if (isSgBasicBlock(i->first) && i->second)
                  {
                    isSgBasicBlock(i->first)->get_statements().insert(isSgBasicBlock(i->first)->get_statements().begin(),the_computation);
                    the_computation->set_parent(i->first);
                  }
                 else
                  {
#if 0
                 // DQ (3/14/2006): Bug here when SgExprStatement from SgForStatement test is used here!
                    printf ("Bug here when SgExprStatement from SgForStatement test is used: i->first = %s \n",i->first->class_name().c_str());
                    i->first->get_file_info()->display("Location i->first");
                    printf ("Bug here when SgExprStatement from SgForStatement test is used: TransformationSupport::getStatement(i->first) = %s \n",
                         TransformationSupport::getStatement(i->first)->class_name().c_str());
                    printf ("Bug here when SgExprStatement from SgForStatement test is used: the_computation = %s \n",the_computation->class_name().c_str());
                    the_computation->get_file_info()->display("Location the_computation: debug");
                    ROSE_ASSERT(i->first != NULL);
                    ROSE_ASSERT(i->first->get_parent() != NULL);
                    printf ("i->first->get_parent() = %s \n",i->first->get_parent()->class_name().c_str());
                    i->first->get_file_info()->display("Location i->first: debug");
#endif

                    SgForStatement*  forStatement = isSgForStatement(i->first->get_parent());
                    if (forStatement != NULL)
                       {
                      // Make sure that both pointers are not equal because they are both NULL!
                         ROSE_ASSERT(forStatement->get_test() != NULL);
                         SgExprStatement* possibleTest = isSgExprStatement(i->first);
                         if ( forStatement->get_test() == possibleTest )
                            {
                           // This is a special case of a SgExpressionStatement as a target in a SgForStatement
                           // printf ("Found special case of target being the test of a SgForStatement \n");
                              forStatement->set_test(the_computation);
                              the_computation->set_parent(forStatement);
                            }
                       }
                      else
                       {
                         SgForInitStatement*  forInitStatement = isSgForInitStatement(i->first->get_parent());
                         if (forInitStatement != NULL)
                            {
                           // printf ("Found the SgForInitStatement \n");
                              SgVariableDeclaration* possibleVariable = isSgVariableDeclaration(i->first);
                              SgExprStatement* possibleExpression = isSgExprStatement(i->first);
                              SgStatementPtrList & statementList = forInitStatement->get_init_stmt();
                              SgStatementPtrList::iterator i = statementList.begin();
                              bool addToForInitList = false;
                              while ( (addToForInitList == false) && (i != statementList.end()) )
                                 {
                                // if ( *i == possibleVariable )
                                   if ( *i == possibleVariable || *i == possibleExpression )
                                      {
                                     // This is a special case of a SgExpressionStatement as a target in a SgForStatement
                                     // printf ("Found special case of SgForInitStatement transformation \n");
                                        addToForInitList = true;
                                      }
                                   i++;
                                 }

                           // Only modify the STL list outside of the loop over the list to avoid interator invalidation
                              if (addToForInitList == true)
                                 {
                                // Add the the_computation statment to the list in the SgForInitStatement.
                                // Later if we abandon the SgForInitStatement then we would have to add it to
                                // the list of SgInitializedName objects in the SgVariableDeclaration OR modify
                                // the expression list to handle the extra expression (but I think this case in
                                // handled above).
                                // printf ("Adding the_computation to the list in the SgForInitStatement \n");
                                   statementList.push_back(the_computation);
                                   the_computation->set_parent(forInitStatement);
                                 }
                            }
                           else
                            {
                              myStatementInsert(TransformationSupport::getStatement(i->first),the_computation,i->second,true);
                              the_computation->set_parent(TransformationSupport::getStatement(i->first));
                            }
                       }
                  }
             }
        }

  // DQ (3/16/2006): debugging code to force failure at inspection point
     if (failAtEndOfFunction == true)
        {
          printf ("Error: internal error detected \n");
          ROSE_ASSERT(false);
        }
   }

#if 1
class FindExpressionsVisitor: public AstSimpleProcessing {
  public:
  vector<SgExpression*> exprs;

  virtual void visit(SgNode* n) {
    if (isSgExpression(n))
      exprs.push_back(isSgExpression(n));
  }
};
#else
// DQ (4/8/2006): This has been reimplemented to support apparent problems in PRE
// when expressions are used that are a part of the constant folded expression trees
// now available (and traversed) in the AST.  Because we traverse the expression trees
// of constant folded values we were collecting them into the list of expressions
// upon which to apply PRE.  This is likely nore a great idea, just because we
// make such things available in the AST and in the traversal does not imply that
// they need to be optimized.  In general the rule is that the AST represents
// the structure of the original source code, and the traversals in ROSE traverse
// that structure.  What we want to apply optimization to is in this case less
// than the full AST, and instead the semantically equivalent AST (after constant folding).
// We will soon implement constant folding which can eliminate (prune) the AST of
// all extra such information.

// Here we collect the expression that are in the AST (as before, in the previous version),
// but we exclude those in the constant folded expression trees that have been recently added
// to the AST.  We use inherited attributes to do this.

// Build an inherited attribute for the tree traversal to skip constant folded expressions
class FindExpressionsVisitorInheritedAttribute
   {
     public:
          bool isConstantFoldedValue;
          bool isPartOfFoldedExpression;

      //! Specific constructors are required
          FindExpressionsVisitorInheritedAttribute()
             : isConstantFoldedValue(false), isPartOfFoldedExpression(false) {};

       // Need to implement the copy constructor
          FindExpressionsVisitorInheritedAttribute ( const FindExpressionsVisitorInheritedAttribute & X )
             : isConstantFoldedValue(X.isConstantFoldedValue), isPartOfFoldedExpression(X.isPartOfFoldedExpression) {};
   };

// Tree traversal to find expressions in the AST
class FindExpressionsVisitor
   : public SgTopDownProcessing<FindExpressionsVisitorInheritedAttribute>
   {
     public:
       // Storage for expressions that are found (excluding those in constant folded expression trees which cause errors in PRE)
          vector<SgExpression*> exprs;

       // Functions required by the rewrite mechanism
          FindExpressionsVisitorInheritedAttribute evaluateInheritedAttribute ( SgNode* n, FindExpressionsVisitorInheritedAttribute inheritedAttribute )
             {
               if (inheritedAttribute.isConstantFoldedValue == true)
                  {
                    inheritedAttribute.isConstantFoldedValue = false;
                    inheritedAttribute.isPartOfFoldedExpression = true;
                  }
                 else
                  {
                    SgValueExp* valueExpression = isSgValueExp(n);
                 // Check for constant folded value
                    if (valueExpression != NULL && valueExpression->get_valueExpressionTree() != NULL)
                       {
                         inheritedAttribute.isConstantFoldedValue = true;
                       }
                  }

               if (inheritedAttribute.isPartOfFoldedExpression == false)
                  {
                    SgExpression* expr = isSgExpression(n);
                    if ( expr != NULL )
                         exprs.push_back(expr);
                  }

               return inheritedAttribute;
             }
   };
#endif

// Do partial redundancy for all expressions within a given function, whose
// body is given in n.
void
PRE::partialRedundancyEliminationFunction(SgFunctionDefinition* n)
{
    // FIXME: do expressions in batches using bit vectors
    typedef PRE::myControlFlowGraph ControlFlowGraph;
    typedef int Vertex;
    typedef int Edge;
    typedef vector<int>::const_iterator VertexIter;
    typedef vector<int>::const_iterator OutEdgeIter;
    typedef vector<int>::const_iterator InEdgeIter;
    typedef vector<int>::const_iterator EdgeIter;

    // DQ (4/8/2006): Call the new constant folding (not fully implemented except that it works well to eliminate the stored
    // constant expression trees in the AST which are redundant with the values on SgValueExp IR nodes.  The storage of the
    // constant expression trees from which constant folded values are generated is a new development within ROSE and required
    // to represent the complete structure of the source code (and handle details like marcos, floating literals as strings,
    // etc.).  The storage, and traversal of these redundant constant expressions is a problem for PRE, and since I could not
    // fix it easily, I implemented the constant folding in ROSE (not finished) so that we could remove the constant expression
    // trees which interfere with PRE and which are philosophically a problem for other forms of program analysis as well. So
    // calling this here is as if we have constant folding as a phase before PRE.
    ConstantFolding::constantFoldingOptimization(n);

    FindExpressionsVisitor vis;
    vis.traverse(n, postorder);
    vector<SgExpression*> exprs_done;
    for (vector<SgExpression*>::iterator i = vis.exprs.begin(); i != vis.exprs.end(); ++i) {
        if (std::find_if(exprs_done.begin(), exprs_done.end(), ExpressionTreeEqualTo(*i)) != exprs_done.end())
            continue;
        exprs_done.push_back(*i);

        myControlFlowGraph controlflow;
        makeCfg(n, controlflow);
#if 0
        { ofstream dotfile("cfgnew_preip.dot"); printCfgAsDot(dotfile, controlflow); }
#endif

        // Add insertion points to each edge
        addEdgeInsertionPoints(controlflow);
#if 0
        { ofstream dotfile("cfgnew.dot"); printCfgAsDot(dotfile, controlflow); }
#endif

        partialRedundancyEliminationOne(*i, n->get_body(), controlflow);
    }
}

class DoPreOnEachFunctionVisitor: public AstSimpleProcessing
   {
     public:
          virtual void visit(SgNode* n)
             {
               if (isSgFunctionDefinition(n))
                  {
                 // printf ("Calling partialRedundancyEliminationFunction on node = %s \n",n->class_name().c_str());
                 // n->get_file_info()->display("location of SgFunctionDefinition");

                    PRE::partialRedundancyEliminationFunction(isSgFunctionDefinition(n));
                  }
             }
   };

// Do partial redundancy elimination on all functions within n.
void
PRE::partialRedundancyElimination(SgNode* n)
   {
     DoPreOnEachFunctionVisitor().traverse(n, preorder);
   }


module Jekyll

  class Draft < Post

    # Valid post name regex (no date)
    MATCHER = /^(.*)(\.[^.]+)$/

    # Draft name validator. Draft filenames must be like:
    # my-awesome-post.textile
    #
    # Returns true if valid, false if not.
    def self.valid?(name)
      name =~ MATCHER
    end

    # Get the full path to the directory containing the draft files
    def containing_dir(source, dir)
      File.join(source, dir, '_drafts')
    end

    # The path to the draft source file, relative to the site source
    def relative_path
      File.join(@dir, '_drafts', @name)
    end

    # Extract information from the post filename.
    #
    # name - The String filename of the post file.
    #
    # Returns nothing.
    def process(name)
      m, slug, ext = *name.match(MATCHER)
      self.date = File.mtime(File.join(@base, name))
      self.slug = slug
      self.ext = ext
    end

  end

end


REPORTER = dot

build: blob.js

blob.js:
	@./node_modules/.bin/browserify --standalone blob index.js > blob.js

test:
	@./node_modules/.bin/zuul -- test/index.js

clean:
	rm blob.js

.PHONY: test blob.js


/**
 * This file has no copyright assigned and is placed in the Public Domain.
 * This file is part of the mingw-w64 runtime package.
 * No warranty is given; refer to the file DISCLAIMER.PD within this package.
 */
#define __MTxSpm_LIBRARY_DEFINED__
#include "comsvcs.h"


# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}


package org.evomaster.core.problem.rest

import org.evomaster.core.problem.rest.auth.AuthenticationInfo
import org.evomaster.core.problem.rest.auth.NoAuth
import org.evomaster.core.problem.rest.param.BodyParam
import org.evomaster.core.problem.rest.param.FormParam
import org.evomaster.core.problem.rest.param.Param
import org.evomaster.core.problem.rest.resource.ActionRToken
import org.evomaster.core.problem.rest.util.ParamUtil
import org.evomaster.core.problem.rest.util.ParserUtil
import org.evomaster.core.search.Action
import org.evomaster.core.search.gene.Gene
import org.evomaster.core.search.gene.OptionalGene
import java.net.URLEncoder

class RestCallAction(
        /**
         * Identifier unique within the individual
         * **/
        val id:String,
        val verb: HttpVerb,
        val path: RestPath,
        val parameters: MutableList<Param>,
        var auth: AuthenticationInfo = NoAuth(),
        /**
         * If true, it means that it will
         * instruct to save the "location" header of the HTTP response for future
         * use by following calls. Typical case is to save the location of
         * a resource generated with a POST
         */
        var saveLocation: Boolean = false,
        /**
         * Specify to use the "location" header of a
         * previous POST as path. As there might be different
         * POSTs creating different resources in the same test,
         * need to specify an id.
         *
         * Note: it might well be that we save the location returned
         * by a POST, where the POST itself might use a location for
         * path coming from a previous POST
         */
        var locationId: String? = null,
        val produces: List<String> = listOf(),
        val responseRefs : MutableMap<String, String> = mutableMapOf()
) : RestAction {

    /**
     * collect info of description and summary from swagger
     */
    private var description : String? = null

    /**
     * tokens is used to present a set of tokens exist in the scheme, e.g., swagger specification
     * key is a parsed token
     * value is [ActionRToken]
     */
    val tokens : MutableMap<String, ActionRToken> = mutableMapOf()

    override fun shouldCountForFitnessEvaluations(): Boolean = true

    fun isLocationChained() = saveLocation || locationId?.isNotBlank() ?: false

    override fun copy(): Action {
        val p = parameters.asSequence().map(Param::copy).toMutableList()
        return RestCallAction(id, verb, path, p, auth, saveLocation, locationId, produces, responseRefs)
    }

    override fun getName(): String {
        return "$verb:$path"
    }

    override fun seeGenes(): List<out Gene> {
        return parameters.flatMap { it.seeGenes() }
    }

    override fun toString(): String {
        return "$verb ${resolvedPath()} , auth=${auth.name}"
    }

    fun resolvedPath(): String {
        return path.resolve(parameters)
    }

    /**
     * Make sure that the path params are resolved to the same concrete values of "other".
     * Note: "this" can be just an ancestor of "other"
     *
     * Man: extend bind other types of params, e.g., body param
     **/
    fun bindToSamePathResolution(other: RestCallAction) {
        if (!this.path.isAncestorOf(other.path)) {
            throw IllegalArgumentException("Cannot bind 2 different unrelated paths to the same path resolution: " +
                    "${this.path} vs ${other.path}")
        }
//        for (i in 0 until parameters.size) {
//            val target = parameters[i]
//            if (target is PathParam) {
//                val k = other.parameters.find { p -> p is PathParam && p.name == target.name }!!
//                parameters[i].gene.copyValueFrom(k.gene)
//            }
//        }
        bindToSamePathResolution(other.path, other.parameters)
    }

    /**
    Note: in swagger the "consume" type might be missing.
    So, if for any reason there is a form param, then consider
    the body as an application/x-www-form-urlencoded

    see https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
    Note: that is old HTML 4, still dealing with RFC 1738, from 1994...

    HTML 5.1 (November 2016) has these rules:
    https://www.w3.org/TR/html/sec-forms.html#urlencoded-form-data

    which unfortunately are unreadable...

    Regarding URLEncoder in Java 8, it refers to URIs from RFC 2396 from
    1998 (updating RFC 1738), which is obsoleted by RFC 3986 since 2005!!!

    Plus, x-www-form-urlencoded and encoding of URIs are not the same!!!

    REALLY: WTF?!?

    TODO: update/verify based on
    https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer

     */
    @Deprecated("No needed anymore with OpenAPI v3")
    fun getBodyFormData(): String? {

        val forms = parameters.filterIsInstance<FormParam>()
        if(forms.isEmpty()){
            return null
        }

        return forms.filter { it.gene !is OptionalGene || it.gene.isActive }
                .map {
                    val name = URLEncoder.encode(it.gene.getVariableName(), "UTF-8")
                    val value = URLEncoder.encode(it.gene.getValueAsRawString(), "UTF-8")
                    "$name=$value"
                }
                .joinToString("&")
    }

    fun initTokens(description : String?){
        if(description!= null){
            this.description = description
            tokens.clear()
            ParserUtil.parseAction(this, description, tokens)
        }
    }

    /**
     * it is used to bind [this] action regarding values of [params]
     */
    fun bindToSamePathResolution(otherPath : RestPath, params : List<Param>) {

        if(params.isEmpty()){
            //no param is required to bind
            return
        }
        /*
           there may exist that a rest action has e.g., path parameter and body parameter.
           in this case (not all body param), we only bind non- BodyParam, e.g., PathParam
           the body parameter will be bound by "repair" process to ensure the same attribute of path and body parameter have same value.
         */
        if(!ParamUtil.isAllBodyParam(parameters)){
            parameters.filter { param -> !(param is BodyParam) }.forEach { param->
                ParamUtil.bindParam(param, this.path, otherPath, params)
            }
        }else{
            parameters.forEach {param->
                ParamUtil.bindParam(param, this.path, otherPath, params)
            }
        }
    }

    fun getDescription() : String? = description
    fun clearRefs(){
        responseRefs.clear()
    }
    fun addRef(key: String, ref: String){
        responseRefs[key] = ref
    }

}


/*
 * Copyright (c) 2017 Rostislav Pehlivanov <atomnuker@gmail.com>
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef AVCODEC_MDCT15_H
#define AVCODEC_MDCT15_H

#include <stddef.h>

#include "fft.h"

typedef struct MDCT15Context {
    int fft_n;
    int len2;
    int len4;
    int inverse;
    int *pfa_prereindex;
    int *pfa_postreindex;

    FFTContext ptwo_fft;
    FFTComplex *tmp;
    FFTComplex *twiddle_exptab;

    DECLARE_ALIGNED(32, FFTComplex, exptab)[64];

    /* 15-point FFT */
    void (*fft15)(FFTComplex *out, FFTComplex *in, FFTComplex *exptab, ptrdiff_t stride);

    /* PFA postrotate and exptab */
    void (*postreindex)(FFTComplex *out, FFTComplex *in, FFTComplex *exp, int *lut, ptrdiff_t len8);

    /* Calculate a full 2N -> N MDCT */
    void (*mdct)(struct MDCT15Context *s, float *dst, const float *src, ptrdiff_t stride);

    /* Calculate the middle half of the iMDCT */
    void (*imdct_half)(struct MDCT15Context *s, float *dst, const float *src,
                       ptrdiff_t stride);
} MDCT15Context;

/* Init an (i)MDCT of the length 2 * 15 * (2^N) */
int ff_mdct15_init(MDCT15Context **ps, int inverse, int N, double scale);
void ff_mdct15_uninit(MDCT15Context **ps);

void ff_mdct15_init_x86(MDCT15Context *s);

#endif /* AVCODEC_MDCT15_H */