Страницы

Поиск по вопросам

вторник, 25 июня 2019 г.

Очередь команд в консоли Linux

Предположим, что в данный момент в консоли уже выполняется какая-то команда. Она занимает продолжительное время. Я хотел бы выполнить одну или несколько команд после того, как завершится эта. Как это сделать?
Я знаю, что можно Ctrl + Z и bg, но это не совсем то. Нужно не в бэкграунд отправить команду, а именно дождаться ее завершения.


Ответ

используйте не bg (background), а fg (foreground), после которой добавьте нужную команду (команды):
$ date; sleep 10 Fri Oct 2 10:45:04 MSK 2015 ^Z [1]+ Stopped sleep 10 $ fg; date sleep 10 Fri Oct 2 10:45:14 MSK 2015
fg и последующую команду можно связать и условиями, например: && или ||

Как изменить размер div блока мышкой?

Как можно изменить высоту или ширину блока с помощью мышки, без библиотеки jquery-ui ?
Такой как здесь, только без jquery-ui, искал в интернете, но не смог найти.
Может есть какие-то другие библиотеки (размером 5-10 kb.) но не такие большие как jquery-ui


Ответ

Если тебе нужно изменять ширину блока мышкой, вот пример. Если немножко изменить скрипт, я думаю что и высоту можно менять, только css код нужно доделать.
$(document).ready(function() { var p = $(".pan1"); var d = $(".pan2"); var r = $("#resize"); var curr_width = p.width() var unlock = false; $(document).mousemove(function(e) { var change = curr_width + (e.clientX - curr_width); if(unlock) { if(change > 199) { $("#debug").text(e.clientX + " resize"); p.css("width", change); d.css("margin-left", change); } else { p.css("width", 200); d.css("margin-left", 200); } } }); r.mousedown(function(e) { curr_width = p.width(); unlock = true; r.css("background-color", "rgba(0, 0, 0, 0.2)"); }); $(document).mousedown(function(e) { if(unlock) { e.preventDefault(); } }); $(document).mouseup(function(e) { unlock = false; $("#debug").text(""); r.css("background-color", "rgba(0, 0, 0, 0.1)"); }); });

Архитектура клиентского приложения Android для интернет магазина [закрыт]

Есть интернет-магазин, нужно написать для него приложение, а точнее для работы с его админкой. Основными функциями должны быть:
добавление/изменение/удаление товаров просмотр списка заказов просмотр категорий/подкатегорий/товаров в виде дерева или каких-то выпадающих списков
В качестве обмена данными с сервером используются REST API. В ответ сервер отдает JSON.
Для выполнения запросов использую ApacheHttpClient (смотрю в сторону библиотеки Volley). Для этого у меня создан класс со статическими переменными и методами для каждого запроса.
На данном этапе интересует следующее: в главном Активити есть кнопка "получить древо категорий" —» вызывается класс со статическим методом для данного запроса —» в ответ получаю JSON-ом список категорий/ подкатегорий.

Коротко о работе приложения: В MainActivity пользователь нажимает на кнопку найти товар по id -->> вызывается асинхронный запрос который обращается к статическому методу класса API.getProductById(String id) -->> метод посылает нужный запрос на сервер и получает от него ответ -->> далее метод .getProductById(String id) отправляет ответ в MainActivity -->> в MainActivity формируется интент с получеными данными и отправляется в нужную активити где и отображаются данные для пользователя.
Вопросы:
1) правильный ли я использую подход для выполнения запросов?
2) как поступить с полученным ответом, где и как его лучше хранить, возможно использовать БД?
3) каким способом можно отобразить данное дерево в Активити, что бы при нажатии на товар я мог просмотреть о нем информацию?
КАТЕГОРИЯ
-подкатегория
-товар
-товар
-подкатегория
-товар
-товар
4) Какая иерархия классов должна присутствовать в подобном приложении.
5) Так же интересует: архитектура подобных приложений и какие средства используются для их реализации? Возможно какие-то примеры, литература...
P.S. Это первый опыт написания функционально приложения для Android с помощью Java, буду рад любой информации!


Ответ

Вы немного неверно строите архитектуру. Вам надо:
Вынести обращения к серваку в отдельный сервис, который крутится в своем потоке - см. в сторону IntentService Полученые от Rest API данные надо складывать в SQLite БД, иначе если не будет коннекта или коннект будет медленный у вас ничего не будет показываться. Показывать нужно всегда данные из БД, причем посредством ContentProvider над этим БД. ContentProvider должен давать вам Cursor, а от курсора вы должны рисовать уже свои деревья/списки.
В итоге получится так, что в фоне у вас будет крутиться сервис, который подсасывает данные из сервера и кладет в БД. Ну а поскольку над БД у вас будет развернут ContentProvider то все изменения которые будет вносить сервис в БД будет автоматом отображаться на UI.
Для рисования списков в последнее время модным стало использование RecyclerView, он лучше себя ведет в тяжелых списках с картинками, правда и кода он требует чуток больше чем ListView
Как то так.

Как вывести все выбранные файлы?

Есть поле . Когда я выбираю несколько файлов, хочу получить этот массив и вывести ниже списком пути файлов.
Есть такой код:
$('.add_photo_input').on('change', function() { var value = $('form input[type=file]').val(); alert(value); });
Я получаю значение только последнего выбранного файла, а мне надо всех выбранных: могу выбрать и три файла, и нужно получить пути всех трёх файлов.


Ответ

Имена всех выбранных файлов можно получить с помощью поля files (тип FileList) у , которое представляет собой список из File
$('form input[type=file]').on('change', function() { for (var i = 0; i < this.files.length; i++) { console.log(this.files[i].name); } });


Статья на MDN про работу с файлами

Стоит ли использовать спецсимволы в названиях таблиц/столбцов?

В мануале MySQL.Ru указано, что можно называть таблицы и столбцы с применением различных символов (в более ранних версиях _, $). Стоит ли пользоваться или ограничиваться _?


Ответ

Такие вещи должны оговариваться в "стандарте кодирования" конкретной команды разработчиков. Что для одних норма, то другим кошмар. Важно чтобы в одном проекте не было разнообразия в этом плане.
В тех местах, где мне приходилось работать, для имен MySQL было принято ограничиваться нижним регистром латиницы + цифры и подчеркивание, snake-синтаксис.
Вот примеры толковых соглашений об именах: http://anandarajpandey.com/2015/05/10/mysql-naming-coding-conventions-tips-on-mysql-database/ https://raw.githubusercontent.com/treffynnon/sqlstyle.guide/gh-pages/_includes/sqlstyle.guide.md

express.js не могу получить post данные из формы

Есть следующая форма:


