/*
* Symphony - A modern community (forum/SNS/blog) platform written in Java.
* Copyright (C) 2012-2017, b3log.org & hacpai.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.b3log.symphony.service;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.b3log.latke.logging.Level;
import org.b3log.latke.logging.Logger;
import org.b3log.latke.service.annotation.Service;
import org.b3log.symphony.model.Common;
import org.b3log.symphony.util.Markdowns;
import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Manual query service.
* <p>
* Sees <a href="https://github.com/tldr-pages/tldr">tldr</a> for more details.
* </p>
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.1.3, Apr 19, 2017
* @since 1.8.0
*/
@Service
public class ManQueryService {
/**
* Logger.
*/
private static final Logger LOGGER = Logger.getLogger(ManQueryService.class);
/**
* All command manuals.
*/
private static final List<JSONObject> CMD_MANS = new ArrayList<>();
/**
* Whether tldr is enabled.
*/
public static boolean TLDR_ENABLED;
static {
new Thread(ManQueryService::init).start();
}
/**
* Initializes manuals.
*/
private static void init() {
// http://stackoverflow.com/questions/585534/what-is-the-best-way-to-find-the-users-home-directory-in-java
final String userHome = System.getProperty("user.home");
if (StringUtils.isBlank(userHome)) {
return;
}
try {
Thread.sleep(5 * 1000);
final String tldrPagesPath = userHome + File.separator + "tldr" + File.separator + "pages" + File.separator;
final Collection<File> mans = FileUtils.listFiles(new File(tldrPagesPath), new String[]{"md"}, true);
for (final File manFile : mans) {
InputStream is = null;
try {
is = new FileInputStream(manFile);
final String md = IOUtils.toString(is, "UTF-8");
String html = Markdowns.toHTML(md);
final JSONObject cmdMan = new JSONObject();
cmdMan.put(Common.MAN_CMD, StringUtils.substringBeforeLast(manFile.getName(), "."));
html = html.replace("\n", "").replace("\r", "");
cmdMan.put(Common.MAN_HTML, html);
CMD_MANS.add(cmdMan);
} catch (final Exception e) {
LOGGER.log(Level.ERROR, "Loads man [" + manFile.getPath() + "] failed", e);
} finally {
IOUtils.closeQuietly(is);
}
}
} catch (final Exception e) {
return;
}
TLDR_ENABLED = !CMD_MANS.isEmpty();
Collections.sort(CMD_MANS, (o1, o2) -> {
final String c1 = o1.optString(Common.MAN_CMD);
final String c2 = o2.optString(Common.MAN_CMD);
return c1.compareToIgnoreCase(c2);
});
}
/**
* Gets manuals by the specified command prefix.
*
* @param cmdPrefix the specified comman prefix
* @return a list of manuals, for example, <pre>
* [
* {
* "manCmd": "find",
* "manHTML": "...."
* }, ....
* ]
* </pre>, returns an empty list if not found
*/
public List<JSONObject> getMansByCmdPrefix(final String cmdPrefix) {
final JSONObject toSearch = new JSONObject();
toSearch.put(Common.MAN_CMD, cmdPrefix);
final int index = Collections.binarySearch(CMD_MANS, toSearch, (o1, o2) -> {
String c1 = o1.optString(Common.MAN_CMD);
final String c2 = o2.optString(Common.MAN_CMD);
if (c1.length() < c2.length()) {
return c1.compareToIgnoreCase(c2);
}
c1 = c1.substring(0, c2.length());
return c1.compareToIgnoreCase(c2);
});
final List<JSONObject> ret = new ArrayList<>();
if (index < 0) {
return ret;
}
int start = index;
int end = index;
while (start > -1 && CMD_MANS.get(start).optString(Common.MAN_CMD).startsWith(cmdPrefix.toLowerCase())) {
start--;
}
start++;
final int WINDOW_SIZE = 8;
if (start < index - WINDOW_SIZE) {
end = start + WINDOW_SIZE;
} else {
while (end < CMD_MANS.size() && end < index + 5 && CMD_MANS.get(end).optString(Common.MAN_CMD).startsWith(cmdPrefix.toLowerCase())) {
end++;
if (end >= start + WINDOW_SIZE) {
break;
}
}
}
return CMD_MANS.subList(start, end);
}
}