Related
I have a calculator app and I want it to show large numbers with spaces between them when users enter their input. Currently, they don't have spaces between them like this: 10000; 100000; 1000000. I want them to show like this: 10 000; 100 000; 1 000 000. The second one looks much neater.
Here is my code
class CalculatorActivity : AppCompatActivity() {
#Suppress("PrivatePropertyName")
private var DigitOnScreen = StringBuilder(12)
private var operation: Char = ' '
private var leftHandSide: Double = 0.0
private var rightHandSide: Double = 0.0
private lateinit var binding: ActivityCalculatorBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityCalculatorBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.resultId.text = "0"
initializeButtons()
}
private fun initializeButtons() {
functionalButtons()
operationalButtons()
numericalButtons()
}
/**
* This function initializes all of our numerical buttons from
* [0 - 9]
*/
private fun numericalButtons() {
binding.oneBtn.setOnClickListener {
appendToDigitOnScreen("1")
}
binding.twoBtn.setOnClickListener {
appendToDigitOnScreen("2")
}
binding.threeBtn.setOnClickListener {
appendToDigitOnScreen("3")
}
binding.fourBtn.setOnClickListener {
appendToDigitOnScreen("4")
}
binding.fiveBtn.setOnClickListener {
appendToDigitOnScreen("5")
}
binding.sixBtn.setOnClickListener {
appendToDigitOnScreen("6")
}
binding.sevenBtn.setOnClickListener {
appendToDigitOnScreen("7")
}
binding.eightBtn.setOnClickListener {
appendToDigitOnScreen("8")
}
binding.nineBtn.setOnClickListener {
appendToDigitOnScreen("9")
}
binding.zeroBtn.setOnClickListener {
appendToDigitOnScreen("0")
}
binding.dotBtn.setOnClickListener {
appendToDigitOnScreen(".")
}
}
/**
* Insert the button been clicked onto the screen so user can see
* inputs for the button clicked
*/
private fun appendToDigitOnScreen(digit: String) {
// Add each digit to our string builder
DigitOnScreen.append(digit)
// display it on the screen of our mobile app
binding.resultId.text = DigitOnScreen.toString()
}
/**
* Initialize the operation keys in our calculator like the
* addition key, subtraction key and the likes
*/
private fun operationalButtons() {
binding.additionBtn.setOnClickListener {
selectOperation('A')
}
binding.subtractBtn.setOnClickListener {
selectOperation('S')
}
binding.divideBtn.setOnClickListener {
selectOperation('D')
}
binding.multipyBtn.setOnClickListener {
selectOperation('M')
}
}
/**
* Function to assign operational sign to our math calculations
*/
private fun selectOperation(c: Char) {
operation = c
leftHandSide = DigitOnScreen.toString().toDouble()
DigitOnScreen.clear()
binding.resultId.text = "0"
}
/**
* Handles functional operations in out application like
* clear button, backspace button and the clear everything button
*/
private fun functionalButtons() {
binding.clearEverythingBtn.setOnClickListener {
DigitOnScreen.clear()
binding.resultId.text = "0"
}
binding.clearBtn.setOnClickListener {
if (DigitOnScreen.isEmpty()) {
return#setOnClickListener
} else {
clearDigit()
}
}
binding.backspaceBtn.setOnClickListener {
if (DigitOnScreen.isEmpty()) {
return#setOnClickListener
} else {
clearDigit()
}
}
binding.equalBtn.setOnClickListener {
performMathOperation()
}
}
/**
* This function performs our Math Operation which is then showed on the screen.
*/
private fun performMathOperation() {
rightHandSide = DigitOnScreen.toString().toDouble()
when (operation) {
'A' -> {
val sum = OperationsHelper.add(leftHandSide, rightHandSide)
binding.resultId.text = sum.toString()
DigitOnScreen.clear()
DigitOnScreen.append(sum)
}
'S' -> {
val subtract = OperationsHelper.subtract(leftHandSide, rightHandSide)
binding.resultId.text = subtract.toString()
DigitOnScreen.clear()
DigitOnScreen.append(subtract)
}
'M' -> {
val multiply = OperationsHelper.multiply(leftHandSide, rightHandSide)
binding.resultId.text = multiply.toString()
DigitOnScreen.clear()
DigitOnScreen.append(multiply)
}
'D' -> {
val divide = OperationsHelper.divide(leftHandSide, rightHandSide)
binding.resultId.text = divide.toString()
DigitOnScreen.clear()
DigitOnScreen.append(divide)
}
}
}
/**
* This function remove the last digit on the screen.
*/
private fun clearDigit() {
val length = DigitOnScreen.length
DigitOnScreen.deleteCharAt(length - 1)
if (length <= 0) {
binding.resultId.text = "0"
}else{
binding.resultId.text = DigitOnScreen.toString()
}
}
you can solve it like this for example :
val x = "10000000"
val s:StringBuilder = java.lang.StringBuilder("")
x.mapIndexed{ index, c ->
if ((index+1)%3 ==0){
s.append(" ")
}
s.append(c)
}
println(s)
you need to store the value of all digts -> and every 3 digit make a space
Assuming I have the following classes in Haxe:
class Pair<U, V> {
public var first:U = null;
public var second:V = null;
public function new(u:U, v:V) {
this.first = u;
this.second = v;
}
}
class HashablePair<U:{ function hashCode():Int; }, V:{ function hashCode():Int; }> {
public var first:U = null;
public var second:V = null;
public function new(u:U, v:V) {
this.first = u;
this.second = v;
}
public function hashCode():Int { // just a sample way ...
var h1:Int = (first == null) ? 0 : first.hashCode();
var h2:Int = (second == null) ? 0 : second.hashCode();
return 3 * h1 + 5 * h2;
}
}
I wondered if it is possible to write a macro that adds the hashCode function to the pair class, if and only if both generics U and V implement the hashCode-function ... and thus make it possible to combine the two classes into a single one via meta-programming.
You can achieve the desired behavior by simply switching to an abstract:
typedef Hashable = { function hashCode():Int; };
abstract HashablePair<U:Hashable,V:Hashable>(Pair<U,V>)from Pair<U,V> {
public function new(u:U, v:V)
this = new Pair(u, v);
public function hashCode():Int { // just a sample way ...
var h1:Int = (this.first == null) ? 0 : this.first.hashCode();
var h2:Int = (this.second == null) ? 0 : this.second.hashCode();
return 3 * h1 + 5 * h2;
}
}
The from Pair<U,V> makes Pair<U,V> casts to HashablePair<U,V> allowed, as long as the necessary constrains on U and V are respected.
For a complete exemple, check out Try Haxe #d76E1.
I am trying to prevent the login from another device. And after all the research i have found using session. I am using the default login system of yii2 framework. And added these codes in the user model.
The user model is:
<?php
namespace app\models;
use Yii;
//app\models\Users is the model generated using Gii from users table
use app\models\Users as DbUser;
class User extends \yii\base\Object implements \yii\web\IdentityInterface {
public $id;
public $username;
public $password;
public $authKey;
public $token;
public $email;
public $any;
public $user_type;
/**
* #inheritdoc
*/
public static function findIdentity($id) {
$dbUser = DbUser::find()
->where([
"id" => $id
])
->one();
if (!count($dbUser)) {
return null;
}
return new static($dbUser);
}
/**
* #inheritdoc
*/
public static function findIdentityByAccessToken($token, $userType = null) {
$dbUser = DbUser::find()
->where(["token" => $token])
->one();
if (!count($dbUser)) {
return null;
}
return new static($dbUser);
}
/**
* Finds user by username
*
* #param string $username
* #return static|null
*/
public static function findByUsername($username) {
$dbUser = DbUser::find()
->where([
"username" => $username
])
->one();
if (!count($dbUser))
{
return null;
}
return new static($dbUser);
}
/**
* #inheritdoc
*/
public function getId() {
return $this->id;
}
/**
* #inheritdoc
*/
public function getAuthKey()
{
return $this->authKey;
}
/**
* #inheritdoc
*/
public function validateAuthKey($authKey)
{
return $this->authKey === $authKey;
}
/**
* Validates password
*
* #param string $password password to validate
* #return boolean if password provided is valid for current user
*/
public function validatePassword($password)
{
return Yii::$app->getSecurity()->validatePassword($password, $this->password);
}
public function session_validate()
{
// Encrypt information about this session
$user_agent = $this->session_hash_string($_SERVER['HTTP_USER_AGENT'], $this->any);
// Check for instance of session
if ( session_exists() == false )
{
// The session does not exist, create it
$this->session_reset($user_agent);
}
// Match the hashed key in session against the new hashed string
if ( $this->session_match($user_agent) )
{
return true;
}
// The hashed string is different, reset session
$this->session_reset($user_agent);
return false;
}
/**
* session_exists()
* Will check if the needed session keys exists.
*
* #return {boolean} True if keys exists, else false
*/
private function session_exists()
{
return isset($_SESSION['USER_AGENT_KEY']) && isset($_SESSION['INIT']);
}
/**
* session_match()
* Compares the session secret with the current generated secret.
*
* #param {String} $user_agent The encrypted key
*/
private function session_match( $user_agent )
{
// Validate the agent and initiated
return $_SESSION['USER_AGENT_KEY'] == $user_agent && $_SESSION['INIT'] == true;
}
/**
* session_encrypt()
* Generates a unique encrypted string
*
* #param {String} $user_agent The http_user_agent constant
* #param {String} $unique_string Something unique for the user (email, etc)
*/
private function session_hash_string( $user_agent, $unique_string )
{
return md5($user_agent.$unique_string);
}
/**
* session_reset()
* Will regenerate the session_id (the local file) and build a new
* secret for the user.
*
* #param {String} $user_agent
*/
private function session_reset( $user_agent )
{
// Create new id
session_regenerate_id(TRUE);
$_SESSION = array();
$_SESSION['INIT'] = true;
// Set hashed http user agent
$_SESSION['USER_AGENT_KEY'] = $user_agent;
}
/**
* Destroys the session
*/
private function session_destroy()
{
// Destroy session
session_destroy();
}
}
this a better way of doing it is below.
you do is that you add an extra column to your users table, name it 'conc_login' or preferably text because it might be difficult to predict the size of data we are expecting.
when the user logs in, check if the logins column is empty, if it is empty, create a json that contains the session_id, time of login using the time() function.
If the logins column is not empty or the count of the column when decoded is greater than zero, then you check if the count is greater than login limit , if the number of logins is not yet greater than the login limit, then append the new session to the logins column in the database table
If the login limit is reached then check through the logins and check for the one with expired time, for example, a user that is not active of 300secnds is assumed to be logged out, then you delete the session that has expired from the table
In any request that is been made by a logged user you check if the session key still exists in the login column of the database ($logins[‘session_key’]), and if it is not found then log the user out immediately to avoid escalation of right and if otherwise then update the $login[‘time’] to the new time().
You can implement this code.
Login Form Model Add this function for concurrent user validation
$get_limit = Setting::findOne(['name' => 'login_limit']);
$login_limit = 3; //$get_limit->value;
$active_sess = User::findOne($getUser->id);
if ($active_sess->conc_login == '' or count(json_decode($active_sess->conc_login)) == 0) {
$login_json = json_encode([
[Yii::$app->session->getId() => Yii::$app->session->getId(), 'session_key' => Yii::$app->session->getId(), 'time' => time()]
]);
$active_sess->conc_login = $login_json;
$active_sess->save();
} else if (count(json_decode($active_sess->conc_login)) > 0 and count(json_decode($active_sess->conc_login)) $login_json = json_decode($active_sess->conc_login);
$login_json[] = [Yii::$app->session->getId() => Yii::$app->session->getId(), 'session_key' => Yii::$app->session->getId(), 'time' => time()];
$login_json = json_encode($login_json);
//print_r($login_json); exit;
$active_sess->conc_login = $login_json;
$active_sess->save();
} else if (count(json_decode($active_sess->conc_login)) >= $login_limit) {
$logins = json_decode($active_sess->conc_login);
foreach ($logins as $key => $login) {
if ($login->time < time() - 120) {
//this checks if the iterated login is greater than the current time -120seconds and if found to be true then the user is inactive
//then set this current login to null by using the below statement
//$logins[$key] = null; // or unset($logins[$key]) either should work;
unset($logins[$key]);
}
}
//after iteration we check if the count of logins is still greater than the limit
if (count($logins) >= $login_limit) {
//then return a login error that maximum logins reached
//echo 'you are not allowed to login as you have breeched the maximum session limit.';
//exit;
$login_json = json_encode($logins);
$active_sess->conc_login = $login_json;
$active_sess->save();
$this->addError($attribute, 'you are not allowed to login as you have breeched the maximum session limit.');
} else {
//then login is successsfull
$login_json = [];
foreach ($logins as $key => $val) {
$login_json[] = [$val->session_key => $val->session_key, 'session_key' => $val->session_key, 'time' => $val->time];
}
$login_json[] = [Yii::$app->session->getId() => Yii::$app->session->getId(), 'session_key' => Yii::$app->session->getId(), 'time' => time()];
$login_json = json_encode($login_json);
$active_sess->conc_login = $login_json;
$active_sess->save();
}
//update the logins column to equal to json_encode($logins);
}
User side update session every 60seconds : SiteController
public function actionUserSessionUpdate() {
$session = Yii::$app->session;
$userid = $session->get('userid');
$username = $session->get('username');
$data = array('session_id' => Yii::$app->session->getId());
$isUserLogin = (!empty($userid) && !empty($username)) ? 'true' : 'false';
if ($isUserLogin == 'false') {
echo 'gotologin'; exit;
//return $this->redirect(['/login']);
} else {
//Login user
$active_sess = Clientmanagement::findOne($userid);
$loginjson = json_decode($active_sess->conc_login);
$login_json = [];
foreach ($loginjson as $key => $val) {
if ($val->session_key == Yii::$app->session->getId()) {
$login_json[] = [$val->session_key => $val->session_key, 'session_key' => $val->session_key, 'time' => time()];
} else {
$login_json[] = [$val->session_key => $val->session_key, 'session_key' => $val->session_key, 'time' => $val->time];
}
}
$login_json = json_encode($login_json);
$active_sess->conc_login = $login_json;
$active_sess->save();
}
exit;
}
Client ajax
<?php
if (!empty($session->get('userid'))) {
$usersession_url = Yii::$app->urlManager->createAbsoluteUrl(["/site/user-session-update"]);
$scriptb = <<< JS
$(document).ready(function () {
//Wait 1 Minutes
setInterval(function(){
$.ajax({
type:"post",
data:'id=session',
url:"$usersession_url",
success:function(data){
}
});
}, 60000);
});
JS;
$this->registerJs($scriptb);
}
?>
UserController Logout
public function actionLogout() {
$session = Yii::$app->session;
$userid = $session->get('userid');
//concurrent active user session removed
$active_sess = Clientmanagement::findOne($userid);
$loginjson = json_decode($active_sess->conc_login);
foreach ($loginjson as $key => $login) {
if ($login->session_key == Yii::$app->session->getId()) {
unset($loginjson[$key]);
}
}
$login_json = json_encode($loginjson);
$active_sess->conc_login = $login_json;
$active_sess->save();
$session->destroy();
return $this->redirect(['/login']);
}
We can set that code in component and it will also work for every controller and action. Also, I changed a code
common/components/LoginDevicesLimit.php
<?php
namespace app\common\components;
use Yii;
/**
* Logged In Devices Storage Helper
*/
class LoginDevicesLimit extends \yii\base\Component
{
public function init() {
$this->saveLoginSession();
parent::init();
}
public function saveLoginSession(){
$login_limit = env('DEVICES_LIMIT', 3);
$active_sess = \Yii::$app->user->identityClass::findOne(\Yii::$app->user->id);
if(!$active_sess){
return false;
}
$session_count = count(json_decode($active_sess->conc_login, true));
if ($active_sess->conc_login == '' || $session_count == 0) {
$login_json = json_encode([
Yii::$app->session->getId() => ['session_key' => Yii::$app->session->getId(), 'time' => time()]
]);
$active_sess->conc_login = $login_json;
$active_sess->save();
} else if ($session_count > 0 && $session_count < $login_limit) {
$login_json = json_decode($active_sess->conc_login, true);
$login_json[Yii::$app->session->getId()] = ['session_key' => Yii::$app->session->getId(), 'time' => time()];
$login_json = json_encode($login_json);
//print_r($login_json); exit;
$active_sess->conc_login = $login_json;
$active_sess->save();
} else if ($session_count > $login_limit) {
$logins = json_decode($active_sess->conc_login, true);
foreach ($logins as $key => $login) {
if ($login->time < time() - 120) {
//this checks if the iterated login is greater than the current time -120seconds and if found to be true then the user is inactive
//then set this current login to null by using the below statement
//$logins[$key] = null; // or unset($logins[$key]) either should work;
unset($logins[$key]);
}
}
//after iteration we check if the count of logins is still greater than the limit
if (count($logins) > $login_limit) {
//then return a login error that maximum logins reached
//echo 'you are not allowed to login as you have breeched the maximum session limit.';
//exit;
$login_json = json_encode($logins);
$active_sess->conc_login = $login_json;
$active_sess->save();
throw new \yii\web\HttpException(404, 'You are not allowed to login as you have reached the maximum session limit.');
} else {
//then login is successsfull
$login_json = [];
foreach ($logins as $key => $val) {
$login_json[] = [$val->session_key => $val->session_key, 'session_key' => $val->session_key, 'time' => $val->time];
}
$login_json[] = [Yii::$app->session->getId() => Yii::$app->session->getId(), 'session_key' => Yii::$app->session->getId(), 'time' => time()];
$login_json = json_encode($login_json);
$active_sess->conc_login = $login_json;
$active_sess->save();
}
}
}
}
You can generate additional authentication code during login process that will be stored in user's identity and saved in db as well. Every time user invokes action his code is compared to this saved in db and if doesn't match user is forcefully logged out . This way only the latest login is valid. refer this link
This is an alternative way to prevent the login from another device.
You need to configure your application as shown below using the webvimark/user-management library.
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`auth_key` varchar(32) NOT NULL,
`password_hash` varchar(255) NOT NULL,
`confirmation_token` varchar(255) DEFAULT NULL,
`status` int(11) NOT NULL DEFAULT '1',
`superadmin` smallint(1) DEFAULT '0',
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
`registration_ip` varchar(15) DEFAULT NULL,
`bind_to_ip` varchar(255) DEFAULT NULL,
`email` varchar(128) DEFAULT NULL,
`email_confirmed` smallint(1) NOT NULL DEFAULT '0',
`sessions` longtext,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
<?php
$config =
[
'bootstrap' => ['log', 'LogoutDevices'],
'components' =>
[
'user' => ['class' => 'webvimark\modules\UserManagement\components\UserConfig', 'enableAutoLogin' => true, 'on afterLogin' => function($event) {(new \app\components\ABCDEFG\DevicesLimit\LoginDevicesLimit())->sessionManager(); \webvimark\modules\UserManagement\models\UserVisitLog::newVisitor($event->identity->id);}],
'LogoutDevices' => ['class' => 'app\components\ABCDEFG\DevicesLimit\LogoutDevices']
]
]
Then, you need to use the classes below:
<?php
namespace app\models\UserManagement;
use Yii;
/**
* This is the model class for table "user".
*
* #property string $sessions
*/
class User extends \webvimark\modules\UserManagement\models\User
{
function __construct($config = [])
{
parent::__construct($config);
}
function init()
{
parent::init();
}
}
<?php
namespace app\components\ABCDEFG\DevicesLimit;
use Yii;
use yii\base\Component;
use app\models\UserManagement\User;
/**
* Logged In Devices Storage Helper
*
* Class LoginDevicesLimit
* #package app\components\ABCDEFG\DevicesLimit
*
* #property integer $iDevicesLimit
*
*/
class LoginDevicesLimit extends Component
{
private int $iDevicesLimit = 1;
public function init()
{
parent::init();
}
public function getDevicesLimit() : int
{
return $this->iDevicesLimit;
}
public function setDevicesLimit(int $iDevicesLimit = 1) : self
{
if($iDevicesLimit >= 1)
{
$this->iDevicesLimit = $iDevicesLimit;
}
return $this;
}
public function sessionManager() : self
{
if(isset(Yii::$app->user->id))
{
$oUser = User::findOne(Yii::$app->user->id);
if(!empty($oUser))
{
if(User::hasRole('Expert', $superAdminAllowed = false))
{
$this->setDevicesLimit(3);
}
$sSessionsJSON = $oUser->sessions;
$aSessions = json_decode($sSessionsJSON, true);
$aSession = Helper::getCurrentSessionData();
if(is_array($aSessions) && !empty($aSessions))
{
$bIsSessionExists = false;
foreach($aSessions as $iSessionKey => $aSessionData)
{
if($aSessionData['id'] == $aSession['id'])
{
$aSessions[$iSessionKey] = $aSession;
$bIsSessionExists = true;
break;
}
}
if($bIsSessionExists == true)
{
$aTime = array_column($aSessions, 'time');
array_multisort
(
$aTime, SORT_NUMERIC, SORT_ASC,
$aSessions
);
}
else
{
array_unshift($aSessions, $aSession);
}
}
else
{
$aSessions[] = $aSession;
}
$aSessions = array_slice($aSessions, 0, $this->getDevicesLimit());
$sSessionsJSON = json_encode($aSessions);
if(json_last_error() == JSON_ERROR_NONE)
{
$oUser->sessions = $sSessionsJSON;
$oUser->save();
}
}
}
return $this;
}
}
<?php
namespace app\components\ABCDEFG\DevicesLimit;
use Yii;
use yii\base\Component;
use app\models\UserManagement\User;
/**
* Logged In Devices Storage Helper
*
* Class LogoutDevices
* #package app\components\ABCDEFG\DevicesLimit
*
*/
class LogoutDevices extends Component
{
public function init()
{
parent::init();
self::logoutOnExceedingLimit();
}
public static function logoutOnExceedingLimit()
{
$xRetValue = NULL;
if(Yii::$app->request->isAjax == false && isset(Yii::$app->user->id))
{
$oUser = User::findOne(Yii::$app->user->id);
if(!empty($oUser))
{
$sSessionsJSON = $oUser->sessions;
$aSessions = json_decode($sSessionsJSON, true);
$aSession = Helper::getCurrentSessionData();
if(is_array($aSessions) && !empty($aSessions))
{
$bIsSessionExists = in_array($aSession['id'], array_column($aSessions, 'id'));
if($bIsSessionExists == false)
{
Yii::$app->session->setFlash('devices-limit', true);
Yii::$app->session->close();
Yii::$app->user->logout(false);
$xRetValue = Yii::$app->response->redirect(['site/devices-limit-reached'], 302);
}
}
}
}
return $xRetValue;
}
}
<?php
namespace app\components\ABCDEFG\DevicesLimit;
use Yii;
use yii\base\Component;
/**
* Helper
*
* Class Helper
* #package app\components\ABCDEFG\DevicesLimit
*
*/
class Helper extends Component
{
public function init()
{
parent::init();
}
public static function getCurrentSessionData() : array
{
return
[
'id' => Yii::$app->session->getId(),
'time' => time(),
'device' => Yii::$app->request->userAgent,
];
}
}
i have followed https://gist.github.com/xrstf/b48a970098a8e76943b9 to integrate nutch and elastic-search. everything is working fine data is stored in Hbase 'webpage' table but i am not able to fetch data in elastic search.i want to know how to fetch data in elastic search.
below is my code
package com.process;
/*
import package will be here
*/
public class HbaseToElastic extends Configured implements
org.apache.hadoop.util.Tool {
static class Mapper extends TableMapper<Text, IndexWritable> {
public static String CLUSTER;
public static String SEARCH_HOST;
public static String SEARCH_PORT;
public static String SEARCH_INDEX_NAME;
public static String SEARCHtYPE;
public static int BULKSIZE;
public static String TABLENAME;
public static String FAMILY;
private static List<String> SPORTS_KEYWORDS;
private static List<String> BUSINESS_KEYWORDS;
private static List<String> GOSSIP_KEYWORDS;
private static List<String> CRIME_KEYWORDS;
private static Map<String, Map<String, String>> STATE_MAP = new HashMap<String, Map<String, String>>();
private static Map<String, String> CITY_MAP = new HashMap<String, String>();
private static Mapper mapper = new Mapper();
static {
try {
System.out.println("done1");
DetectorFactory.loadProfile("./profiles");
System.out.println("done2");
} catch (final LangDetectException e) {
System.out.println("done3");
e.printStackTrace();
}
}
Configuration hbaseConf = null;
HTable table = null;
List<Put> hbasePutErrorList = new ArrayList<Put>();
/**
* Clean up the hbase table object
*/
#Override
protected void cleanup(final Context context) throws IOException,
InterruptedException {
super.cleanup(context);
table.put(hbasePutErrorList);
table.close();
hbasePutErrorList.clear();
}
/**
* Initialize various variables
*/
#Override
protected void setup(
final org.apache.hadoop.mapreduce.Mapper<ImmutableBytesWritable, Result, Text, IndexWritable>.Context context)
throws IOException, InterruptedException {
final Configuration conf = context.getConfiguration();
CLUSTER = conf.get("cluster");
SEARCH_HOST = conf.get("search_host");
SEARCH_PORT = conf.get("search_port");
SEARCH_INDEX_NAME = conf.get("search_index_name");
SEARCHtYPE = conf.get("search_type");
BULKSIZE = conf.getInt("search_bulk_size", 500);
TABLENAME = conf.get("table_name");
FAMILY = conf.get("family");
hbaseConf = HBaseConfiguration.create();
hbaseConf.set("hbase.zookeeper.quorum",
conf.get("hbase.zookeeper.quorum"));
hbaseConf.set("hbase.zookeeper.property.clientPort",
conf.get("hbase.zookeeper.property.clientPort"));
hbaseConf.set("hbase.rpc.timeout", conf.get("hbase.rpc.timeout"));
hbaseConf.set("hbase.regionserver.lease.period",
conf.get("hbase.regionserver.lease.period"));
hbaseConf.set("hbase.master", conf.get("hbase.master"));
table = new HTable(hbaseConf, conf.get("table_name"));
SPORTS_KEYWORDS = new ArrayList<String>();
BUSINESS_KEYWORDS = new ArrayList<String>();
GOSSIP_KEYWORDS = new ArrayList<String>();
CRIME_KEYWORDS = new ArrayList<String>();
String keywrods = conf.get("sportskeywords");
String[] keyarr = keywrods.split(",");
for (final String key : keyarr) {
SPORTS_KEYWORDS.add(key.trim());
}
keywrods = conf.get("businesskeywords");
keyarr = keywrods.split(",");
for (final String key : keyarr) {
BUSINESS_KEYWORDS.add(key.trim());
}
keywrods = conf.get("gossipkeywords");
keyarr = keywrods.split(",");
for (final String key : keyarr) {
GOSSIP_KEYWORDS.add(key.trim());
}
keywrods = conf.get("crimekeywords");
keyarr = keywrods.split(",");
for (final String key : keyarr) {
CRIME_KEYWORDS.add(key.trim());
}
final String stateMap = conf.get("statemap");
final Gson g = new Gson();
STATE_MAP = g.fromJson(stateMap, Map.class);
}
/**
* map function
*/
#Override
public void map(final ImmutableBytesWritable row, final Result result,
final Context context) throws IOException, InterruptedException {
try {
final byte b = 0;
int deleteFlag = 0;
final String keyString = Bytes.toString(row.get());
final Map<String, Object> mapobject = new HashMap<String, Object>();
for (final KeyValue kv : result.raw()) {
final String key = (new String(kv.getQualifier()));
final String value = (new String(kv.getValue()));
mapobject.put(key, value);
}
final Gson g = new Gson();
if (checkValidType(mapobject)) {
refineMetaTags(mapobject);
if (refineDescription(mapobject)) {
assignCity(mapobject);
if (checkTitleImage(mapobject)) {
if (setLang(mapobject)) {
setCorrectCategory(mapobject);
correctDuplicateTitle(mapobject);
final String json = g.toJson(mapobject);
context.write(new Text(keyString),
new IndexWritable(json, b));
deleteFlag = 1;
}
}
}
}
if (deleteFlag == 0) {
final Put put = new Put(Bytes.toBytes(keyString));
put.add(Bytes.toBytes("cf"), Bytes.toBytes("ErrorFlag"),
Bytes.toBytes("1"));
hbasePutErrorList.add(put);
}
} catch (final Exception e) {
e.printStackTrace();
}
}
/**
* Remove duplicate statement in the title
*
* #param mapobject
*/
private void correctDuplicateTitle(final Map<String, Object> mapobject) {
final String duplicateTitle = mapobject.get("title").toString();
final String stripedTitleArr[] = duplicateTitle.split(" ", 4);
if (stripedTitleArr.length == 4) {
final String subString = stripedTitleArr[0] + " "
+ stripedTitleArr[1] + " " + stripedTitleArr[2];
if (stripedTitleArr[3].contains(subString)) {
mapobject.put("title", duplicateTitle
.substring(duplicateTitle.indexOf(subString,
subString.length() - 1)));
mapobject.put("title", stripedTitleArr[3]
.substring(stripedTitleArr[3].indexOf(subString)));
}
}
}
/**
* Set category based on the various category specific keyword
*
* #param mapobject
*/
private void setCorrectCategory(final Map<String, Object> mapobject) {
final String url = mapobject.get("url") + "";
final String cat = mapobject.get("tags") + "";
if ("sports".equalsIgnoreCase(cat)
|| "cricket".equalsIgnoreCase(cat)) {
if (!(url.toLowerCase().contains("sport")
|| url.toLowerCase().contains("खेल")
|| url.toLowerCase().contains("cric") || url
.toLowerCase().contains("क्रिकेट"))) {
final String desc = mapobject.get("description").toString();
boolean isSports = false;
int count = 0;
for (final String keyword : SPORTS_KEYWORDS) {
if (desc.contains(keyword)) {
count++;
}
}
if (count > 1) {
isSports = true;
}
if (!isSports) {
mapobject.put("tags", "national");
}
if (isSports
&& (desc.contains("क्रिकेट")
|| url.toLowerCase().contains("cric")
|| desc.contains("टॉस")
|| desc.contains("वनडे") || desc
.contains("बल्लेबाज"))) {
mapobject.put("tags", "cricket");
}
}
} else if ("business".equalsIgnoreCase(cat)) {
if ((url.toLowerCase().contains("sport") || url.toLowerCase()
.contains("खेल"))) {
mapobject.put("tags", "sports");
} else if (url.toLowerCase().contains("cric")
|| url.toLowerCase().contains("क्रिकेट")) {
mapobject.put("tags", "cricket");
} else if (!(url.toLowerCase().contains("busines")
|| url.toLowerCase().contains("व्यापार")
|| url.toLowerCase().contains("economy")
|| url.toLowerCase().contains("finance")
|| url.toLowerCase().contains("बिजनेस")
|| url.toLowerCase().contains("market")
|| url.toLowerCase().contains("karobar") || url
.contains("कारोबार"))) {
final String desc = mapobject.get("description").toString();
int count = 0;
for (final String keyword : BUSINESS_KEYWORDS) {
if (desc.contains(keyword)) {
count++;
}
}
if (count < 2) {
mapobject.put("tags", "national");
}
}
} else if ("gossip".equalsIgnoreCase(cat)) {
if ((url.toLowerCase().contains("sport") || url.toLowerCase()
.contains("खेल"))) {
mapobject.put("tags", "sports");
} else if (url.toLowerCase().contains("cric")
|| url.toLowerCase().contains("क्रिकेट")) {
mapobject.put("tags", "cricket");
} else if (url.toLowerCase().contains("busines")) {
mapobject.put("tags", "business");
} else if (!(url.toLowerCase().contains("masala")
|| url.toLowerCase().contains("gossip")
|| url.toLowerCase().contains("gupshup") || url
.toLowerCase().contains("garam"))) {
final String desc = mapobject.get("description").toString();
int count = 0;
for (final String keyword : GOSSIP_KEYWORDS) {
if (desc.contains(keyword)) {
count++;
}
}
if (count < 2) {
mapobject.put("tags", "national");
}
}
} else if ("crime".equalsIgnoreCase(cat)) {
if ((url.toLowerCase().contains("sport") || url.toLowerCase()
.contains("खेल"))) {
mapobject.put("tags", "sports");
} else if (url.toLowerCase().contains("cric")
|| url.toLowerCase().contains("क्रिकेट")) {
mapobject.put("tags", "cricket");
} else if (url.toLowerCase().contains("busines")) {
mapobject.put("tags", "business");
} else if (!(url.toLowerCase().contains("crime")
|| url.toLowerCase().contains("terrorist")
|| url.toLowerCase().contains("abuse")
|| url.toLowerCase().contains("forgery")
|| url.toLowerCase().contains("assault")
|| url.toLowerCase().contains("violence")
|| url.toLowerCase().contains("rape")
|| url.toLowerCase().contains("teasing")
|| url.toLowerCase().contains("molestation")
|| url.toLowerCase().contains("scandal") || url
.toLowerCase().contains("murder"))) {
final String desc = mapobject.get("description").toString();
int count = 0;
for (final String keyword : CRIME_KEYWORDS) {
if (desc.contains(keyword)) {
count++;
}
}
if (count < 2) {
mapobject.put("tags", "national");
}
}
} else if (cat != null && cat.startsWith("local")) {
}
}
/**
* Check valid type of the HTML pages
*
* #param mapobject
* #return
*/
private boolean checkValidType(final Map<String, Object> mapobject) {
if (mapobject.containsKey("type")
&& !(mapobject.get("type").toString().contains("image") || mapobject
.get("type").toString().contains("rss"))) {
return true;
}
return false;
}
/**
* refine the description according to its length and must starting with
* english and it the description is not present get the description
* from the metatags description
*
* #param mapobject
* #return {#link Boolean}
*/
private boolean refineDescription(final Map<String, Object> mapobject) {
if (mapobject.containsKey("description")
&& mapobject.get("description").toString().length() > 75
&& !mapobject.get("description").toString().contains(";}")
&& !mapobject.get("description").toString()
.contains("<cite>")
&& !mapobject.get("description").toString()
.contains("href=")
&& !mapobject.get("description").toString()
.contains("All rights reserved")) {
return true;
} else if (mapobject.containsKey("metatag.description")
&& mapobject.get("metatag.description").toString().length() > 75
&& !mapobject.get("metatag.description").toString()
.contains(";}")
&& !mapobject.get("metatag.description").toString()
.contains("<cite>")) {
mapobject.put("description",
mapobject.get("metatag.description"));
return true;
}
return false;
}
/**
* refine metatags by refining meta keyword to only include the English
* keyword only that has at most three keyword and if not present then
* create the keyword with title field of the html and if none of the
* keyword found then form it using the help of the url and exclude the
* number from the keywords
*
* #param mapobject
*/
private void refineMetaTags(final Map<String, Object> mapobject) {
String metaTag = "";
int tagFlag = 0;
if (mapobject.containsKey("metatag.keywords")) {
final String metaTags[] = mapobject.get("metatag.keywords")
.toString().replaceAll("\\|", ",").split(",");
String domain = null;
StringBuilder temp = null;
for (final String metaTag2 : metaTags) {
if (mapobject.containsKey("host")) {
domain = mapobject.get("host") + "";
if (domain.split("\\.").length > 1
&& (metaTag2
.contains(domain.split("\\.")[domain
.split("\\.").length - 2]) || metaTag2
.contains(domain.split("\\.")[0])))
{
continue;
}
}
String[] arr = metaTag2.split(" ");
arr = removeUnicodeWords(arr);
if (arr.length > 0 && arr.length < 5) {
temp = new StringBuilder();
for (final String str : arr) {
temp.append(str);
temp.append(" ");
}
if (metaTag.length() + temp.length() < 70) {
metaTag = metaTag + "," + temp.toString();
}
}
}
if (metaTag.startsWith(",")) {
metaTag = metaTag.trim();
metaTag = metaTag.substring(1, metaTag.length());
}
}
if (metaTag.length() < 1 && mapobject.containsKey("title")) {
/**
* Extracting tags from the title tag if the length of the
* keyword is greater than 4
*/
final String title = (String) mapobject.get("title");
final String splitTitle[] = title.split(" ");
int count = 0;
for (int i = 0; i < splitTitle.length; i++) {
if (splitTitle[i].length() > 4
&& !splitTitle[i].matches("^[\\u0900-\\u097F].*")) {
metaTag = metaTag + splitTitle[i] + ",";
count++;
if (count == 5) {
break;
}
}
}
if (metaTag.split(",").length > 3) {
if (metaTag.endsWith(",")) {
metaTag = metaTag.trim();
metaTag = metaTag.substring(0, metaTag.length() - 1);
}
} else {
metaTag = "";
}
}
if (metaTag.length() < 1) {
/**
* Extracting the tags from the url if the length of the keyword
* is greater than 4
*/
final String splitUrl[] = mapobject.get("url").toString()
.split("/");
final String lastSplitValue = splitUrl[splitUrl.length - 1];
final String tagList[] = generateTokens(lastSplitValue);
if (tagList != null) {
int count = 0;
for (int i = 0; i < tagList.length; i++) {
if (tagList[i].length() > 4
&& !tagList[i].matches("^[\\u0900-\\u097F].*")) {
metaTag = metaTag + tagList[i] + ",";
count++;
if (count == 5) {
break;
}
}
}
}
if (metaTag.endsWith(",")) {
metaTag = metaTag.trim();
metaTag = metaTag.substring(0, metaTag.length() - 1);
}
}
if (metaTag.length() > 0) {
metaTag = metaTag.replaceAll("\\[", "");
metaTag = metaTag.replaceAll("\"", "");
metaTag = metaTag.replaceAll(";", "");
metaTag = metaTag.replaceAll(":", "");
metaTag = metaTag.replaceAll("\u0027", "");
metaTag = metaTag.replaceAll("\u003d", "");
metaTag = metaTag.replaceAll("\u0026", "");
tagFlag = 1;
}
mapobject.put("TagFlag", tagFlag);
mapobject.put("metatag.keywords", metaTag);
}
/**
* Remove unicode character
*
* #param arr
* #return
*/
private String[] removeUnicodeWords(final String[] arr) {
final List<String> returnArr = new ArrayList<String>();
for (final String str : arr) {
if (str != null && str.trim().length() > 3
&& !str.matches("^[\\u0900-\\u097F].*")
&& !(str.matches("^[0-9].*"))) {
returnArr.add(str.trim());
}
}
final String[] retrnArr = new String[returnArr.size()];
returnArr.toArray(retrnArr);
return retrnArr;
}
/**
* Generate Token list with the help of the lucene analyzer
*
* #param lastSplitValue
* #return {#link ArrayIndexOutOfBoundsException} of the list of the
* keywords
*/
private String[] generateTokens(String lastSplitValue) {
final List<String> list = new ArrayList<String>();
lastSplitValue = lastSplitValue.replace("\\.", " ").replace("%20",
" ");
try {
final Version matchVersion = Version.LUCENE_45;
final Analyzer analyzer = new HindiAnalyzer(matchVersion);
final TokenStream ts = analyzer.tokenStream("field",
new StringReader(lastSplitValue));
ts.reset();
while (ts.incrementToken()) {
final CharTermAttribute cta = ts
.getAttribute(CharTermAttribute.class);
if (cta.toString().length() > 4
&& !cta.toString().matches("^[0-9].*")) {
list.add(cta.toString());
}
}
ts.end();
ts.close();
analyzer.close();
} catch (final Exception e) {
e.printStackTrace();
}
if (list.size() > 3) {
return list.toArray(new String[list.size()]);
} else {
return null;
}
}
/**
* Checks title and assign their language based on their first character
* of the title
*
* #param mapobject
* #return {#link Map}
*/
private boolean setLang(final Map<String, Object> mapobject) {
final String title = mapobject.get("title").toString();
final String description = mapobject.get("title").toString();
String language = "";
try {
language = mapper.detect(title);
mapper.detect(description);
} catch (final LangDetectException e) {
System.out.println("\n title with error is - " + title);
System.out.println("\n description with error is - "
+ description);
e.printStackTrace();
/*
* String title = mapobject.get("title").toString(); language =
* mapobject.get("lang") + ""; language = language.trim(); if
* (language.trim().equalsIgnoreCase("hi") ||
* language.trim().startsWith("en") ||
* language.trim().equalsIgnoreCase("lt")) { String[] titleArr =
* title.trim().split(" "); int i = 0; for (String titlePart :
* titleArr) { if
* (titlePart.trim().matches("^[\\u0900-\\u097F].*")) { i++; } }
* if (i >= titleArr.length * 0.5) { mapobject.put("lang",
* "hi"); } else { mapobject.put("lang", "lt"); } return true; }
*/
return false;
}
if (language.trim().equalsIgnoreCase("hi")
|| language.trim().startsWith("en")
|| language.trim().equalsIgnoreCase("lt")) {
mapobject.put("lang", language);
return true;
}
return false;
}
private String detect(final String text) throws LangDetectException {
final Detector detector = DetectorFactory.create();
detector.append(text);
return detector.detect();
}
/**
* Checks whether to include the doc based on their title and get the
* title from anchor tag title to choose the title that has largest
* number of the words and in hindi and it also gets the image from
* anchor tag href attribute
*
* #param mapobject
* of the key value pair
* #return {#link Boolean}
*/
private boolean checkTitleImage(final Map<String, Object> mapobject) {
final TreeSet<String> set = new TreeSet<String>(new SetSort());
final Gson gson = new Gson();
JsonArray array = null;
JsonObject object2 = null;
if (mapobject.containsKey("anchor")
&& mapobject.get("anchor") != null) {
final String arr = (String) mapobject.get("anchor");
try {
array = gson.fromJson(arr, JsonArray.class);
for (final JsonElement jsonElement : array) {
try {
object2 = gson.fromJson(jsonElement.getAsString(),
JsonObject.class);
} catch (final Exception e) {
if (object2 == null) {
object2 = new JsonObject();
object2.addProperty("title",
jsonElement.getAsString());
object2.addProperty("href", "");
object2.addProperty("alt", "");
}
}
if (object2 != null) {
assignTitleImage(mapobject, set, object2);
}
object2 = null;
}
} catch (final ClassCastException e) {
object2 = gson.fromJson(arr, JsonObject.class);
assignTitleImage(mapobject, set, object2);
} catch (final Exception e) {
e.printStackTrace();
}
if (!set.isEmpty()) {
int loop = 0;
final List<String> tempList = new LinkedList<String>();
for (final String string : set) {
final String title = string;
tempList.add(title.trim());
loop++;
if (loop == 2) {
break;
}
}
if (!tempList.isEmpty()) {
if (tempList.get(0).matches("^[\\u0900-\\u097F].*")) {
mapobject.put("title", tempList.get(0));
} else if (tempList.size() > 1
&& !(tempList.get(0)
.matches("^[\\u0900-\\u097F].*"))
&& tempList.get(1).matches(
"^[\\u0900-\\u097F].*")) {
mapobject.put("title", tempList.get(1));
} else {
mapobject.put("title", tempList.get(0));
}
}
}
}
if (mapobject.containsKey("title")
&& mapobject.get("title").toString().length() > 0
&& mapobject.get("title").toString().split(" ").length > 2
&& mapobject.get("title").toString().split(" ").length < 20
&& !mapobject.get("title").toString().contains("<")) {
if (set.isEmpty()) {
mapobject.put("title",
getTitleRefined(mapobject.get("title") + ""));
}
return true;
}
return false;
}
/**
* #param mapobject
* #param set
* #param object2
*/
private void assignTitleImage(final Map<String, Object> mapobject,
final TreeSet<String> set, final JsonObject object2) {
if (!mapobject.containsKey("ImgH1")
&& !mapobject.containsKey("ImgH2")) {
if (object2.get("href") != null
&& object2.get("href").getAsString().length() > 0
&& (object2.get("href").getAsString().toLowerCase()
.contains(".jpg")
|| object2.get("href").getAsString()
.toLowerCase().contains(".jpeg") || object2
.get("href").getAsString().toLowerCase()
.contains(".gif"))) {
putImages(mapobject, object2.get("href").getAsString()
.trim(), mapobject.get("tags").toString().trim()
.toLowerCase());
}
}
if (object2.get("title") != null
&& object2.get("title").getAsString().length() > 0
&& object2.get("title").getAsString().split(" ").length > 2
&& object2.get("title").getAsString().split(" ").length < 20
&& !object2.get("title").getAsString().contains("<")) {
final String newTitle = getTitleRefined(object2.get("title")
.getAsString());
set.add(newTitle.trim());
}
}
/**
* This function used to refine the title based on specific bad keyword
* during observation
*
* #param title
* #return refined title
*/
private String getTitleRefined(String title) {
title = title.replaceAll("\u0027", "");
title = title.replaceAll("\u0026", "");
title = title.replaceAll("\u003d", "");
if (title.contains("-")) {
if (title.trim().split("-").length > 1
&& !title.trim().split("-")[1].trim().matches(
"^[\\u0900-\\u097F].*")) {
return title.trim().split("-")[0].trim();
}
} else if (title.contains(":")) {
if (!title.trim().split(":")[0].trim().matches(
"^[\\u0900-\\u097F].*")
&& title.trim().split(":").length > 1) {
return title.trim().split(":")[1].trim();
}
}
return title;
}
/**
* Creates the path for the images
*
* #param map
* of the key value pair
* #param imageUrl
* #param category
*/
private void putImages(final Map<String, Object> map2,
final String imageUrl, final String category) {
try {
map2.put("ImgSrc", StringEscapeUtils.unescapeHtml(imageUrl)
.trim());
if (map2.containsKey("ImgSrc") && map2.get("ImgSrc") != null
&& map2.get("ImgSrc").toString().length() > 0) {
map2.put(
"ImgSrc",
StringEscapeUtils.unescapeHtml(map2.get("ImgSrc")
.toString())
+ "##RAFTAAR##"
+ imageUrl.trim());
} else {
return;
}
String imgNamearr[] = null;
try {
imgNamearr = imageUrl.split("/");
} catch (final Exception e) {
e.printStackTrace();
}
String imgName = null;
try {
imgName = imgNamearr[imgNamearr.length - 1];
} catch (final Exception e) {
e.printStackTrace();
}
final String imagePath = "/"
+ String.valueOf(imgName.charAt(0));
imgName = imgName.replaceAll(" ", "_").replaceAll("%20", "_");
if (imgName.split(".jpg").length > 0) {
imgName = imgName.split(".jpg")[0];
imgName = imgName + ".jpg";
}
map2.put("ImgH1", "h1/" + category + imagePath + "/" + imgName);
map2.put("ImgH2", "h2/" + category + imagePath + "/" + imgName);
} catch (final Exception e) {
e.printStackTrace();
}
}
/**
* Inserts the data to the elasticsearch
*
* #param mapobject
* #param key
* unique id generally it is the unique url
*/
public static void insertToElastic(final Map<String, Object> mapobject,
final String key) {
final Settings settings = ImmutableSettings.settingsBuilder()
.put("cluster.name", CLUSTER).build();/*
* change ccluster.name
* to cluster
*/
final Client client = new TransportClient(settings)
.addTransportAddress(new InetSocketTransportAddress(
SEARCH_HOST, Integer.parseInt(SEARCH_PORT)));
client.prepareIndex(SEARCH_INDEX_NAME, SEARCHtYPE, key)
.setSource(mapobject).execute().actionGet();
client.close();
}
/**
* Assign the city to the news without city
*
* #param category
* #param description
* #return update category with city
*/
private static void assignCity(final Map<String, Object> mapobject) {
String category = mapobject.get("tags").toString();
if (category.endsWith("/")) {
boolean flag = true;
final String catArr[] = category.split("/");
if (catArr.length == 2) {
final String state = catArr[1];
CITY_MAP = STATE_MAP.get(state);
for (final Entry<String, String> e : CITY_MAP.entrySet()) {
final String description = mapobject.get("description")
.toString();
if (description.contains(e.getValue())) {
category = category + e.getKey();
mapobject.put("tags", category);
flag = false;
break;
}
}
}
if (flag) {
mapobject.put("tags", "national");
}
}
}
}
/**
* Update the data to hbase
*
* #param tableName
* #param rowKey
* #param family
* #param qualifier
* #param value
* #param conf
*/
public static void updateIntoHbase(final String tableName,
final String rowKey, final String family, final String qualifier,
final String value, final Configuration conf) {
HTable table = null;
try {
table = new HTable(conf, tableName);
} catch (final IOException e) {
e.printStackTrace();
}
final Put put = new Put(Bytes.toBytes(rowKey));
put.add(Bytes.toBytes(family), Bytes.toBytes(qualifier),
Bytes.toBytes(value));
try {
table.put(put);
table.close();
} catch (final IOException e) {
e.printStackTrace();
}
}
/**
* Return the map of the all states and city
*
* #param fileName
* #return
*/
private static Map<String, Map<String, String>> returnMap(
final String fileName) {
final Map<String, Map<String, String>> map = new HashMap<String, Map<String, String>>();
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(fileName));
String line;
while ((line = br.readLine()) != null) {
final String arr[] = line.split("\t", 3);
if (arr.length == 3) {
if (map.containsKey(arr[0])) {
Map<String, String> m = new HashMap<String, String>();
m = map.get(arr[0]);
m.put(arr[1], arr[2]);
} else {
final Map<String, String> m = new HashMap<String, String>();
m.put(arr[1], arr[2]);
map.put(arr[0], m);
}
}
}
} catch (final FileNotFoundException e) {
e.printStackTrace();
} catch (final IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (final Exception e) {
e.printStackTrace();
}
}
}
return map;
}
public static void main(final String[] args) throws Exception {
int c = 0;
c = ToolRunner.run(new Configuration(), new HbaseToElastic(), args);
System.exit(c);
}
}
I am trying to port a Java application for use in node.js, and am running into an issue with Object inheritance. I have a base object HVal, and 2 subclasses, HBin and HBool. When I try to use both HBin and HBool, the 1st object that is loaded is overridden by the 2nd object that is loaded, even though they are being assigned to different vars. Anyone have an idea of what is going on here.
HVal.js
/** Package private constructor */
function HVal() {};
/** Abstract functions that must be defined in inheriting classes
* hashCode: int - Hash code is value based
* toZinc: String - Encode value to zinc format
* equals: boolean - Equality is value based
*/
/** String - String format is for human consumption only */
HVal.prototype.toString = function() { return this.toZinc(); };
/** int - Return sort order as negative, 0, or positive */
HVal.prototype.compareTo = function(that) { return this.toString().localeCompare(that); };
/** boolean - check for type match */
HVal.prototype.typeis = function (check, prim, obj) { return typeof(check)==prim || check instanceof obj; };
/** Add hashCode function to Javascript String object */
String.prototype.hashCode = function() {
var hash = 0, i, chr, len;
if (this.length == 0) return hash;
for (i = 0, len = this.length; i < len; i++) {
chr = this.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
};
/** Export for use in other modules */
module.exports = new HVal();
HBin.js
var hval = require('./HVal');
/** Private constructor */
function HBin(mime) {
/** MIME type for binary file */
this.mime = mime;
};
HBin.prototype = hval;
/** Construct for MIME type */
HBin.prototype.make = function(mime) {
if (!hval.typeis(mime, 'string', String) || mime.length == 0 || mime.indexOf('/') < 0)
throw new Error("Invalid mime val: \"" + mime + "\"");
return new HBin(mime);
};
/** int - Hash code is based on mime field */
HBin.prototype.hashCode = function() { return mime.hashCode(); };
/** String - Encode as "Bin(<mime>)" */
HBin.prototype.toZinc = function() {
var s = "Bin(";
for (var i=0; i<this.mime.length; ++i)
{
var c = this.mime.charAt(i);
if (c > 127 || c == ')') throw new Error("Invalid mime, char='" + c + "'");
s += c;
}
s += ")";
return s.toString();
};
/** boolean - Equals is based on mime field */
HBin.prototype.equals = function(that) {
if (!typeOf(that) == HBin) return false;
return this.mime === that.mime;
};
/** Export for use in other modules */
module.exports = new HBin();
HBool.js
var hval = require('./HVal');
/** Private constructor */
function HBool(val) {
/** Boolean value */
this.val = val;
};
HBool.prototype = hval;
/** Construct from boolean value */
HBool.prototype.make = function(val) {
if (!hval.typeis(val, 'boolean', Boolean))
throw new Error("Invalid boolean val: \"" + val + "\"");
return new HBool(val);
};
/** int - Hash code is same as java.lang.Boolean */
HBool.prototype.hashCode = function() { return this.val ? 1231 : 1237; };
/** String - Encode as T/F */
HBool.prototype.toZinc = function() { return this.val ? "T" : "F"; };
/** boolean - Equals is based on reference */
HBool.prototype.equals = function(that) { return this === that; };
/** String - String format is for human consumption only */
HBool.prototype.toString = function() { return this.val ? "true" : "false"; };
/** Export for use in other modules */
module.exports = new HBool();
index.js
var hbin = require('./HBin');
var hbool = require('./HBool');
console.log('BIN: ' + hbin.make("test/test").toString());
console.log();
console.log('T: ' + hbool.make(true).toString());
console.log('F: ' + hbool.make(false).toString());
Output - failing on first console.log
HBool.js:19
throw new Error("Invalid boolean val: \"" + val + "\"");
Error: Invalid boolean val: "test/test"
at HVal.HBool.make (HBool.js:19:11)
at Object.<anonymous> (index.js:4:28)
...
The issue has to do with how you're exporting from a module and how you're assigning the prototype when you want to inherit and the issue is subtle. There are a couple things going on here. First off, module.exports is cached for modules, so every time you do:
var hval = require('./HVal');
you are getting the exact same HVal instantiated object back each time and you can never make a different object because you didn't export the constructor. That is a problem for all your modules. You should export the constructor function and let the user of the module actually create new instances of your object with new.
You can do that by changing:
module.exports = new HVal();
to:
module.exports = HVal;
And, then when you require() it, you just get the constructor:
var HVal = require('./HVal');
var HBool = require('./HBool');
And, then you can create an HBool object like this:
var hbool = new HBool();
This issue then seems to be messing up your inheritance when you assign something like:
HBool.prototype = hval;
The problem is entirely fixed if you export the constructors themselves and then change the above prototype assignment to use Object.create:
HBool.prototype = Object.create(HVal.prototype);
You can see a working demo here (modules removed to make the demo easier to show): http://jsfiddle.net/jfriend00/ty5wpkqm/
I also made another correction to the code. Instead of this:
if (!hval.typeis(mime, 'string', String) || mime.length == 0 || mime.indexOf('/') < 0)
I changed it to actually use the inherited methods on this object:
if (!this.typeis(mime, 'string', String) || mime.length == 0 || mime.indexOf('/') < 0)
This is the proper way to call methods on the current object (even inherited methods). Now, this happens to be a static method (it doesn't use the instance at all so you could move it off the object entirely, but since you have declared it on the object, you should refer to it as this.typeis().
I also noticed that your .equals() method is not correct. You have this:
/** boolean - Equals is based on mime field */
HBin.prototype.equals = function(that) {
if (!typeOf(that) == HBin) return false;
return this.mime === that.mime;
};
First off, did you create a new global function called typeOf()? The built-in mechanism in Javascript is lowercase typeof. Second off, typeof(that) will never be HBin. Objects in Javascript don't report a type like that. An object would report typeof(that) === "object". You can perhaps use instanceof:
/** boolean - Equals is based on mime field */
HBin.prototype.equals = function(that) {
return that instanceof HBin && this.mime === that.mime;
};