и файл server.js:
var express = require("express");
var bodyParser = require('body-parser'); var multer = require('multer'); // v1.0.5 var upload = multer(); // for parsing multipart/form-data
var app = express();
app.use(bodyParser.json()); // for parsing application/json app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
app.set("view engine","ejs");
app.use(express.static(__dirname + "/public"));
app.get("/", function(req,res){ res.render("pages/index", { title: "Главная", msg: "Привет Мир!", data: q, info: "" }); });
app.post('/', upload.array(), function (req, res, next) { console.log(req.body); //{} console.log(req.body.title); //undefined console.log(req.body.data); //undefined
res.json(req.body); //{} });
app.get("/hello", function(req,res){ res.render("pages/other", { title: "Другая страница", msg: "hello world" }); });
app.use(function(req, res, next) { res.status(404).render("pages/error", { title: "ошибка 404", msg: "ошибка 404" });
res.status(500).render("pages/error", { title: "ошибка 500", msg: "ошибка 500" }); });
app.listen(8080); console.log("Сервер запущен!");
Делаю по туториалам, недавно начал с этим разбираться, столкнулся с тем, что создал форму, но получить на сервере данные никак я не могу. пробовал и req.query, и req.params('title') -- undefined.
Пакеты установил которые подключаю.
Собственно... много информации на английском языке -- может я что то упускаю?
Вопрос: Как получить данные из формы передаваемые методом post и обработать их на сервере используя технологии nodejs и express.js?


Ответ

Для отправки данных формы необходимо проименовать поля.


В name - с каким названием параметр прилетит серверу. id - ид инпута для браузера, с сервером не связан никак.

array.sort() не понимает украинские буквы Є и І

Имеется простой массив имен на украинском языке, в котором имена сортируются в алфавитном порядке например:

Все сортируется отлично за исключением имен которые начинаются с украинских букв Є и І — такие имена почему то ставит первыми. Как сделать чтобы они были на нужном месте?


Ответ

Нашел ответ на англоязычной версии StackOverflow:

Сохранение положения скролла при возврате на страницу

Есть страница А. Мы опустились в самый низ и перешли по ссылке на страницу В.
Как сохранить положение окна (в данном случае самый низ страницы) по возврату на страницу А?


Ответ

На событии $(window).unload(...) сохранить позицию скролла страницы в локальном хранилище или куках. При возврате проверять наличие сохраненного значения и мотать до сохраненного значения.

Эффективная работа с большими объемами данных

Изначальная архитектура приложения была построена ошибочно - все данные сливались в один файл, размер которого перевалил теперь за отметку 950ГБ.
Есть ли какой-нибудь эффективный метод (из области big-data) выделения из этого массива данных групп сущностей, состоящих из одних и тех же символов (так называемых анаграмм)?


Ответ

Стандартная идея вот какая:
К каждой сущности дописать её номер в исходном списке. У каждой сущности отсортировать символы. Отсортировать сущности. Теперь анаграммы будут находиться рядом. Теперь для нахождения анаграмм нужен лишь один пробег по данным.
Все эти операции хорошо «параллелятся», Кроме, пожалуй сортировки.
С учётом этого можно изменить немного алгоритм:
Распартиционировать данные как угодно. Пронумеровать сущности и отсортировать их символы на каждом хосте по отдельности (номеру назначать уникальный префикс, чтобы не смешивать) Отсортировать данные каждого хоста. Смёржить все данные. Сначала заливать результат на первый хост, когда 1/n всех данных зальётся — на второй хост и т. д. (Это по сути перепартиционирование.) Далее пробег по данным на каждом хосте.
Вместо пункта 4, возможно, более эффективно будет не сливать данные вместе, а делать многохостовый пробег:
string currentValue = null; int anagramCount = 0; string[] nextByHost = new string[N]; for (i = 0..n-1) { currentByHost[i] = ""; nextByHost = fetch from host[i]; }
while (any of hosts has data) { if any of nextByHost[i] equals to currentValue, anagramCount++ fetch next from host[i] to nextByHost[i] else store currentValue and anagramCount currentValue = min(nextByHost) }

Загрузка методов фреймворка в программу

Возможно ли загрузить все используемые фреймворком библиотеки в программу, чтобы она запускалась на компьютерах без установленного фреймфорка


Ответ

Да, с .NET Native это возможно.
Но при этом не забудьте о текущих ограничениях:
ваша программа должна быть скомпилирована не под .NET Framework, а под .NET Core вследствие этого десктопные приложения (WinForms, WPF) сразу отпадают целевая платформа вашей программы должна быть Windows 10 вам всё же придётся таскать с собой dll-ку рантайм-библиотеки вам придётся кое-где указывать метаданные вручную (например, для классов, которые будут сериализироваться или с которыми будет использована рефлексия), и тестировать, не забыли ли вы что-нибудь
Со временем какие-то из этих ограничений уйдут. Но если вы новичок в .NET, я бы не советовал сейчас (2015 год) этим пользоваться.

Как узнать на каком input (его id) находится курсор?

У меня есть 4 -а. Я набираю числа, и курсор автоматически переходит на следующий . Как узнать id текущего (в котором находится курсор) -а?


Ответ

$(document).ready(function(){ $('input').on('focus',function(){ var $self = $(this); $('#result').text($self.attr('id')); }); });


При старте приложения используется 86% оперативной памяти(HeapSize)

Я разрабатываю приложение для Андроида(платформа Xamarin). На текущий момент это приложение является прототипом(внутри нет ничего тяжелого). Было добавленна библиотека app7compat support,так же был заимплементен тулбар с DrawerLayout. Провожу тесты на устройстве Samsung Galaxy S4 Active и если корректно понимаю,то это скорее всего баг\глюк, т.к. при запуске приложения Android Device Monitor показывает следующее :
Как такое вообще возможно? Моя активити содержит:
Тулбар(с Drawer layout) Пару контролов,таких как Imageview/ImageButtons
Как я говорил раньше,это происходит сразу после запуска. Чем же может быть занята память на 86%, если даже нету никаких цпу\гпу вычислений? Почему это происходит?


Ответ

Это нормально. Приложение занимает 86% от выделенной в данный момент для него памяти.
Много конечно, но не смертельно.
Гляньте лучше сколько памяти всего может быть выделено. В случае андроид это делается так:
Runtime rt = Runtime.getRuntime(); long maxMemory = rt.maxMemory(); Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));
или так:
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); int memoryClass = am.getMemoryClass(); Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));
Если не ошибаюсь для S4 это 192 Mb (201326592 b)

Что означают следующие ANSI escape последовательности?

Просмотрел несколько источников, в том числе англоязычных, но нигде не нашел расшифровку следующих escape-последовательностей:
\e[%i%d;%dr (В библиотеке S-lang присваивается переменной с именемScroll_R_Str)
\e[%i%d;%dH (Тут 3 аргумента, но для установки позиции, согласно документации, используется 2, я не понял этого момента..., в библиотеке S-lang данная последовательность присваивается переменной с именемAbs_Curs_Pos_Str)
\e[P \e[%dM \e[%dL
Обновлено: неизвестны для меня также следующие последовательности (первые две, насколько я понимаю, не являются ESC-последовательностями, так ли это?)
\016 \017 \e(B\e)0
Помогите пожалуйста в них разобраться и понять, что они делают?


Ответ

Посмотрите man console_codes или то же самое в сети или по-русски. С ESC[ начинаются CSI-последовательности.

Как отключить рекламу?

Как отключить рекламу в android приложении после клика пользователя, например, на сутки? Стандартный баннер, который вызывается в Activity
AdView adView = (AdView) this.findViewById(R.id.adView); AdRequest adRequest = new AdRequest.Builder().build(); adView.loadAd(adRequest)
Что с ним сделать, чтобы отключить после нажатия?


Ответ

Сохрани в shared preferences или в бд переменную,которая хранит время последнего нажатия ,при включении приложения загружай переменную и сравнивай с текущей датой ,в последующем вызывай adview

Реализовать Counting Sort в стиле C++

Стало интересно, как оформить алгоритм сортировки подсчетом из Кормена по стандартам C++ для сортировки произвольных объектов, а не только чисел. Достоинство этой сортировки, кроме скорости, в том, что она устойчива, то есть если набор каких-то объектов пронормирован целыми числами, но алгоритм не будет перемешивать объекты с одинаковой нормой. Можно либо задать функцию нормировки, либо создавать пары "объект - число", которые передаются в функцию сортировки. Вот код:
#include #include #include
//Сортировка подсчетом void csort(const std::vector in, std::vector &out, int max){ int *c = new int[max + 1]; std::memset(c, 0, (max + 1) * sizeof(int)); out.resize(in.size()); for(int i = 0; i < in.size(); i++) c[in.at(i)]++; for(int i = 1; i < max + 1; i++) c[i] = c[i] + c[i - 1]; for(int i = in.size() - 1; i >= 0; i--){ c[in[i]]--; out[c[in[i]]] = in[i]; } delete[] c; }
int main(){ std::vector u, v; int a[17] = {0, 4, 7, 1, 2, 3, 3, 3, 9, 11, 13, 15, 15, 2, 17, 16, 16}; size_t max = 17;
u.assign(a, a + 17); std::copy(u.begin(), u.end(), std::ostream_iterator(std::cout, " ")); csort(u, v, max); std::cout << std::endl; std::copy(v.begin(), v.end(), std::ostream_iterator(std::cout, " ")); std::cin >> max; }
В C++ советуют вместо массивов везде использовать векторы. Можно каким-то образом в строке int *c = new int[max + 1]; использовать вектор? И надо ли это вообще? В первой части c[in.at(i)]++; я использую функцию at() вместо прямого обращения по индексу in[i], а в конце обращаюсь к элементам напрямую. Какой способ лучше? Например, что в последнем цикле множество функций at() будет очень затруднять чтение кода.
Как еще можно переписать этот код? Как правильно писать абстракцию типа данных, которые не являются числами и которые надо сортировать?


Ответ

В C++ советуют вместо массивов везде использовать векторы. Можно каким-то образом в строке int *c = new int[max + 1]; использовать вектор?
Да, примерно так:
std::vector c(max + 1, 0);
Оно же будет и инициализацией нулями.
И надо ли это вообще?
Это Ваша гарантия того, что в случае exception ниже, выделенная память будет освобождена. Т.е. в общем случае да, надо.
В первой части c[in.at(i)]++; я использую функцию at() вместо прямого обращения по индексу in[i], а в конце обращаюсь к элементам напрямую. Какой способ лучше? Например, что в последнем цикле множество функций at() будет очень затруднять чтение кода.
at - генерирует out_of_range exception, в случае, если Вы пытаетесь обратиться к элементу вне размеров массива. Обращение "напрямую" в этом случае приведет к undefined behavior. В остальном они идентичны. На мой взгляд, т.к. Вы точно знаете размеры своего массива и никак не рискуете обратиться вне, и никто, кроме Вас и этого потока этот массив не изменяет, Вы смело можете обращаться "напрямую".
Как еще можно переписать этот код?
Красивый пример предложен в соседнем ответе :)
Я же предложу обратить внимание на возможные проблемы кода.
1) int i = in.size() - 1 - размер массива имеет тип size_t, который с хорошим шансом больше, чем int. И самая печаль, если он имеет ту же разрядность, но, например, unsigned. Это может при определенном размере массива дать Вам отрицательное стартовое значение. И в случае обращения по at(), Вы получите exception, и просто потечете памятью там, где делаете new int, а в случае обращения напрямую, будет undefined behavior, и Ваш процесс, например, полезет взламывать Пентагон :)
2) c[i] = c[i] + c[i - 1]; - теоретически это может привести к переполнению, и получению веселья. Но метод подсчета не используется при большом разбросе значений, а минимум у Вас не устанавливается и по дефолту 0, поэтому на это можно (из соображений здравого смысла) не обращать внимания.
3) Эту часть я бы заменил на более простое.
out.resize(in.size()); //... for(int i = 1; i < max + 1; i++) c[i] = c[i] + c[i - 1]; for(int i = in.size() - 1; i >= 0; i--){ c[in[i]]--; out[c[in[i]]] = in[i];
Вот на это:
out.reserve(in.size()); for(size_t i = 0; i < max + 1; i++) for (size_t j = c[i]; j > 0; --j) out.push_back(i);
Имхо так понятнее, что происходит.
Как правильно писать абстракцию типа данных, которые не являются числами и которые надо сортировать?
Зависит от того, по какому признаку их надо сортировать. Исходя из этого и будет необходимо сформировать целочисленное представление этого признака. Если же признаков для сортировки нет, и надо отсортировать "как-нибудь", лучше всего сразу на месте объявить массив отсортированным. Ну, по какому-то существующему, известному только Вам секретному правилу :)

Почему jQuery использует id как атрибут для querySelectorAll?

Как известно, jQuery.fn.find по возможности пытается скормить селектор браузерному querySelectorAll
При этом есть вариант, когда для некоторого элемента генерируется id, после чего браузеру достаётся селектор, в начало которого добавилась преверка на этот id.
Зачем вообще нужен этот фокус разобрался. Селектор ставится в соответствие не внутри элемента, а вообще в документе. Просто фильтруется поддерево.
Вопрос зачем делается выборка по атрибуту вместо id Т. е. почему nid = "[id='" + nid + "'] ";, а не nid = "#" + nid + " ";?
Более полный контекст для двух версий приведён далее:
1.8.1
// qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { var groups, i, len, old = context.getAttribute("id"), nid = old || expando, newContext = rsibling.test( selector ) && context.parentNode || context;
if ( old ) { nid = nid.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); }
groups = tokenize(selector, context, xml); // Trailing space is unnecessary // There is always a context check nid = "[id='" + nid + "']"; for ( i = 0, len = groups.length; i < len; i++ ) { groups[i] = nid + groups[i].selector; } try { push.apply( results, slice.call( newContext.querySelectorAll( groups.join(",") ), 0 ) ); return results; } catch(qsaError) { } finally { if ( !old ) { context.removeAttribute("id"); } } }
1.11.3
// qSA works strangely on Element-rooted queries // We can work around this by specifying an extra ID on the root // and working up from there (Thanks to Andrew Dupont for the technique) // IE 8 doesn't work on object elements if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { groups = tokenize( selector );
if ( (old = context.getAttribute("id")) ) { nid = old.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); } nid = "[id='" + nid + "'] ";
i = groups.length; while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); } newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; newSelector = groups.join(","); }


Ответ

Коротко о проблеме:


JS:
var foo = document.getElementById("foo"); console.log( foo.querySelectorAll('div span').length ); // 1
Проблема querySelectorAll в том, что сначала он вытащит все что подходит под селектор div span начиная с корня документа, а потом отфильтрует полученный результат на вхождение в элемент-контекст (foo) - т.е. селектор div span найдет один span и да - этот span находится внутри #foo
Трюк с аттрибутами используется как раз для того, чтобы исключить элемент контекста и начинать запрос сразу с корня документа, избегая такой странной выборки. Небольшое пояснение кода:
if ( (old = context.getAttribute("id")) ) { nid = old.replace( rescape, "\\$&" ); } else { context.setAttribute( "id", nid ); }
Если у элемента-контекста есть id, то используем его, а иначе добавляем ему id с уникальным только что созданным хешем.
nid = "[id='" + nid + "'] ";
Преобразуем в селектор аттрибута, благодаря операции выше мы уверены, что у элемента-контекста всегда будет id - либо уникальный либо сгенерированный.
i = groups.length; while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); }
Группы - это если селекторов несколько (перечислены через запятую). Здесь в начало каждой группы добавляется селектор аттрибута. Если брать оригинальный пример с запросом foo.querySelectorAll('div span'), то группа в данном случае это div span, а после добавления аттрибута получится [id="foo"] div span. Пре передаче этого нового селектора в querySelectorAll запрос будет выполнен уже в рамках элемента-контекста не смотря ни на что.
Используются аттрибуты [id="foo"] вместо #foo, т.к. идентификатор по спецификации может сожержать спецсимволы, которые очень сложно эскейпить самостоятельно, и в команде было принято решение скинуть парсинг идентификатора на браузер.
Источники:
John Resig - Thoughts on querySelectorAll jquery/sizzle - Replace ID-attribute selectors with ID-selectors where possible #354 jquery/sizzle - Bug #7533. Fix for Element-rooted queries where ID contains CSS3 selector meta characters jquery/sizzle - Bug #7533. Fix for IDs which contain CSS3 selector meta characters #37

Как получить порядковый номер символа по таблице Unicode

Скажите, как можно получить порядковый номер символа по таблице Unicode в Java? В Паскале была функция ord(char), а какой аналог в Java?


Ответ

Если у вас есть
char c = 'W';
то нужно сделать всего-лишь
int u = (int) c;
Результатом будет 87.

Как и где в Android Studio посмотреть количество запущенных services у приложения?

Раньше эклипс я помню показывал какие и сколько сервисов у приложения запущено, а в AndroidStudio как посмотреть?


Ответ

Tools -> Android -> Monitor
Там отображаются процессы. Там же отображаются и сервисы.

PUSH уведомления [закрыт]

В проекте необходимо реализовать механизм PUSH уведомлений. Проект идет на двух платформах - Android и iOS.
Есть конкретные вопросы.
Мне обязательно надо регистрировать приложения в сервисах - APNS для iOS и GCM для Android или это можно избежать если пользоваться сторонними сервисами? Как получить уникальный регистрационный TOKEN для iOS и Андроид, какие библиотеки для этого используются? Какие сервисы вы можете посоветовать для упрощения данной задачи? Желательно с хорошей документацией по описанию API и примерами приложений для каждой из платформ. Ну и возможно вы посоветуете для каждой из платформ хорошие статьи с примерами, где все объясняется по пунктам.


Ответ

Отвечу только по части iOS
Да, надо. Ответ на этот вопрос содержится тут. Никакие библиотеки не нужны, главное помнить, что токен можно получить, и, соответственно, протестировать только на реальном устройстве, а не симуляторе. Чтобы протестировать приём уведомлений можете воспользоваться этой утилитой. Могу посоветовать вот эту статью и вот это видео.

Естественная сортировка (natural order) и ActiveRecord в Yii2

Использую Active Record в Yii2, и получаю список в такой последовательности (сортировка по ASCII, ASCIIbetical order):
Pacific 12°-28° 1200w 80v Pacific 14°-35° 1200w 80v Pacific 23°-50° 1200w 80v Pacific 5.5°-13° 1200w 80v
Однако нормальный пользователь (не программист!) ожидает, что последний из элементов будет в начале. Естественная сортировка (natural order, сортировка по алфавиту):
Pacific 5.5°-13° 1200w 80v Pacific 12°-28° 1200w 80v Pacific 14°-35° 1200w 80v Pacific 23°-50° 1200w 80v
В PHP возможность использовать естественную сортировку есть по умолчанию (что, кстати, преимущество языка по сравнению с другими):
функция natsort() функция sort() с флагом SORT_NATURAL функция strnatcmp()
Но вот использование фреймворка Yii2, похоже, сводит это преимущество на нет. Я получаю тот самый "ASCIIbetical order", который нужно как-то пересортировывать. Дополнительная засада зарыта в виджете GridView, который если и получит отсортированный список, то при сортировке по клику на ярлык колонки перестроит список опять же как в первом примере.
Есть ли возможность "минимальной кровью" подружить Yii2 и естественную сортировку?
P.S. В указанной выше статье есть чудный фрагмент:
Я не могу понять одну тупую вещь. Боже мой, люди. Вы программисты. Почти у всех высшее образование, но никто из вас не знает, что черт подери значит "по алфавиту". Вам должно быть стыдно. Если вы используете для сортировки по алфавиту дефолтный алгоритм языка программирования — скорее всего, это сортировка по ASCII (по хорошей причине), — то идите к ближайшему зеркалу и дайте себе несколько пощечин, а потом вернитесь на рабочие места и исправьте юнит-тесты, которые не ловят эту проблему.


Ответ

Неделю я воевал с PHP и MySQL, выясняя нюансы работы с сортировкой. Грабли лежат на каждом шагу. Например, natcasesort() - учитывает регистр, но спотыкается о цифры:
Рабочая станция визуализации/файл-сервер системы рабочая станция художника по свету Тубус Pacific 12°-28° Тубус Pacific 14°-35° Тубус Pacific 23°-50° Тубус Pacific 5.5°-13° (элемент списка не на месте)
sort($lines, SORT_NATURAL | SORT_FLAG_CASE) - упорядочивает смесь цифр и букв, но спотыкается о регистр, если есть кириллица:
Рабочая станция визуализации/файл-сервер системы Тубус Pacific 5.5°-13° Тубус Pacific 12°-28° Тубус Pacific 14°-35° Тубус Pacific 23°-50° рабочая станция художника по свету (элемент не на месте)
Важный теоретический момент: MySQL не умеет делать естественную сортировку, а Yii2 получает данные в том порядке, в каком из отдает база данных. Поэтому вместо сортировки по буквенно-цифровому полю стоит использовать сортировку по полю вспомогательному. О реализации этой идеи - ниже. Не буду рассказывать о всех деталях, расскажу о ключевых.
Создаем вспомогательное поле (ниже - sequence). В нем будем хранить целые числа (int). Элементы списка пронумерованы сотнями начиная со ста. Это позволит нам добавлять новые элементы не пересчитывая всю последовательность. Со ста начинаем, чтобы можно было добавлять элементы в начало списка и не заморачиваться с отрицательными числами. Аналогичный рецепт есть у Александра Макарова в блоге, только он предлагает хранить последовательность в виде decimal. Этот момент совершенно непринципиален, выбирайте то, что больше нравится.
Теперь нам нужно просчитать последовательность для имеющегося списка. Я создал класс CyrrilicHandler, в котором есть несколько методов, связанных с этой задачей:
public function upperletters() { return [ 'А','Б','В','Г','Д','Е','Ё','Ж','З','И','Й','К','Л','М','Н','О','П', 'Р','С','Т','У','Ф','Х','Ц','Ч','Ш','Щ','Ъ','Ы','Ь','Э','Ю','Я', ]; }
public function lowerletters() { return [ 'а','б','в','г','д','е','ё','ж','з','и','й','к','л','м','н','о','п', 'р','с','т','у','ф','х','ц','ч','ш','щ','ъ','ы','ь','э','ю','я', ]; }
// Приводим строку к нижнему регистру public function strtolower($string) { return str_replace(self::upperletters(), self::lowerletters(), $string); }
// Приводим строку к верхнему регистру public function strtoupper($string) { return str_replace(self::lowerletters(), self::upperletters(), $string); }
// Естественная регистронезависимая сортировка (базовая латиница + кириллица) public function sort($array = []) { $complexArray = [];
foreach ($array as $value) { $complexArray[] = [ 'value' => $value, 'lowercase' => self::strtolower($value), ]; }
ArrayHelper::multisort($complexArray, ['lowercase'], [SORT_ASC], SORT_NATURAL | SORT_FLAG_CASE); return ArrayHelper::getColumn($complexArray, 'value'); }
// Естественная регистронезависимая сортировка с возможностью задать первый // ключ возвращаемого массива и интервал между элементами // Первый элемент - 100, далее - с интервалом 100 public function sortAndSetInitialAndInterval($array = []) { $raw = self::sort($array); $res = [];
$key = 100; // начинаем последовательность со ста
foreach ($raw as $value) { $res[$key] = $value; $key += 100; // интервал - 100 }
return $res; }
Обновляем имеющиеся данные:
$lines = Line::find() ->select('line') ->orderBy('line') ->column();
// Естественная регистронезависимая сортировка $lines = CyrrilicHandler::sortAndSetInitialAndInterval($lines);
$table = Line::tableName();
foreach ($lines as $sequence => $line):
$query = "UPDATE {$table} SET sequence = :sequence WHERE line = :line";
Yii::$app->db->createCommand($query) ->bindValue(':sequence', $sequence) ->bindValue(':line', $line) ->execute();
endforeach; // $lines as $sequence => $line
Проделывать такую операцию каждый раз не хочется, поэтому я создал метод newPosition(), который возвращает номер в существующей последовательности для заданной строки. Вот этот метод и методы, связанные с ним (CyrrilicHandler):
// Поиск позиции нового элемента в последовательности (чтобы не обновлять все записи // последовательности) public function newPosition($string) { // В зависимости от первого символа строки (цифра или нет) получаем разные блоки данных $block = self::isInitialSymbolNumber($string) ? self::findEntriesStartedWithNumber() : self::findEntriesStartedWithSameSymbol($string);
if (sizeof($block)) {
// Если переданная строка уже есть в БД, возвращаем ее позицию if (array_search($string, $block) !== false) return array_search($string, $block);
list($before, $after) = self::analyseBlockWithString($block, $string); } else { list($before, $after) = self::analyseBordersWithString($string); }
return $before + floor(($after - $before)/2); }
// Проверяем первый символ (цифра или нет) private function isInitialSymbolNumber($string) { return is_numeric(mb_substr($string, 0, 1, 'UTF-8')); }
private function findEntriesStartedWithNumber() { return Line::find() ->select('line') ->where("line REGEXP '^[0-9]'") ->orderBy('sequence') ->indexBy('sequence') ->column(); }
private function findEntriesStartedWithSameSymbol($string) { return Line::find() ->select('line') ->where(['like', 'line', mb_substr($string, 0, 1, 'UTF-8') . '%', false]) ->orderBy('sequence') ->indexBy('sequence') ->column(); }
private function analyseBlockWithString($block, $string) { // Добавляем в блок строку и сортируем, исходный блок остается неизменным $sorted = self::sort(array_merge($block, ['x' => $string]));
// Находим номер нового элемента в отсортированной последовательности $eid = array_search($string, $sorted);
$before = isset($sorted[$eid-1]) ? array_search($sorted[$eid-1], $block) : false; $after = isset($sorted[$eid+1]) ? array_search($sorted[$eid+1], $block) : false;
if ($before && $after) return [$before, $after];
// Если строка на границе - нужны доп. данные из БД if (!$before) $before = self::findBeforeByAfter($after) ?: 0;
if (!$after) $after = self::findAfterByBefore($before) ?: array_pop(array_keys($block)) + 100;
return [$before, $after]; }
private function analyseBordersWithString($string) { $before = self::findPreviousEntryNumber($string) ?: 0; $after = self::findAfterByBefore($before) ?: false;
// Если не нашлось ничего ни до ни после (первая запись в БД) if ($before === 0 && !$after) $after = 200;
if ($before !== 0 && $after) return [$before, $after];
// Добавляемая строка попадает в конец списка, т.к. интервал 100, то суммируем двойной интервал // (позже разделится на 2) return [$before, $before + 200]; }
private function findPreviousEntryNumber($string) { return self::isInitialSymbolNumber($string) ? self::findPreviousStartedWithNumber() : self::findPreviousStartedWithNotNumber($string); }
private function findPreviousStartedWithNumber() { return Line::find() ->select('sequence') ->where(['<', 'line', 0]) ->orderBy('sequence DESC') ->limit(1) ->scalar(); }
private function findPreviousStartedWithNotNumber($string) { return Line::find() ->select('sequence') ->where(['<', 'line', $string]) ->orderBy('sequence DESC') ->limit(1) ->scalar(); }
// Находим следующий номер за указанным private function findAfterByBefore($number) { return Line::find() ->select('sequence') ->where(['>', 'sequence', $number]) ->orderBy('sequence ASC') ->limit(1) ->scalar(); }
// Находим номер, предыдущий указанному private function findBeforeByAfter($number) { return Line::find() ->select('sequence') ->where(['<', 'sequence', $number]) ->orderBy('sequence DESC') ->limit(1) ->scalar(); }
Чтобы результат стал виден пользователям, указываем поле, по которому будем сортировать данные в параметрах сортировки провайдера данных:
[ 'defaultOrder' => [ 'sequence' => SORT_ASC, ], 'attributes' => [ 'line' => [ 'asc' => ['sequence' => SORT_ASC], 'desc' => ['sequence' => SORT_DESC], ], ], ];
Буду рад замечаниям и уточнениям.

Android Google Map v2 нарисовать маршрут

У меня есть Google Map fragment:

Мое местоположение получаю вот так:
private void getMyLocation() { LatLng latLng = new LatLng(mMap.getMyLocation().getLatitude(), mMap.getMyLocation().getLongitude()); CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, 16); mMap.animateCamera(cameraUpdate); }
Нужно показать маршрут к заданным координатам, как на фото:


Ответ

Помогла данная библиотека. Мой код:
Route supportRoute = new Route();
LatLng source = new LatLng(mMap.getMyLocation().getLatitude(), mMap.getMyLocation().getLongitude()); LatLng dest= new LatLng(destLat, destLog);
supportRoute.drawRoute(mMap, MapsActivity.this, source, dest, true, "en");

Компиляция 7-zip c помощью MSYS2

Подскажите, как компилировать 7-zip c помощью MSYS2


Ответ

Подробная инструкция по компиляции с помощью MSVC доступна здесь
Добавлю вариант с компиляцией через Cygwin
Скачиваем Cygwin: выбираем версию x32. Устанаваливаем, например, в директорию C:\projects\tools\cygwin. Из доступных пакетов понадобятся: make, gcc/g++, wxwidgets. Соглашаемся с установкой предложенных пакетов. Скачиваем нужную версию 7zip под POSIX-системы: sourceforge. Распаковываем, например, в директорию C:\projects\other\7zip\source Запускаем Cygwin.bat, переходим в директорию, где распакована библиотека 7zip: cd /cygdrive/c/projects/other/7zip/source В директории с исходниками есть множество makefile для различных платформ, выбираем makefile.cygwin и копируем содержимое в файл makefile.machine Выполняем make -j4 для компиляции цели all Редактируем файл install.sh для указания своего пути установки скомпилированных файлов. Находим параметр DEST_HOME и изменяем его, например, так: /cygdrive/c/projects/other/7zip/build Выполняем ./install.sh Теперь в каталоге /bin расположен исполняемый файл 7za.exe. Для его запуска копируем следующие библиотеки: cyggcc_s-1.dll, cygstdc++-6.dll, cygwin1.dll, из каталога cygwin_install_dir/bin 7zip готов к использованию

Как исключить некоторые свойства при серилизации

Я пытаюсь сериализовать объект с несколькими свойствами, но не хочу включать все свойства при сериализации.
Конечно, я мог бы использовать атрибут [XmlIgnore], но мне необходима возможность делать это динамически.
Вопрос заключается в следующем как исключить некоторые свойства при серилизации не изменяя исходного класса, при это хотелось бы иметь строгую типизацию.
Заранее спасибо.


Ответ

сделать это можно следующим образом.
Воспользуемся готовым классом XmlAttributeOverrides но сделаем пару методов расширений для добавления строгой типизации:
public static class XmlAttributeOverridesHelpers { public static void Add(this XmlAttributeOverrides overrides, Expression> propertySelector, XmlAttributes attributes) { overrides.Add(typeof(T), propertySelector.BuildString(), attributes); }
public static string BuildString(this Expression propertySelector) { switch (propertySelector.NodeType) { case ExpressionType.Lambda: LambdaExpression lambdaExpression = (LambdaExpression)propertySelector; return BuildString(lambdaExpression.Body);
case ExpressionType.Convert: case ExpressionType.Quote: UnaryExpression unaryExpression = (UnaryExpression)propertySelector; return BuildString(unaryExpression.Operand);
case ExpressionType.MemberAccess:
MemberExpression memberExpression = (MemberExpression)propertySelector; MemberInfo propertyInfo = memberExpression.Member;
if (memberExpression.Expression is ParameterExpression) { return propertyInfo.Name; } else { // we've got a nested property (e.g. MyType.SomeProperty.SomeNestedProperty) return BuildString(memberExpression.Expression) + "." + propertyInfo.Name; }
default: // drop out and throw break; } throw new InvalidOperationException("Expression must be a member expression: " + propertySelector.ToString()); } }
применять можно следующим образом:
var overrides = new XmlAttributeOverrides(); var ignore = new XmlAttributes { XmlIgnore = true }; overrides.Add(m => m.Id, ignore); overrides.Add(m => m.DateChanged, ignore); Type t = typeof(List); XmlSerializer serial = new XmlSerializer(t, overrides);
Решение было найдено здесь

Работа перегруженного оператора во время инициализации экземпляра класса [закрыт]

Как перегрузить, например, оператор присваивания таким образом, чтобы он работал во время инициализации экземпляра класса?
#include ` using namespace std;`
class Matrix { private: int **Arr; int Size; public: Matrix(int SizeOfMatrix); Matrix operator = (Matrix &); };
int main() { Matrix A(4); Matrix B(4) = A; system("pause"); return 0; } Matrix::Matrix(int SizeOfMatrix) { Size = SizeOfMatrix; Arr = new int *[Size]; for (int i = 0; i < Size; i++) { Arr[i] = new int[Size]; for (int j = 0; j < Size; j++) Arr[i][j] = rand() % 50; } } Matrix Matrix :: operator =(Matrix &A) { for (int i = 0; i < Size; i++) { for (int j = 0; j < Size; j++) Arr[i][j] = A.Arr[i][j]; } return *this; }


Ответ

Прежде всего хотел бы отметить, что данное предложение
Matrix B(4) = A;
некорректное и не должно компилироваться. Фактически в данном предложении имеется два инициализатора: 4 и A
Но даже если вы правильно напишите
Matrix A(4); Matrix B = A;
то здесь вызывается не оператор присваивания, как вы думаете, а конструктор копирования, который определен компилятором неявно, и который просто почленно копирует члены данных объектов.
Когда вы динамически в классе распределяете память, то есть используете указатели, то вы должны определить явно по крайней мере конструктор копирования, копирующий оператор присваивания и деструктор. В противном случае поведение вашей программы может оказаться неопределенным. Например, в результате использования конструктора копирования, созданного компилятором неявно, может оказаться, что два объекта имеют указатели на одну и ту же область памяти, а потому при удалении этих объектов будет осуществлена попытка удалить одну и ту же память дважды.
Ваш класс может выглядеть следующим образом
class Matrix { private: int **Arr; size_t Size; public: explicit Matrix( size_t ); Matrix( const Matrix & ); ~Matrix(); Matrix & operator = ( const Matrix & ); };
Matrix::Matrix( size_t SizeOfMatrix ) : Arr( nullptr ), Size ( SizeOfMatrix ) { if ( Size ) { Arr = new int *[Size];
for ( size_t i = 0; i < Size; i++ ) { Arr[i] = new int[Size]; for ( size_t j = 0; j < Size; j++ ) Arr[i][j] = rand() % 50; } } }
Matrix::Matrix( const Matrix &rhs ) : Arr( nullptr ), Size ( rhs.Size ) { if ( Size ) { Arr = new int *[Size];
for ( size_t i = 0; i < Size; i++ ) { Arr[i] = new int[Size]; for ( size_t j = 0; j < Size; j++ ) Arr[i][j] = rhs.Arr[i][j]; } } }
Matrix::~Matrix() { for ( size_t i = 0; i < Size; i++ ) delete [] Arr[i]; delete [] Arr; }
Matrix & Matrix::operator =( const Matrix &rhs ) { if ( this != &rhs ) { int **tmp = nullptr;
if ( rhs.Size != 0 ) { if ( Size != rhs.Size ) { tmp = new int *[rhs.Size];
for ( size_t i = 0; i < rhs.Size; i++ ) { tmp[i] = new int[rhs.Size]; } } else { tmp = Arr; }
for ( size_t i = 0; i < rhs.Size; i++ ) { for ( size_t j = 0; j < rhs.Size; j++ ) tmp[i][j] = rhs.Arr[i][j]; } }
if ( Size != rhs.Size ) { for ( size_t i = 0; i < Size; i++ ) delete [] Arr[i]; delete [] Arr; }
Arr = tmp; Size = rhs.Size; }
return *this; }

.htaccess - обращение к файлам отличного формата

На хостинге находятся множество файлов со следующими именами:
index.php.html index.php_cat=1.html index.php_product=1.html index.php_view=content.html pages.php_view=order.html pages.php_view=register.html price.php.html
Нужно сделать так, чтоб при обращении в браузере по линкам, тянулся контент с этих файлов. Линки в браузере соответственно:
mydomain.com/index.php mydomain.com/index.php?cat=1 mydomain.com/index.php?product=1 mydomain.com/index.php?view=content mydomain.com/pages.php?view=order mydomain.com/pages.php?view=register mydomain.com/price.php


Ответ

Требуемую задачу выполняет следующая конструкция:
RewriteEngine On RewriteBase /
RewriteCond %{QUERY_STRING} !^$ RewriteRule ^(.*\.php)$ $1_%{QUERY_STRING}.html? [L,R=301,NC]
RewriteCond %{QUERY_STRING} ^$ RewriteRule ^(.*\.php)$ $1.html [L,R=301,NC]
Если же требуется, чтобы адрес в строке браузера не менялся, нужно просто убрать директиву редиректа R=..., т.е. содержимое файла будет следующим:
RewriteEngine On RewriteBase /
RewriteCond %{QUERY_STRING} !^$ RewriteRule ^(.*\.php)$ $1_%{QUERY_STRING}.html? [L,NC]
RewriteCond %{QUERY_STRING} ^$ RewriteRule ^(.*\.php)$ $1.html [L,NC]

Имитация множественного наследования

Есть класс и функция-конструктор. Делается попытка реализовать класс, являющийся чем-то типа потомка обоих. Точнее, методы из прототипа функции-конструктора копируются в прототип класса-потомка, унаследованный от класса.
Да, я понимаю, что это не полноценное наследование, но для решения задачи этого хватает.
Проблема в другом. Как заставить тайпскрипт воспринимать копируемые методы?
class First { someMethod() { console.log('someMethod from First'); } }
function Second() { console.log('Second'); }
Second.prototype.doSmth = function () { console.log('doSmth from Second'); }
interface IBoth { someMethod() doSmth() }
class Both extends First /* implements IBoth */ { constructor() { console.log('constructor of Both'); super(); Second.call(this); } }
for (let key in Second.prototype) { Both.prototype[key] = Second.prototype[key]; }
На самом деле, надо обеспечить видимость методов ещё на уровень дальше
class Final extends Both { doIt() { this.someMethod(); //this.doSmth(); // Надо заставить видеть метод тут (this as any as IBoth).doSmth(); // Компилируется, но это ужас } }
если при этом методы не будут видны из самого Both, то это годится.
Вот что я уже пробовал:
При попытке написать
class Both extends First implements IBoth {
возникает ошибка, что я не реализую методы интерфейса. При переименовании Both в _Both и использовании
var Both = _Both as typeof _Both;
всё остаётся как было, что логично, поскольку тут никак не используется First При переименовании Both в _Both и использовании
var Both = _Both as typeof IBoth;
говорит, что не может найти имя IBoth
Пробовал ещё несколько вариантов, но они совсем бредовые. Что ещё можно сделать?

Попробовать можно тут: http://www.typescriptlang.org/Playground Полный код для проверки
Запустить (код из правой панели) после добавления строки:
(new Final).doIt();
Вывод при запуске при раскомментированной строке this.doSmth();
constructor of Both Second someMethod from First doSmth from Second doSmth from Second

PS: Этот вопрос на английском.


Ответ

Интерфейс вообще не нужен, нужно просто объявить прототипное поле с нужным типом:
doSmth: () => void
Она видима как свойство, а не как метод, но это непринципиально.
Полный код:
class First { someMethod() { console.log('someMethod from First'); } }
function Second() { console.log('Second'); }
Second.prototype.doSmth = function () { console.log('doSmth from Second'); }
class Both extends First { constructor() { console.log('constructor of Both'); super(); Second.call(this); }
doSmth: () => void }
for (let key in Second.prototype) { Both.prototype[key] = Second.prototype[key]; }
class Final extends Both { doIt() { this.someMethod(); this.doSmth(); //Both.prototype.doSmth(); // ok //Final.prototype.doSmth(); // ok } }
PS: Надо было гуглить не всяческие варианты с наследованием, а typescript class prototype variable - сразу нашёл подходящий вариант.

SimpleJavaFileObject импорт кастом пакета/классов

Приветствую!
Задача заключается в компиляции динамического класса(созданного из строкового представления) и дальнейшем инстанцировании.
Проблема заключается в импортировании моего пакета/классов, хранящихся в отличных от стандартных java-директорий (lib), в динамический класс. К примеру: после выполнения строки import controller.*; возникает ошибка:
/CompiledClass.java:4: error: package controller does not exist
но стандартные пакеты/классы импортируются без ошибок, и компиляция соответственно выполняется тоже без ошибок.
Пробовал решить через getTask метод JavaCompiler Api путем отправки optionList с параметрами, но в итоге не успешно.
Как правильно импортировать в динамический класс свои пакет/классы?
Мой код:
package view;
import controller.*;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URI;
import java.security.SecureClassLoader;
import java.util.*;
import javax.tools.*; import javax.tools.JavaCompiler.*; import javax.tools.JavaFileObject.Kind;
public class CompileJavaManager {
public static void compileJavaFileObject(StringBuilder inputString, Writer jspOut) throws Exception { String className = "CompiledClass"; String classMethod = "methodOf" + className; Class compiledClass = null; //Class c = Class.forName(className);
StringBuilder javaFileContents = new StringBuilder("" + //"package compiled;" + '
' + "import java.io.*;
" + "import java.util.*;
" + "import controller.*;
" + '
' + "public class " + className +"{
" + " private Writer out = null;
" + '
' + " public " + className +"(Writer out){
" + " this.out = out;
" + //" this.container = container;" + " }" + " public void " + classMethod + "() throws java.io.IOException{
" + //inputString + " }
" + "}
");
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); DiagnosticCollector diagnosticsCollector = new DiagnosticCollector(); StandardJavaFileManager standardJavaFileManager = compiler.getStandardFileManager(diagnosticsCollector, null, null); SpecialClassLoader classLoader = new SpecialClassLoader(); SpecialJavaFileManager fileManager = new SpecialJavaFileManager(standardJavaFileManager, classLoader);
List optionList = new ArrayList(); optionList.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));
JavaObjectFromString javaObjectFromString = new JavaObjectFromString(className, javaFileContents.toString());
Iterable fileObjects = Arrays.asList(javaObjectFromString); Iterable classes = null; Writer out = new PrintWriter(jspOut);
CompilationTask task = compiler.getTask(out, fileManager, diagnosticsCollector, optionList, classes, fileObjects); Boolean result = task.call(); List diagnostics = diagnosticsCollector.getDiagnostics();
if (result) { compiledClass = classLoader.findClass(className); Constructor constructor = compiledClass.getConstructor(Writer.class); Object instance = constructor.newInstance(jspOut); //call the method, pass a null params Method instanceMethod = compiledClass.getDeclaredMethod(classMethod, null); instanceMethod.invoke(instance, null); //System.out.println(instance); } else { // Compilation fails for (Diagnostic d : diagnostics) { System.out.println(d); } } }
private static class JavaObjectFromString extends SimpleJavaFileObject { private String sourceCode = null;
public JavaObjectFromString(String className, String sourceCode) throws Exception { super(URI.create("file:///" + className + Kind.SOURCE.extension), Kind.SOURCE); this.sourceCode = sourceCode; }
@Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return sourceCode; }
@Override public OutputStream openOutputStream() { throw new IllegalStateException(); }
@Override public InputStream openInputStream() { return new ByteArrayInputStream(sourceCode.getBytes()); } }
private static class JavaObjectFromByteCode extends SimpleJavaFileObject { private ByteArrayOutputStream baos;
public JavaObjectFromByteCode(String name) { super(URI.create("byte:///" + name + Kind.CLASS.extension), Kind.CLASS); }
@Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { throw new IllegalStateException(); }
@Override public OutputStream openOutputStream() { baos = new ByteArrayOutputStream(); return baos; }
@Override public InputStream openInputStream() { throw new IllegalStateException(); }
public byte[] getBytes() { return baos.toByteArray(); } }
private static class SpecialClassLoader extends ClassLoader { private Map m = new HashMap();
protected Class findClass(String name) throws ClassNotFoundException { JavaObjectFromByteCode jobc = m.get(name); if (jobc==null){ jobc = m.get(name.replace(".","/")); if (jobc==null){ return super.findClass(name); } } return defineClass(name, jobc.getBytes(), 0, jobc.getBytes().length); }
public void addClass(String name, JavaObjectFromByteCode jobc) { m.put(name, jobc); } }
private static class SpecialJavaFileManager extends ForwardingJavaFileManager { private SpecialClassLoader scl;
public SpecialJavaFileManager(StandardJavaFileManager sjfm, SpecialClassLoader scl) { super(sjfm); this.scl = scl; }
public JavaFileObject getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject sibling) throws IOException { JavaObjectFromByteCode jobc = new JavaObjectFromByteCode(name); scl.addClass(name, jobc); return jobc; }
public ClassLoader getClassLoader(Location location) { return scl; } }
}
Заранее спасибо!


Ответ

Во время компиляции ты указал
optionList.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")));
Это значит, что компилятор во время компиляции видит только jar'ники из стандартной библиотеки. Твой jar'ник с пакетом controller наверняка лежит отдельно. Поэтому надо испльзовать
optionList.addAll(Arrays.asList("-classpath", System.getProperty("java.class.path")+";путь/где/лежит/jar/с/пакетом/controller.jar"));

XSS - кавычка вначале

скажите, что означает кавычка и знак больше вначале следующего кода?
">
почему эти знаки ставятся таким образом, приведите пример с какой фильтрацией проходит такое?


Ответ

Такое проходит, если вы на странице делаете вывод сразу в HTML тег, например в атрибут. Вот такой код вставит тег скрипт в HTML и загрузит вредоносный JS код:
Ссылка
На выходе получим HTML код:
">Ссылка
Что и вызовет выполнение JS скрипта.
Что бы избежать такого, удаляете теги через strip_tags() или преобразуйте все теги в специальные сущности htmlspecialchars()

Поиск необходмых заголовков

Есть метод, который считает хедеры для таблицы
private List headers(String html) { Document doc = Jsoup.parse(html); ArrayList result = new ArrayList<>(); Elements header; Element firstThead = doc.select("thead").first(); Elements trOfFirstThead = firstThead.children(); for (Element tr : firstThead.children()) { Elements select = tr.select("th"); for (Element th : select) { String s = th.attributes().get("rowspan"); if (!s.isEmpty() && s.equals(String.valueOf(trOfFirstThead.size()))) { result.add(th.text()); } } } header = trOfFirstThead.last().children();
for (Element element : header) { if (element.tag().getName().equals(tag_th)) { result.add(element.text()); } } return result; }
Суть метода такова - на вход поступает таблица, у которой есть раздел thead и из него необходимо получить хедеры в виде коллекции строк. Если хедеры расположены в несколько рядов, то выбирается нижний ряд, и по нему берутся названия.
Данный алгоритм работает для таблиц, представленых под номерами 1, 2, и 3 ( см. вложение). Но для таблицы типа 4 хедеры находятся не правильно.
Требуемая коллекция :
h4 h10 h11 h12 h6 h7
При работе алгоритма получается следующая коллекция :
h4 h7 h10 h11 h12
Прошу помочь советом/алгоритмом, как можно реализовать нужное поведение.
P.S. исходный код таблиц.

  • Таблица 1

    h1 h2 h3
    1 2 3
    4 5 6
    7 8 9
  • Таблица 2

    h4 h5
    h1 h2 h3
    1 2 3
    4 5 6
    7 8 9
  • Таблица 3

    h4 h5
    h1 h2
    1 2 3
    4 5 6
    7 8 9
  • Таблица 4

    h4 h5 h7
    h1 h2 h8 h6
    h10 h11 h12
    1 2 3 4 5 6
    7 8 9 10 11 12


Ответ

По-моему, решением будет реализация в каком-то объеме прописанного в HTML5 алгоритма построения таблицы, благо обработка там простая. Вот этот код выдает нужный результат на ваших примерах:
static class TableHeader { private String[][] cells; private int y_height = 0; private int x_width = 0;
public TableHeader( int rows, int columns, Element thead ) { cells = new String[rows][columns];
parseTHead( thead ); }
private void ensureCapacity( int rows, int columns ) { if ( rows <= cells.length && columns <= cells[0].length ) return;
int nRows = Math.max( cells.length, rows ); int nColumns = Math.max( cells[0].length, columns );
String[][] newCells = new String[nRows][nColumns]; for ( int row = 0; row < cells.length; row++ ) { System.arraycopy(cells[row], 0, newCells[row], 0, cells[row].length ); }
cells = newCells; }
private void fill( String cellValue, int row, int col, int rowspan, int colspan ) { ensureCapacity( row + rowspan, col + colspan ); for ( int r = 0; r < rowspan; r++ ) { for ( int c = 0; c < colspan; c++ ) { cells[row + r][col + c] = cellValue; } } }
private int cellSpan( Element th, String attrName ) { String attrValue = th.attr( attrName ); int result = 1; if ( attrValue.isEmpty() ) return result; try { result = Integer.parseInt( attrValue ); } catch ( NumberFormatException ex ) { /*ignore*/ }; return result; }
// http://www.w3.org/TR/html5/tabular-data.html#algorithm-for-processing-row-groups private void parseTHead( Element thead ) { //int y_start = y_height; // #1 int y_current = 0; final Elements rows = thead.children().select( "tr" ); final int rowsNumber = rows.size(); ensureCapacity(rowsNumber, x_width); for ( Element tr : rows ) { // #2 //http://www.w3.org/TR/html5/tabular-data.html#algorithm-for-processing-rows if ( y_height == y_current ) { y_height += 1; } int x_current = 0; //TODO: Run the algorithm for growing 'downward-growing cells'. for ( Element currentCell : tr.children().select( "td, th" ) ) { //6. While xcurrent is less than xwidth and the slot with coordinate (xcurrent, ycurrent) // already has a cell assigned to it, increase xcurrent by 1. while ( x_current < x_width && cells[y_current][x_current] != null ) x_current += 1; if ( x_current == x_width ) { x_width += 1; //# 7 } int colspan = cellSpan( currentCell, "colspan" ); //#8 int rowspan = cellSpan( currentCell, "rowspan" ); //#9 if (colspan == 0) colspan = 1; //TODO: 10. If rowspan is zero and the table element's Document is not set to quirks mode, // then let 'cell grows downward' be true, and set rowspan to 1. // Otherwise, let cell grows downward be false. //FIXME: не позволяем rowspan создавать больше строк, чем есть // как этот вопрос решен в стандарте? rowspan = Math.min( rowsNumber - y_current, rowspan ); if ( x_width < x_current + colspan ) x_width = x_current + colspan; if ( y_height < y_current + rowspan ) y_height = y_current + rowspan; // TODO: If any of the slots involved already had a cell covering them, // then this is a table model error. // Those slots now have two cells overlapping. fill( currentCell.text(), y_current, x_current, rowspan, colspan ); // #13 // TODO: If 'cell grows downward' is true, then add the tuple // {c, xcurrent, colspan} to the list of 'downward-growing cells'. x_current += colspan; //#15 } y_current += 1; } }
public List lastRow() { return Arrays.stream( cells[y_height - 1]).limit( x_width ).collect( Collectors.toList()); } }
private static List headers3(String html) { Document doc = Jsoup.parse(html);
Element firstThead = doc.select("thead").first();
TableHeader header = new TableHeader(10, 10, firstThead);
return header.lastRow(); }
В реализации не обрабатывается случай с rowspan="0", вроде как все манипуляции с шириной и высотой можно закинуть в fill, и ни на чем, кроме ваших примеров я ее не проверял. В качестве бонуса, такой подход позволяет легко получить полный заголовок столбца.
upd: есть очевидная проблема со случаем, когда y_current + rowspan превышает количество , в результате fill создает лишние ряды, чего в браузере не наблюдается. С colspan наверняка та же ситуация. Пока просто ограничил rowspan сверху, но я явно чего-то не понимаю в стандарте.

Составить регулярное выражение

Здравствуйте, такая проблема, необходимо написать регулярное выражение, которое выделяло бы все, что находится между словами =begin и =end. Если же случай такой - =begin ... =end ... =end, то вернуть нужно только то что идет до первого =end. Сам я написал вот это - "=begin(.*)=end", но это работает только, когда текст умещается в одной строке.


Ответ

using System.Text.RegularExpressions; // ... var str = "=begin 1 =end 2 =end"; var m = Regex.Match(str, "=begin(.+?)=end", RegexOptions.Singleline); m.Groups[1].Value // " 1 "

Перезапускается loadInBAckground в Loader, при повороте экрана

У меня есть Loader, создаю и вызываю его так:
mLoadData = getSupportLoaderManager().initLoader(LOADER_ID, bundle, this); mLoadData.onContentChanged();
При первом запуске все хорошо: запускается конструктор, onStart, doInBackground и, в конце, я получаю данные в активность. Но когда я поворачиваю экран — перезапускается loadInBackground, а потом вызывается два раза метод onLoadFinished в активности (она слушатель). Как сделать так, чтобы, при повороте экрана, я получил или ссылку на Loader, который уже существует, или получил данные, через метод onLoadFinished (можно эквивалентный), которые были загружены ранее, без перезапуска loadInBackground?


Ответ

Согласно en-SO, докам и здравому смыслу onContentChanged() говорит лоадеру, что данные изменились и их надо заново загрузить. Т.е. вам надо запускать лоадер методом его запуска (sic!) forceLoad() вместо того, что используете вы. В этом случае лоадер не перезапустится после поворота экрана и будет каждый раз выдавать только уже загруженные данные, если они есть.

Как разбить код, чтобы MethodLength был хотя бы 15

Есть консерн:
module Omniauthable extend ActiveSupport::Concern included do def self.find_for_oauth(auth) authorization = Authorization.where(provider: auth.provider, uid: auth.uid.to_s).first return authorization.user if authorization
if auth.info.try(:email) email = auth.info[:email] else return false end
user = User.where(email: email).first if user user.create_authorization(auth) else password = Devise.friendly_token[0, 20] user = User.new(email: email, password: password, password_confirmation: password) if user.valid? user.save! user.create_authorization(auth) else return false end end user end
def create_authorization(auth) authorizations.create(provider: auth.provider, uid: auth.uid) end end end
Нужно разделить self.find_for_oauth(auth), чтобы количество строк в методе было хотя бы 15.


Ответ

Я бы переписал так:
module Omniauthable extend ActiveSupport::Concern included do def self.find_for_oauth(auth) authorization = Authorization.where(provider: auth.provider, uid: auth.uid.to_s).first if authorization authorization.user else @email = auth.info.try(:email) find_user # ищем пользователя create_user unless @user # если не находим, то создаем auth_user(auth) # пытаемся авторизовать и возвращаем либо nil либо пользователя end end
def create_authorization(auth) authorizations.create(provider: auth.provider, uid: auth.uid) end private # скрываем служебные методы
def self.find_user @user = User.where(email: @email).first if @email end
def self.create_user password = Devise.friendly_token[0, 20] udata = { email: @email, password: password, password_confirmation: password } user = User.create(udata) # здесь происходит валидация и сохранение @user = user if user.errors.count == 0 # если после создания нет ошибок, то объявляем переменную экземпляра end
def self.auth_user(auth) if @user # если в предыдущих методах был найден/создан пользователь, если @user = nil(не существует, то nil вернется из метода) @user.create_authorization(auth) @user end end end end