diff --git a/README.md b/README.md index 845043e..793c91e 100644 --- a/README.md +++ b/README.md @@ -18,38 +18,50 @@ Content内容如果为JSON将会自动格式化显示
![image](https://www.darkal.cn/imgd.php?src=/2016/09/230686663947787928.jpg&width=350)
分享功能将抓包生成的所有数据包打包为har文件并压缩为zip,支持分享到微信、QQ等

-2. 环境切换
+2. 返回包注入
+支持修改流量返回包(该版本暂时只支持http的修改)
+![image](https://www.darkal.cn/imgd.php?src=/2016/09/WechatIMG180.jpeg&width=350) +![image](https://www.darkal.cn/imgd.php?src=/2016/09/WechatIMG181.jpeg&width=350)

+ +3. 环境切换
支持切换模拟为微信、手Q,默认为普通浏览器。
![image](https://www.darkal.cn/imgd.php?src=/2016/09/WechatIMG81.jpeg&width=350)

-3. 多样性输入:导航、地址栏、扫一扫、schema呼起
+4. 多样性输入:导航、地址栏、扫一扫、schema呼起
支持地址栏直接输入地址,扫扫描二维码,以及schema呼起app并打开目标页面。
schema的协议格式为:jdhttpmonitor://webview?param={'url'='http://www.darkal.cn'}

-4. Host配置
+5. Host配置
可以配置各域名的host
![image](https://www.darkal.cn/imgd.php?src=/2016/09/WechatIMG79.jpeg&width=350&t=1)

-5. 查看console.log日志
+6. 查看console.log日志
![image](https://www.darkal.cn/imgd.php?src=/2016/09/WechatIMG82.jpeg&width=350)

-6. 网络工具
+7. 网络工具
目前AndroidHttpCapture集成了常见的网络工具,如dns,ping,以及设备信息
![image](https://www.darkal.cn/imgd.php?src=/2016/09/621495078826.jpg&width=350)

-7. 设置系统代理,监听其他app请求包
+8. 设置系统代理,监听其他app请求包
当将用户手机的代理服务器设置为127.0.0.1:8888时,可以对其他app(例如微信)的HTTP数据进行抓包
(此时AndroidHttpCapture就是一个手机上的fiddler)
![image](https://www.darkal.cn/imgd.php?src=/2016/09/WechatIMG80.jpeg&width=350)

- + ### Q & A
1. 分享的http包如何查看和分析?
   分享的文件解压后为.har文件,可以通过fiddler方式或者在线工具进行分析。
Fiddler方式需要先将包导到电脑上,然后使用fiddler导入该包:Import Sessions->Select Import Format ->HTTPArchive ->选择包,即可
在线工具外网:http://h5.darkal.cn/har/ 只需要将包拖入此工具即可分析

- + +### 已知BUG
+1. 信任所有的服务器证书不做校验
+2. 开启返回包注入功能后,https返回的部分页面存在 err_CONTENT_LENGTH_MISMATCH 错误
+ +#### 如果觉得工具好用的话请多多star以及Pull requests
支持我喝杯咖啡请扫描下面的二维码,谢谢(ง •̀_•́)ง
+![image](http://h5.darkal.cn/har/guide/img/code.jpg)

+ ### 致谢
AndroidHttpCapture基于Netty、browsermob-proxy来实现核心抓包的功能
Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients.
@@ -58,8 +70,7 @@ https://github.com/netty/netty
A free utility to help web developers watch and manipulate network traffic from their AJAX applications.
https://github.com/lightbody/browsermob-proxy
-修改了多处browsermob-proxy的源码适配Android系统
-#### 目前遗留了一个Bug:信任所有的服务器证书不做校验

+修改了多处browsermob-proxy的源码适配Android系统

MIT License
Copyright (c) 2016 AndroidHttpCapture diff --git a/app/build.gradle b/app/build.gradle index b7d13ca..7239ec9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,8 +25,8 @@ android { applicationId "cn.darkal.networkdiagnosis" minSdkVersion 14 targetSdkVersion 22 - versionCode 25 - versionName "2.4.26" + versionCode 30 + versionName "3.1.30" // Enabling multidex support. multiDexEnabled true resConfigs "zh" @@ -76,10 +76,11 @@ dependencies { compile 'com.android.support:support-annotations:24.2.1' compile 'com.android.support:multidex:1.0.1' // littleproxy及其依赖 - compile('net.lightbody.bmp:littleproxy:1.1.0-beta-bmp-13') { + compile('net.lightbody.bmp:littleproxy:1.1.0-beta-bmp-16') { exclude group: 'io.netty' } - compile 'net.sf.qualitycheck:quality-check:1.3' + + compile group: 'org.apache.directory.studio', name: 'org.apache.commons.io', version: '2.4' compile 'javax.annotation:jsr250-api:1.0' compile 'com.fasterxml.jackson.core:jackson-core:2.7.6' compile 'com.fasterxml.jackson.core:jackson-databind:2.7.6' @@ -92,10 +93,12 @@ dependencies { compile 'com.google.jimfs:jimfs:1.1' compile 'com.jcraft:jzlib:1.1.3' // 日志处理 - compile 'org.slf4j:slf4j-api:1.7.21' +// compile 'org.slf4j:slf4j-api:1.7.21' + compile 'org.slf4j:slf4j-log4j12:1.7.21' + // compile 'org.slf4j:jcl-over-slf4j:1.7.21' // 启用Netty的日志输出(调试用) - // compile 'com.noveogroup.android:android-logger:1.3.5' +// compile 'com.noveogroup.android:android-logger:1.3.5' // 文件上传插件 compile 'net.gotev:uploadservice:3.0.3' // Bugly上报 diff --git a/app/libs/netty-android.jar b/app/libs/netty-all-android-4.0.44.Final.jar old mode 100644 new mode 100755 similarity index 51% rename from app/libs/netty-android.jar rename to app/libs/netty-all-android-4.0.44.Final.jar index 892b86f..5e356a1 Binary files a/app/libs/netty-android.jar and b/app/libs/netty-all-android-4.0.44.Final.jar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 871ddca..7b44b7b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,12 +17,12 @@ @@ -31,15 +31,17 @@ android:value="Github" /> - + @@ -64,7 +66,7 @@ android:screenOrientation="portrait" /> @@ -72,12 +74,12 @@ @@ -91,16 +93,15 @@ - + android:name=".Activity.HarDetailActivity" + android:theme="@style/AppTheme.NoActionBar" /> - + android:name=".Activity.JsonPreviewActivity" + android:theme="@style/AppTheme.NoActionBar" /> + \ No newline at end of file diff --git a/app/src/main/java/cn/darkal/networkdiagnosis/Activity/ChangeFilterActivity.java b/app/src/main/java/cn/darkal/networkdiagnosis/Activity/ChangeFilterActivity.java new file mode 100644 index 0000000..031165e --- /dev/null +++ b/app/src/main/java/cn/darkal/networkdiagnosis/Activity/ChangeFilterActivity.java @@ -0,0 +1,128 @@ +package cn.darkal.networkdiagnosis.Activity; + +import android.content.DialogInterface; +import android.support.design.widget.FloatingActionButton; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.RelativeLayout; + +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import cn.darkal.networkdiagnosis.Adapter.ContentFilterAdapter; +import cn.darkal.networkdiagnosis.Bean.ResponseFilterRule; +import cn.darkal.networkdiagnosis.R; +import cn.darkal.networkdiagnosis.SysApplication; +import cn.darkal.networkdiagnosis.Utils.DeviceUtils; +import cn.darkal.networkdiagnosis.Utils.SharedPreferenceUtils; + +public class ChangeFilterActivity extends AppCompatActivity { + @BindView(R.id.activity_change_filter) + public RelativeLayout relativeLayout; + + @BindView(R.id.lv_filter) + public ListView listView; + + @BindView(R.id.fab_add) + public FloatingActionButton floatingActionButton; + + ContentFilterAdapter contentFilterAdapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_change_filter); + ButterKnife.bind(this); + setupActionBar(); + + List ruleList = ((SysApplication)getApplication()).ruleList; + + contentFilterAdapter = new ContentFilterAdapter(this,ruleList); + listView.setAdapter(contentFilterAdapter); + + floatingActionButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showDialog(null); + } + }); + } + + /** + * Set up the {@link android.app.ActionBar}, if the API is available. + */ + private void setupActionBar() { + setTitle("返回包注入"); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + // Show the Up button in the action bar. + actionBar.setDisplayHomeAsUpEnabled(true); + } + } + + public void showDialog(final ResponseFilterRule responseFilterRule){ + AlertDialog.Builder builder = new AlertDialog.Builder(ChangeFilterActivity.this); + + View textEntryView = LayoutInflater.from(ChangeFilterActivity.this).inflate(R.layout.alert_resp_filter, null); + final EditText urlEditText = (EditText) textEntryView.findViewById(R.id.et_origin_url); + final EditText regexEditText = (EditText) textEntryView.findViewById(R.id.et_regex); + final EditText contentEditText = (EditText) textEntryView.findViewById(R.id.et_replace_result); + if(responseFilterRule!=null){ + urlEditText.setText(responseFilterRule.getUrl()); + regexEditText.setText(responseFilterRule.getReplaceRegex()); + contentEditText.setText(responseFilterRule.getReplaceContent()); + builder.setTitle("修改注入项"); + }else{ + builder.setTitle("新增注入项"); + } + + builder.setCancelable(true); + builder.setView(textEntryView); + builder.setPositiveButton("确认", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if(responseFilterRule!=null){ + responseFilterRule.setUrl(urlEditText.getText().toString()); + responseFilterRule.setReplaceRegex(regexEditText.getText().toString()); + responseFilterRule.setReplaceContent(contentEditText.getText().toString()); + }else { + if(urlEditText.getText().length()>0 && regexEditText.getText().length()>0 + && contentEditText.getText().length()>0) { + ResponseFilterRule responseFilterRule = new ResponseFilterRule(); + responseFilterRule.setUrl(urlEditText.getText().toString()); + responseFilterRule.setReplaceRegex(regexEditText.getText().toString()); + responseFilterRule.setReplaceContent(contentEditText.getText().toString()); + ((SysApplication) getApplication()).ruleList.add(responseFilterRule); + } + } + contentFilterAdapter.notifyDataSetChanged(); + } + }); + builder.setNegativeButton("取消",null); + builder.show(); + } + + @Override + protected void onStop() { + SharedPreferenceUtils.save(getApplicationContext(), + "response_filter",((SysApplication) getApplication()).ruleList); + super.onStop(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } +} diff --git a/app/src/main/java/cn/darkal/networkdiagnosis/Activity/MainActivity.java b/app/src/main/java/cn/darkal/networkdiagnosis/Activity/MainActivity.java index 781d1af..385dc4f 100644 --- a/app/src/main/java/cn/darkal/networkdiagnosis/Activity/MainActivity.java +++ b/app/src/main/java/cn/darkal/networkdiagnosis/Activity/MainActivity.java @@ -9,10 +9,12 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.preference.PreferenceManager; import android.security.KeyChain; import android.support.design.widget.NavigationView; import android.support.design.widget.Snackbar; @@ -54,10 +56,13 @@ import net.lightbody.bmp.core.har.Har; import net.lightbody.bmp.core.har.HarPage; +import org.apache.commons.io.IOUtils; import org.json.JSONObject; import java.io.File; +import java.io.FileInputStream; import java.io.InputStream; +import java.security.KeyStore; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; @@ -68,7 +73,7 @@ import butterknife.BindView; import butterknife.ButterKnife; -import cn.darkal.networkdiagnosis.Adapter.FilterAdpter; +import cn.darkal.networkdiagnosis.Adapter.PageFilterAdapter; import cn.darkal.networkdiagnosis.Bean.PageBean; import cn.darkal.networkdiagnosis.Fragment.BaseFragment; import cn.darkal.networkdiagnosis.Fragment.BackHandledInterface; @@ -90,8 +95,8 @@ public class MainActivity extends AppCompatActivity implements BackHandledInterface { public final static String CODE_URL = "#"; public final static String UPLOAD_URL = "#"; - public final static String HOME_URL = "http://h5.darkal.cn/har/guide/widget.basic.html"; - public final static String GUIDE_URL = "http://h5.darkal.cn/har/guide/widget.guide.html"; + public final static String HOME_URL = "http://h5.darkal.cn/har/guide/widget.basic2.html"; + public final static String GUIDE_URL = "http://h5.darkal.cn/har/guide/widget.guide2.html"; public final static int TYPE_NONE = 0; public final static int TYPE_SHARE = 1; @@ -137,9 +142,15 @@ public class MainActivity extends AppCompatActivity implements BackHandledInterf public Set disablePages = new HashSet<>(); public StringBuffer consoleLog = new StringBuffer(); + public SharedPreferences shp; + + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + shp = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + installCert(); setContentView(R.layout.activity_main); @@ -381,6 +392,13 @@ else if (id == R.id.nav_slideshow) { startActivity(intent); } else if (id == R.id.nav_ua) { showUaDialog(); + } else if (id == R.id.nav_modify) { + if(shp.getBoolean("enable_filter", false)) { + Intent intent = new Intent(MainActivity.this, ChangeFilterActivity.class); + startActivity(intent); + }else { + Toast.makeText(MainActivity.this, "请前往设置启用注入功能", Toast.LENGTH_LONG).show(); + } } else if (id == R.id.nav_cosole) { showLogDialog(); } else if (id == R.id.nav_host) { @@ -441,16 +459,22 @@ public void setSelectedFragment(BaseFragment selectedFragment) { } public void installCert() { - final String CERTIFICATE_RESOURCE = "/sslSupport/ca-certificate-rsa.cer"; + final String CERTIFICATE_RESOURCE = Environment.getExternalStorageDirectory() + "/har/littleproxy-mitm.pem"; Boolean isInstallCert = SharedPreferenceUtils.getBoolean(this, "isInstallCert", false); if (!isInstallCert) { Toast.makeText(this, "必须安装证书才可实现HTTPS抓包", Toast.LENGTH_LONG).show(); try { byte[] keychainBytes; - InputStream bis = MainActivity.class.getResourceAsStream(CERTIFICATE_RESOURCE); - keychainBytes = new byte[bis.available()]; - bis.read(keychainBytes); + + FileInputStream is = null; + try { + is = new FileInputStream(CERTIFICATE_RESOURCE); + keychainBytes = new byte[is.available()]; + is.read(keychainBytes); + } finally { + IOUtils.closeQuietly(is); + } Intent intent = KeyChain.createInstallIntent(); intent.putExtra(KeyChain.EXTRA_CERTIFICATE, keychainBytes); @@ -785,7 +809,7 @@ public void showFilter(final Context context, final int type) { pageBeenList.add(pageBean); } - listView.setAdapter(new FilterAdpter(pageBeenList)); + listView.setAdapter(new PageFilterAdapter(pageBeenList)); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setCancelable(true); diff --git a/app/src/main/java/cn/darkal/networkdiagnosis/Activity/SettingsActivity.java b/app/src/main/java/cn/darkal/networkdiagnosis/Activity/SettingsActivity.java index d24157d..c43f5d6 100644 --- a/app/src/main/java/cn/darkal/networkdiagnosis/Activity/SettingsActivity.java +++ b/app/src/main/java/cn/darkal/networkdiagnosis/Activity/SettingsActivity.java @@ -11,6 +11,7 @@ import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; +import android.os.Environment; import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceActivity; @@ -64,8 +65,6 @@ public class SettingsActivity extends AppCompatPreferenceActivity implements Pre ListPreference lp;//创建一个ListPreference对象 Preference hostPreference; - String path; - /** * Helper method to determine if the device has an extra-large screen. For * example, 10" tablets are extra-large. @@ -89,6 +88,8 @@ protected void onCreate(Bundle savedInstanceState) { findPreference("system_host").setOnPreferenceChangeListener(this); + findPreference("enable_filter").setOnPreferenceChangeListener(this); + findPreference("install_cert").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { @@ -163,12 +164,20 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { DeviceUtils.changeHost(((SysApplication)getApplication()).proxy,newValue.toString()); hostPreference.setSummary(getHost()); } + + // 重启抓包进程 + if (preference.getKey().equals("enable_filter")) { + Toast.makeText(this, "重启程序后生效", Toast.LENGTH_SHORT).show(); +// ((SysApplication)getApplication()).stopProxy(); +// ((SysApplication)getApplication()).startProxy(); +// Toast.makeText(this, "抓包进程重启完成", Toast.LENGTH_SHORT).show(); + } return true; } public void installCert() { - final String CERTIFICATE_RESOURCE = "/sslSupport/ca-certificate-rsa.cer"; + final String CERTIFICATE_RESOURCE = Environment.getExternalStorageDirectory() + "/har/littleproxy-mitm.pem"; Toast.makeText(this, "必须安装证书才可实现HTTPS抓包", Toast.LENGTH_LONG).show(); try { byte[] keychainBytes; diff --git a/app/src/main/java/cn/darkal/networkdiagnosis/Adapter/ContentFilterAdapter.java b/app/src/main/java/cn/darkal/networkdiagnosis/Adapter/ContentFilterAdapter.java new file mode 100644 index 0000000..2ca0355 --- /dev/null +++ b/app/src/main/java/cn/darkal/networkdiagnosis/Adapter/ContentFilterAdapter.java @@ -0,0 +1,94 @@ +package cn.darkal.networkdiagnosis.Adapter; + +import android.content.DialogInterface; +import android.databinding.DataBindingUtil; +import android.databinding.ViewDataBinding; +import android.support.v7.app.AlertDialog; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +import java.util.List; + +import cn.darkal.networkdiagnosis.Activity.ChangeFilterActivity; +import cn.darkal.networkdiagnosis.Activity.MainActivity; +import cn.darkal.networkdiagnosis.BR; +import cn.darkal.networkdiagnosis.Bean.PageBean; +import cn.darkal.networkdiagnosis.Bean.ResponseFilterRule; +import cn.darkal.networkdiagnosis.Fragment.PreviewFragment; +import cn.darkal.networkdiagnosis.R; +import cn.darkal.networkdiagnosis.SysApplication; +import cn.darkal.networkdiagnosis.Utils.DeviceUtils; + +/** + * Created by Darkal on 2016/9/5. + */ + +public class ContentFilterAdapter extends BaseAdapter{ + ChangeFilterActivity changeFilterActivity; + + public ContentFilterAdapter(ChangeFilterActivity changeFilterActivity,List ruleList){ + this.ruleList = ruleList; + this.changeFilterActivity = changeFilterActivity; + } + + private List ruleList; + + @Override + public int getCount() { + return ruleList.size(); + } + + @Override + public Object getItem(int position) { + return ruleList.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + ViewDataBinding listItemBinding; + if (convertView != null) { + listItemBinding = (ViewDataBinding) convertView.getTag(); + } else { + listItemBinding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_filter, parent, false); + convertView = listItemBinding.getRoot(); + convertView.setTag(listItemBinding); + } + listItemBinding.setVariable(BR.pages,ruleList.get(position)); + listItemBinding.executePendingBindings(); +// listItemBinding.cli(new ButtonClick(MainActivity.this,position)); + convertView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + changeFilterActivity.showDialog(ruleList.get(position)); + } + }); + convertView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + AlertDialog.Builder builder = new AlertDialog.Builder(changeFilterActivity); + builder.setTitle("请确认是否清除该注入项?"); + builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + ruleList.remove(ruleList.get(position)); + ContentFilterAdapter.this.notifyDataSetChanged(); + } + }); + builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + } + }); + builder.create().show(); + + return false; + } + }); + return convertView; + } +} diff --git a/app/src/main/java/cn/darkal/networkdiagnosis/Adapter/FilterAdpter.java b/app/src/main/java/cn/darkal/networkdiagnosis/Adapter/PageFilterAdapter.java similarity index 93% rename from app/src/main/java/cn/darkal/networkdiagnosis/Adapter/FilterAdpter.java rename to app/src/main/java/cn/darkal/networkdiagnosis/Adapter/PageFilterAdapter.java index 1099578..275a3e9 100644 --- a/app/src/main/java/cn/darkal/networkdiagnosis/Adapter/FilterAdpter.java +++ b/app/src/main/java/cn/darkal/networkdiagnosis/Adapter/PageFilterAdapter.java @@ -17,9 +17,9 @@ * Created by Darkal on 2016/9/5. */ -public class FilterAdpter extends BaseAdapter{ +public class PageFilterAdapter extends BaseAdapter{ - public FilterAdpter(List pageBeenList){ + public PageFilterAdapter(List pageBeenList){ this.pageBeenList = pageBeenList; } diff --git a/app/src/main/java/cn/darkal/networkdiagnosis/Bean/ResponseFilterRule.java b/app/src/main/java/cn/darkal/networkdiagnosis/Bean/ResponseFilterRule.java new file mode 100644 index 0000000..7605dc0 --- /dev/null +++ b/app/src/main/java/cn/darkal/networkdiagnosis/Bean/ResponseFilterRule.java @@ -0,0 +1,63 @@ +package cn.darkal.networkdiagnosis.Bean; + +import java.io.Serializable; + +import static cn.darkal.networkdiagnosis.Bean.ResponseFilterRule.RULE_TYPE.STRING_REPLACE; + +/** + * Created by darkal on 2017/5/31. + */ + +public class ResponseFilterRule implements Serializable{ + enum RULE_TYPE{ + STRING_REPLACE, + BEGIN_INSERT, + END_INSERT + } + + private RULE_TYPE ruleType = STRING_REPLACE; + private String url; + private String replaceRegex; + private String replaceContent; + private Boolean isEnable = true; + + public RULE_TYPE getRuleType() { + return ruleType; + } + + public void setRuleType(RULE_TYPE ruleType) { + this.ruleType = ruleType; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getReplaceRegex() { + return replaceRegex; + } + + public void setReplaceRegex(String replaceRegex) { + this.replaceRegex = replaceRegex; + } + + public String getReplaceContent() { + return replaceContent; + } + + public void setReplaceContent(String replaceContent) { + this.replaceContent = replaceContent; + } + + public Boolean getEnable() { + return isEnable; + } + + public void setEnable(Boolean enable) { + isEnable = enable; + } +} diff --git a/app/src/main/java/cn/darkal/networkdiagnosis/SysApplication.java b/app/src/main/java/cn/darkal/networkdiagnosis/SysApplication.java index 16a73d9..ed1d773 100644 --- a/app/src/main/java/cn/darkal/networkdiagnosis/SysApplication.java +++ b/app/src/main/java/cn/darkal/networkdiagnosis/SysApplication.java @@ -21,10 +21,16 @@ import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.Random; +import cn.darkal.networkdiagnosis.Bean.ResponseFilterRule; +import cn.darkal.networkdiagnosis.Utils.DeviceUtils; +import cn.darkal.networkdiagnosis.Utils.SharedPreferenceUtils; + /** * Created by xuzhou on 2016/8/10. */ @@ -32,6 +38,7 @@ public class SysApplication extends MultiDexApplication { public static Boolean isInitProxy = false; public static int proxyPort = 8888; public BrowserMobProxy proxy; + public List ruleList; @Override public void onCreate() { @@ -95,16 +102,19 @@ public void startProxy(){ } Log.e("~~~", proxy.getPort() + ""); - proxy.enableHarCaptureTypes(CaptureType.REQUEST_HEADERS, CaptureType.REQUEST_COOKIES, - CaptureType.REQUEST_CONTENT, CaptureType.RESPONSE_HEADERS, CaptureType.REQUEST_COOKIES, - CaptureType.RESPONSE_CONTENT); - String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA) - .format(new Date(System.currentTimeMillis())); - proxy.newHar(time); + Object object = SharedPreferenceUtils.get(this.getApplicationContext(), "response_filter"); + if (object != null && object instanceof List) { + ruleList = (List) object; + } SharedPreferences shp = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + if(shp.getBoolean("enable_filter", false)) { + Log.e("~~~enable_filter", ""); + initResponseFilter(); + } + // 设置hosts if(shp.getString("system_host", "").length()>0){ AdvancedHostResolver advancedHostResolver = proxy.getHostNameResolver(); @@ -117,6 +127,39 @@ public void startProxy(){ proxy.setHostNameResolver(advancedHostResolver); } + proxy.enableHarCaptureTypes(CaptureType.REQUEST_HEADERS, CaptureType.REQUEST_COOKIES, + CaptureType.REQUEST_CONTENT, CaptureType.RESPONSE_HEADERS, CaptureType.REQUEST_COOKIES, + CaptureType.RESPONSE_CONTENT); + + String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA) + .format(new Date(System.currentTimeMillis())); + proxy.newHar(time); + + isInitProxy = true; } + + public void stopProxy(){ + if(proxy!=null){ + proxy.stop(); + } + } + + private void initResponseFilter(){ + try { + if(ruleList == null){ + ResponseFilterRule rule = new ResponseFilterRule(); + rule.setUrl("xw.qq.com/index.htm"); + rule.setReplaceRegex(""); + rule.setReplaceContent(""); + + ruleList = new ArrayList<>(); + ruleList.add(rule); + } + + DeviceUtils.changeResponseFilter(this,ruleList); + }catch (Exception e){ + e.printStackTrace(); + } + } } diff --git a/app/src/main/java/cn/darkal/networkdiagnosis/Utils/DeviceUtils.java b/app/src/main/java/cn/darkal/networkdiagnosis/Utils/DeviceUtils.java index b4b6ea4..34ef1d4 100644 --- a/app/src/main/java/cn/darkal/networkdiagnosis/Utils/DeviceUtils.java +++ b/app/src/main/java/cn/darkal/networkdiagnosis/Utils/DeviceUtils.java @@ -6,9 +6,24 @@ import android.util.Log; import net.lightbody.bmp.BrowserMobProxy; +import net.lightbody.bmp.filters.RequestFilter; +import net.lightbody.bmp.filters.ResponseFilter; import net.lightbody.bmp.proxy.dns.AdvancedHostResolver; +import net.lightbody.bmp.util.HttpMessageContents; +import net.lightbody.bmp.util.HttpMessageInfo; +import org.littleshoot.proxy.HttpFilters; +import org.littleshoot.proxy.HttpFiltersSource; + +import java.net.InetSocketAddress; +import java.util.List; + +import cn.darkal.networkdiagnosis.Bean.ResponseFilterRule; import cn.darkal.networkdiagnosis.SysApplication; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; /** * Created by Darkal on 2016/9/21. @@ -67,4 +82,28 @@ public static void changeHost(BrowserMobProxy browserMobProxy,String newValue){ } browserMobProxy.setHostNameResolver(advancedHostResolver); } + + public static void changeResponseFilter(SysApplication sysApplication,final List ruleList){ + if(ruleList == null){ + Log.e("~~~~","changeResponseFilter ruleList == null!"); + return; + } + + sysApplication.proxy.addResponseFilter(new ResponseFilter() { + @Override + public void filterResponse(HttpResponse response, HttpMessageContents contents, HttpMessageInfo messageInfo) { + for (ResponseFilterRule rule: ruleList) { + if(rule.getEnable()) { + if (contents.isText() && messageInfo.getUrl().contains(rule.getUrl())) { + String originContent = contents.getTextContents(); + if (originContent != null) { + contents.setTextContents(contents.getTextContents().replaceAll( + rule.getReplaceRegex(), rule.getReplaceContent())); + } + } + } + } + } + }); + } } diff --git a/app/src/main/java/cn/darkal/networkdiagnosis/Utils/SharedPreferenceUtils.java b/app/src/main/java/cn/darkal/networkdiagnosis/Utils/SharedPreferenceUtils.java index a8f6647..1627148 100644 --- a/app/src/main/java/cn/darkal/networkdiagnosis/Utils/SharedPreferenceUtils.java +++ b/app/src/main/java/cn/darkal/networkdiagnosis/Utils/SharedPreferenceUtils.java @@ -1,8 +1,16 @@ package cn.darkal.networkdiagnosis.Utils; +import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; +import android.util.Base64; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; /** * Created by xuzhou on 2016/9/12. @@ -95,4 +103,81 @@ public static void remove(Context context, String key) { e.printStackTrace(); } } + + /** + * writeObject 方法负责写入特定类的对象的状态,以便相应的 readObject 方法可以还原它 + * 最后,用Base64.encode将字节文件转换成Base64编码保存在String中 + * + * @param object 待加密的转换为String的对象 + * @return String 加密后的String + */ + private static String Object2String(Object object) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = null; + try { + objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); + objectOutputStream.writeObject(object); + String string = new String(Base64.encode(byteArrayOutputStream.toByteArray(), Base64.DEFAULT)); + objectOutputStream.close(); + return string; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * 使用Base64解密String,返回Object对象 + * + * @param objectString 待解密的String + * @return object 解密后的object + */ + private static Object String2Object(String objectString) { + byte[] mobileBytes = Base64.decode(objectString.getBytes(), Base64.DEFAULT); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(mobileBytes); + ObjectInputStream objectInputStream = null; + try { + objectInputStream = new ObjectInputStream(byteArrayInputStream); + Object object = objectInputStream.readObject(); + objectInputStream.close(); + return object; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + } + + /** + * 使用SharedPreference保存对象 + * + * @param context context + * @param key 储存对象的key + * @param saveObject 储存的对象 + */ + public static void save(Context context, String key, Object saveObject) { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sharedPreferences.edit(); + String string = Object2String(saveObject); + editor.putString(key, string); + editor.commit(); + } + + /** + * 获取SharedPreference保存的对象 + * + * @param context context + * @param key 储存对象的key + * @return object 返回根据key得到的对象 + */ + public static Object get(Context context, String key) { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + String string = sharedPreferences.getString(key, null); + if (string != null) { + Object object = String2Object(string); + return object; + } else { + return null; + } + } } diff --git a/app/src/main/java/net/lightbody/bmp/BrowserMobProxy.java b/app/src/main/java/net/lightbody/bmp/BrowserMobProxy.java old mode 100644 new mode 100755 index ee81846..28a9d80 --- a/app/src/main/java/net/lightbody/bmp/BrowserMobProxy.java +++ b/app/src/main/java/net/lightbody/bmp/BrowserMobProxy.java @@ -1,7 +1,6 @@ package net.lightbody.bmp; import net.lightbody.bmp.core.har.Har; -import net.lightbody.bmp.core.har.HarPage; import net.lightbody.bmp.filters.RequestFilter; import net.lightbody.bmp.filters.ResponseFilter; import net.lightbody.bmp.mitm.TrustSource; @@ -108,7 +107,6 @@ public interface BrowserMobProxy { */ Har getHar(); - Har getHar(Set pageRef); Har getHar(String pageRef); @@ -228,13 +226,6 @@ public interface BrowserMobProxy { */ Har newPage(String pageRef, String pageTitle); - /** - * 删除page - * - * @return the HAR as it existed immediately after ending the current page - **/ - Boolean deletePage(HarPage harPage); - /** * Stops capturing traffic in the HAR. Populates the {@link net.lightbody.bmp.core.har.HarPageTimings#onLoad} value for the current page * based on the amount of time it has been captured. @@ -565,9 +556,11 @@ public interface BrowserMobProxy { boolean waitForQuiescence(long quietPeriod, long timeout, TimeUnit timeUnit); /** - * Sets an upstream proxy that this proxy will use to connect to external hosts. + * Instructs this proxy to route traffic through an upstream proxy. + * + * Note: A chained proxy must be set before the proxy is started, though it can be changed after the proxy is started. * - * @param chainedProxyAddress address and port of the upstream proxy, or null to remove an upstream proxy + * @param chainedProxyAddress address of the upstream proxy */ void setChainedProxy(InetSocketAddress chainedProxyAddress); diff --git a/app/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java b/app/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java old mode 100644 new mode 100755 index 326d0b5..56b5882 --- a/app/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java +++ b/app/src/main/java/net/lightbody/bmp/BrowserMobProxyServer.java @@ -3,7 +3,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.MapMaker; - +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; import net.lightbody.bmp.client.ClientUtil; import net.lightbody.bmp.core.har.Har; import net.lightbody.bmp.core.har.HarLog; @@ -43,7 +46,6 @@ import net.lightbody.bmp.proxy.dns.DelegatingHostResolver; import net.lightbody.bmp.util.BrowserMobHttpUtil; import net.lightbody.bmp.util.BrowserMobProxyUtil; - import org.littleshoot.proxy.ChainedProxy; import org.littleshoot.proxy.ChainedProxyAdapter; import org.littleshoot.proxy.ChainedProxyManager; @@ -56,9 +58,12 @@ import org.littleshoot.proxy.impl.DefaultHttpProxyServer; import org.littleshoot.proxy.impl.ProxyUtils; import org.littleshoot.proxy.impl.ThreadPoolConfiguration; +import org.littleshoot.proxy.mitm.Authority; +import org.littleshoot.proxy.mitm.CertificateSniffingMitmManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.ArrayList; @@ -79,11 +84,6 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; - /** * A LittleProxy-based implementation of {@link net.lightbody.bmp.BrowserMobProxy}. */ @@ -202,6 +202,12 @@ public class BrowserMobProxyServer implements BrowserMobProxy { */ private final AtomicBoolean harCaptureFilterEnabled = new AtomicBoolean(false); + /** + * Set to true when LittleProxy has been bootstrapped with the default chained proxy. This allows modifying the chained proxy + * after the proxy has been started. + */ + private final AtomicBoolean bootstrappedWithDefaultChainedProxy = new AtomicBoolean(false); + /** * The address of an upstream chained proxy to route traffic through. */ @@ -309,19 +315,24 @@ public int getMaximumResponseBufferSizeInBytes() { if (!mitmDisabled) { - if (mitmManager == null) { - mitmManager = ImpersonatingMitmManager.builder() - .rootCertificateSource(new KeyStoreFileCertificateSource( - KEYSTORE_TYPE, - useEcc ? EC_KEYSTORE_RESOURCE : RSA_KEYSTORE_RESOURCE, - KEYSTORE_PRIVATE_KEY_ALIAS, - KEYSTORE_PASSWORD)) - .serverKeyGenerator(useEcc ? new ECKeyGenerator() : new RSAKeyGenerator()) - .trustSource(trustSource) - .build(); +// if (mitmManager == null) { +// mitmManager = ImpersonatingMitmManager.builder() +// .rootCertificateSource(new KeyStoreFileCertificateSource( +// KEYSTORE_TYPE, +// useEcc ? EC_KEYSTORE_RESOURCE : RSA_KEYSTORE_RESOURCE, +// KEYSTORE_PRIVATE_KEY_ALIAS, +// KEYSTORE_PASSWORD)) +// .serverKeyGenerator(useEcc ? new ECKeyGenerator() : new RSAKeyGenerator()) +// .trustSource(trustSource) +// .build(); +// } + + try { + bootstrap.withManInTheMiddle(new CertificateSniffingMitmManager( + new Authority())); + }catch (Exception e){ + e.printStackTrace(); } - - bootstrap.withManInTheMiddle(mitmManager); } if (readBandwidthLimitBps > 0 || writeBandwidthLimitBps > 0) { @@ -331,6 +342,10 @@ public int getMaximumResponseBufferSizeInBytes() { if (chainedProxyManager != null) { bootstrap.withChainProxyManager(chainedProxyManager); } else if (upstreamProxyAddress != null) { + // indicate that the proxy was bootstrapped with the default chained proxy manager, which allows changing the + // chained proxy after the proxy is started. + bootstrappedWithDefaultChainedProxy.set(true); + bootstrap.withChainProxyManager(new ChainedProxyManager() { @Override public void lookupChainedProxies(HttpRequest httpRequest, Queue chainedProxies) { @@ -465,9 +480,6 @@ public Har newHar(String initialPageRef) { @Override public Har newHar(String initialPageRef, String initialPageTitle) { - // eagerly initialize the User Agent String Parser, since it will be needed for the HAR -// BrowserMobPrzoxyUtil.getUserAgentStringParser(); - Har oldHar = getHar(); addHarCaptureFilter(); @@ -544,7 +556,7 @@ public Har newPage(String pageRef) { } @Override - public synchronized Har newPage(String pageRef, String pageTitle) { + public Har newPage(String pageRef, String pageTitle) { if (har == null) { throw new IllegalStateException("No HAR exists for this proxy. Use newHar() to create a new HAR before calling newPage()."); } @@ -577,15 +589,6 @@ public synchronized Har newPage(String pageRef, String pageTitle) { return endOfPageHar; } - @Override - public Boolean deletePage(HarPage harPage) { - if (har == null) { - throw new IllegalStateException("No HAR exists for this proxy. Use newHar() to create a new HAR before calling newPage()."); - } - - return har.getLog().deletePage(harPage); - } - @Override public Har endHar() { Har oldHar = getHar(); @@ -883,15 +886,19 @@ public boolean waitForQuiescence(long quietPeriod, long timeout, TimeUnit timeUn } /** - * Instructs this proxy to route traffic through an upstream proxy. Proxy chaining is not compatible with man-in-the-middle - * SSL, so HAR capture will be disabled for HTTPS traffic when using an upstream proxy. - *

- * Note: Using {@link #setChainedProxyManager(ChainedProxyManager)} will supersede any value set by this method. + * Instructs this proxy to route traffic through an upstream proxy. + * + * Note: Using {@link #setChainedProxyManager(ChainedProxyManager)} will supersede any value set by this method. A chained + * proxy must be set before the proxy is started, though it can be changed after the proxy is started. * * @param chainedProxyAddress address of the upstream proxy */ @Override public void setChainedProxy(InetSocketAddress chainedProxyAddress) { + if (isStarted() && !bootstrappedWithDefaultChainedProxy.get()) { + throw new IllegalStateException("Cannot set a chained proxy after the proxy is started if the proxy was started without a chained proxy."); + } + upstreamProxyAddress = chainedProxyAddress; } @@ -904,6 +911,8 @@ public InetSocketAddress getChainedProxy() { * Allows access to the LittleProxy {@link ChainedProxyManager} for fine-grained control of the chained proxies. To enable a single * chained proxy, {@link BrowserMobProxy#setChainedProxy(InetSocketAddress)} is generally more convenient. * + * Note: The chained proxy manager must be enabled before calling {@link #start()}. + * * @param chainedProxyManager chained proxy manager to enable */ public void setChainedProxyManager(ChainedProxyManager chainedProxyManager) { @@ -944,7 +953,8 @@ public void addLastHttpFilterFactory(HttpFiltersSource filterFactory) { */ @Override public void addResponseFilter(ResponseFilter filter) { - addLastHttpFilterFactory(new ResponseFilterAdapter.FilterSource(filter)); + filterFactories.add(new ResponseFilterAdapter.FilterSource(filter)); +// addLastHttpFilterFactory(); } /** diff --git a/app/src/main/java/net/lightbody/bmp/client/ClientUtil.java b/app/src/main/java/net/lightbody/bmp/client/ClientUtil.java old mode 100644 new mode 100755 index 7c6b1b5..79372fe --- a/app/src/main/java/net/lightbody/bmp/client/ClientUtil.java +++ b/app/src/main/java/net/lightbody/bmp/client/ClientUtil.java @@ -69,27 +69,27 @@ public static AdvancedHostResolver createDnsJavaWithNativeFallbackResolver() { // public static org.openqa.selenium.Proxy createSeleniumProxy(BrowserMobProxy browserMobProxy) { // return createSeleniumProxy(browserMobProxy, getConnectableAddress()); // } -// -// /** -// * Creates a Selenium Proxy object from the BrowserMobProxy instance, using the specified connectableAddress as the Selenium Proxy object's -// * proxy address. Determines the port using {@link net.lightbody.bmp.BrowserMobProxy#getPort()}. The BrowserMobProxy must be started. -// * -// * @param browserMobProxy started BrowserMobProxy instance to read the port from -// * @param connectableAddress the network address the Selenium Proxy will use to reach this BrowserMobProxy instance -// * @return a Selenium Proxy instance, configured to use the BrowserMobProxy instance as its proxy server -// * @throws java.lang.IllegalStateException if the proxy has not been started. -// */ + + /** + * Creates a Selenium Proxy object from the BrowserMobProxy instance, using the specified connectableAddress as the Selenium Proxy object's + * proxy address. Determines the port using {@link net.lightbody.bmp.BrowserMobProxy#getPort()}. The BrowserMobProxy must be started. + * + * @param browserMobProxy started BrowserMobProxy instance to read the port from + * @param connectableAddress the network address the Selenium Proxy will use to reach this BrowserMobProxy instance + * @return a Selenium Proxy instance, configured to use the BrowserMobProxy instance as its proxy server + * @throws java.lang.IllegalStateException if the proxy has not been started. + */ // public static org.openqa.selenium.Proxy createSeleniumProxy(BrowserMobProxy browserMobProxy, InetAddress connectableAddress) { // return createSeleniumProxy(new InetSocketAddress(connectableAddress, browserMobProxy.getPort())); // } -// -// /** -// * Creates a Selenium Proxy object using the specified connectableAddressAndPort as the HTTP proxy server. -// * -// * @param connectableAddressAndPort the network address (or hostname) and port the Selenium Proxy will use to reach its -// * proxy server (the InetSocketAddress may be unresolved). -// * @return a Selenium Proxy instance, configured to use the specified address and port as its proxy server -// */ + + /** + * Creates a Selenium Proxy object using the specified connectableAddressAndPort as the HTTP proxy server. + * + * @param connectableAddressAndPort the network address (or hostname) and port the Selenium Proxy will use to reach its + * proxy server (the InetSocketAddress may be unresolved). + * @return a Selenium Proxy instance, configured to use the specified address and port as its proxy server + */ // public static org.openqa.selenium.Proxy createSeleniumProxy(InetSocketAddress connectableAddressAndPort) { // Proxy proxy = new Proxy(); // proxy.setProxyType(Proxy.ProxyType.MANUAL); diff --git a/app/src/main/java/net/lightbody/bmp/core/har/Har.java b/app/src/main/java/net/lightbody/bmp/core/har/Har.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarCache.java b/app/src/main/java/net/lightbody/bmp/core/har/HarCache.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarCacheStatus.java b/app/src/main/java/net/lightbody/bmp/core/har/HarCacheStatus.java old mode 100644 new mode 100755 index 427641b..36736fa --- a/app/src/main/java/net/lightbody/bmp/core/har/HarCacheStatus.java +++ b/app/src/main/java/net/lightbody/bmp/core/har/HarCacheStatus.java @@ -1,8 +1,10 @@ package net.lightbody.bmp.core.har; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import net.lightbody.bmp.core.json.ISO8601DateFormatter; + +import net.lightbody.bmp.core.json.ISO8601WithTDZDateFormatter; import java.util.Date; @@ -14,7 +16,7 @@ public class HarCacheStatus { private volatile int hitCount; private volatile String comment = ""; - @JsonSerialize(using = ISO8601DateFormatter.class) + @JsonSerialize(using = ISO8601WithTDZDateFormatter.class) public Date getExpires() { return expires; } @@ -23,7 +25,7 @@ public void setExpires(Date expires) { this.expires = expires; } - @JsonSerialize(using = ISO8601DateFormatter.class) + @JsonSerialize(using = ISO8601WithTDZDateFormatter.class) public Date getLastAccess() { return lastAccess; } diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarContent.java b/app/src/main/java/net/lightbody/bmp/core/har/HarContent.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarCookie.java b/app/src/main/java/net/lightbody/bmp/core/har/HarCookie.java old mode 100644 new mode 100755 index 643feb1..31bc961 --- a/app/src/main/java/net/lightbody/bmp/core/har/HarCookie.java +++ b/app/src/main/java/net/lightbody/bmp/core/har/HarCookie.java @@ -1,12 +1,14 @@ package net.lightbody.bmp.core.har; -import java.net.URLDecoder; -import java.util.Date; - +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.annotation.JsonSerialize; + import net.lightbody.bmp.core.json.ISO8601WithTDZDateFormatter; +import java.net.URLDecoder; +import java.util.Date; + @JsonInclude(JsonInclude.Include.NON_NULL) public class HarCookie { private volatile String name; diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarEntry.java b/app/src/main/java/net/lightbody/bmp/core/har/HarEntry.java old mode 100644 new mode 100755 index c091303..39339f2 --- a/app/src/main/java/net/lightbody/bmp/core/har/HarEntry.java +++ b/app/src/main/java/net/lightbody/bmp/core/har/HarEntry.java @@ -1,13 +1,13 @@ package net.lightbody.bmp.core.har; import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.annotation.JsonSerialize; + import net.lightbody.bmp.core.json.ISO8601WithTDZDateFormatter; import java.util.Date; -import java.util.HashSet; -import java.util.Set; import java.util.concurrent.TimeUnit; @JsonInclude(JsonInclude.Include.NON_NULL) diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarLog.java b/app/src/main/java/net/lightbody/bmp/core/har/HarLog.java old mode 100644 new mode 100755 index 2135bcc..dc765ca --- a/app/src/main/java/net/lightbody/bmp/core/har/HarLog.java +++ b/app/src/main/java/net/lightbody/bmp/core/har/HarLog.java @@ -11,17 +11,10 @@ import java.util.Locale; import java.util.concurrent.CopyOnWriteArrayList; -/** - * Modify by xuzhou on 2016/9/2. - * 添加了删除page的功能 - * 添加了clearAllEntries - * 1000请求自动分页 - */ - @JsonInclude(JsonInclude.Include.NON_NULL) public class HarLog { private final String version = "1.2"; - private volatile HarNameVersion creator = new HarNameVersion("BrowserMob Proxy", BrowserMobProxyUtil.getVersionString()); + private volatile HarNameVersion creator = new HarNameVersion("BrowserMob Proxy", BrowserMobProxyUtil.getVersionString()); private volatile HarNameVersion browser; private List pages = new CopyOnWriteArrayList(); private List entries = new CopyOnWriteArrayList(); diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarNameValuePair.java b/app/src/main/java/net/lightbody/bmp/core/har/HarNameValuePair.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarNameVersion.java b/app/src/main/java/net/lightbody/bmp/core/har/HarNameVersion.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarPage.java b/app/src/main/java/net/lightbody/bmp/core/har/HarPage.java old mode 100644 new mode 100755 index 373521f..3555366 --- a/app/src/main/java/net/lightbody/bmp/core/har/HarPage.java +++ b/app/src/main/java/net/lightbody/bmp/core/har/HarPage.java @@ -1,10 +1,9 @@ package net.lightbody.bmp.core.har; -import java.util.Date; - +import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import net.lightbody.bmp.core.json.ISO8601WithTDZDateFormatter; + +import java.util.Date; @JsonInclude(JsonInclude.Include.NON_NULL) public class HarPage { @@ -35,7 +34,7 @@ public void setId(String id) { this.id = id; } - @JsonSerialize(using = ISO8601WithTDZDateFormatter.class) + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ") public Date getStartedDateTime() { return startedDateTime; } diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarPageTimings.java b/app/src/main/java/net/lightbody/bmp/core/har/HarPageTimings.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarPostData.java b/app/src/main/java/net/lightbody/bmp/core/har/HarPostData.java old mode 100644 new mode 100755 index b609ade..a9f10d1 --- a/app/src/main/java/net/lightbody/bmp/core/har/HarPostData.java +++ b/app/src/main/java/net/lightbody/bmp/core/har/HarPostData.java @@ -32,9 +32,6 @@ public String getText() { } public void setText(String text) { - if(text != null && text.length()>100000){ - text = "HarPostData is too large! Size:"+text.length(); - } this.text = text; } diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarPostDataParam.java b/app/src/main/java/net/lightbody/bmp/core/har/HarPostDataParam.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarRequest.java b/app/src/main/java/net/lightbody/bmp/core/har/HarRequest.java old mode 100644 new mode 100755 index 172c750..2cfe689 --- a/app/src/main/java/net/lightbody/bmp/core/har/HarRequest.java +++ b/app/src/main/java/net/lightbody/bmp/core/har/HarRequest.java @@ -68,7 +68,7 @@ public HarPostData getPostData() { } public void setPostData(HarPostData postData) { - this.postData = postData; + this.postData = postData; } public long getHeadersSize() { diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarResponse.java b/app/src/main/java/net/lightbody/bmp/core/har/HarResponse.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/core/har/HarTimings.java b/app/src/main/java/net/lightbody/bmp/core/har/HarTimings.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/core/json/ISO8601DateFormatter.java b/app/src/main/java/net/lightbody/bmp/core/json/ISO8601DateFormatter.java index 7a8c0d6..fc13485 100644 --- a/app/src/main/java/net/lightbody/bmp/core/json/ISO8601DateFormatter.java +++ b/app/src/main/java/net/lightbody/bmp/core/json/ISO8601DateFormatter.java @@ -16,7 +16,7 @@ public class ISO8601DateFormatter extends JsonSerializer { public final static ISO8601DateFormatter instance = new ISO8601DateFormatter(); @Override - public void serialize(java.util.Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { + public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { DateFormat df = (DateFormat) provider.getConfig().getDateFormat().clone(); jgen.writeString(df.format(value)); } diff --git a/app/src/main/java/net/lightbody/bmp/core/json/ISO8601WithTDZDateFormatter.java b/app/src/main/java/net/lightbody/bmp/core/json/ISO8601WithTDZDateFormatter.java index 11bb803..8a64c1c 100644 --- a/app/src/main/java/net/lightbody/bmp/core/json/ISO8601WithTDZDateFormatter.java +++ b/app/src/main/java/net/lightbody/bmp/core/json/ISO8601WithTDZDateFormatter.java @@ -20,7 +20,7 @@ */ public class ISO8601WithTDZDateFormatter extends JsonSerializer { @Override - public void serialize(java.util.Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { + public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException { Calendar cal = Calendar.getInstance(); cal.setTime(value); jgen.writeString(DatatypeConverter.doFormat("%Y-%M-%DT%h:%m:%s%z",cal)); diff --git a/app/src/main/java/net/lightbody/bmp/exception/DecompressionException.java b/app/src/main/java/net/lightbody/bmp/exception/DecompressionException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/exception/UnsupportedCharsetException.java b/app/src/main/java/net/lightbody/bmp/exception/UnsupportedCharsetException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/AddHeadersFilter.java b/app/src/main/java/net/lightbody/bmp/filters/AddHeadersFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/AutoBasicAuthFilter.java b/app/src/main/java/net/lightbody/bmp/filters/AutoBasicAuthFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/BlacklistFilter.java b/app/src/main/java/net/lightbody/bmp/filters/BlacklistFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/BrowserMobHttpFilterChain.java b/app/src/main/java/net/lightbody/bmp/filters/BrowserMobHttpFilterChain.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/ClientRequestCaptureFilter.java b/app/src/main/java/net/lightbody/bmp/filters/ClientRequestCaptureFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java b/app/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java old mode 100644 new mode 100755 index 3f2725e..ca8c044 --- a/app/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java +++ b/app/src/main/java/net/lightbody/bmp/filters/HarCaptureFilter.java @@ -1,12 +1,23 @@ package net.lightbody.bmp.filters; import com.google.common.collect.ImmutableList; - +import com.google.common.io.BaseEncoding; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpContent; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpObject; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.LastHttpContent; +import io.netty.handler.codec.http.QueryStringDecoder; +import io.netty.handler.codec.http.cookie.ClientCookieDecoder; +import io.netty.handler.codec.http.cookie.Cookie; +import io.netty.handler.codec.http.cookie.ServerCookieDecoder; import net.lightbody.bmp.core.har.Har; import net.lightbody.bmp.core.har.HarCookie; import net.lightbody.bmp.core.har.HarEntry; import net.lightbody.bmp.core.har.HarNameValuePair; -import net.lightbody.bmp.core.har.HarNameVersion; import net.lightbody.bmp.core.har.HarPostData; import net.lightbody.bmp.core.har.HarPostDataParam; import net.lightbody.bmp.core.har.HarRequest; @@ -16,7 +27,6 @@ import net.lightbody.bmp.filters.util.HarCaptureUtil; import net.lightbody.bmp.proxy.CaptureType; import net.lightbody.bmp.util.BrowserMobHttpUtil; - import org.littleshoot.proxy.impl.ProxyUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +34,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Calendar; import java.util.Date; import java.util.EnumSet; @@ -33,20 +44,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import cn.darkal.networkdiagnosis.Utils.DatatypeConverter; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.http.HttpContent; -import io.netty.handler.codec.http.HttpHeaders; -import io.netty.handler.codec.http.HttpObject; -import io.netty.handler.codec.http.HttpRequest; -import io.netty.handler.codec.http.HttpResponse; -import io.netty.handler.codec.http.LastHttpContent; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.handler.codec.http.cookie.ClientCookieDecoder; -import io.netty.handler.codec.http.cookie.Cookie; -import io.netty.handler.codec.http.cookie.ServerCookieDecoder; - public class HarCaptureFilter extends HttpsAwareFiltersAdapter { private static final Logger log = LoggerFactory.getLogger(HarCaptureFilter.class); @@ -204,7 +201,9 @@ public HttpResponse clientToProxyRequest(HttpObject httpObject) { harEntry.setResponse(defaultHarResponse); captureQueryParameters(httpRequest); - captureUserAgent(httpRequest); + // not capturing user agent: in many cases, it doesn't make sense to capture at the HarLog level, since the proxy could be + // serving requests from many different clients with various user agents. clients can turn on the REQUEST_HEADERS capture type + // in order to capture the User-Agent header, if desired. captureRequestHeaderSize(httpRequest); if (dataToCapture.contains(CaptureType.REQUEST_COOKIES)) { @@ -321,8 +320,7 @@ private HarRequest createHarRequestForHttpRequest(HttpRequest httpRequest) { protected void captureQueryParameters(HttpRequest httpRequest) { // capture query parameters. it is safe to assume the query string is UTF-8, since it "should" be in US-ASCII (a subset of UTF-8), // but sometimes does include UTF-8 characters. - QueryStringDecoder queryStringDecoder = null; - queryStringDecoder = new QueryStringDecoder(httpRequest.getUri(), Charset.forName("UTF-8")); + QueryStringDecoder queryStringDecoder = new QueryStringDecoder(httpRequest.getUri(), StandardCharsets.UTF_8); try { for (Map.Entry> entry : queryStringDecoder.parameters().entrySet()) { @@ -338,23 +336,6 @@ protected void captureQueryParameters(HttpRequest httpRequest) { } } - protected void captureUserAgent(HttpRequest httpRequest) { - // save the browser and version if it's not yet been set - if (har.getLog().getBrowser() == null) { - String userAgentHeader = HttpHeaders.getHeader(httpRequest, HttpHeaders.Names.USER_AGENT); - if (userAgentHeader != null && userAgentHeader.length() > 0) { - try { -// ReadableUserAgent uai = BrowserMobProxyUtil.getUserAgentStringParser().parse(userAgentHeader); -// String browser = uai.getName(); -// String version = uai.getVersionNumber().toVersionString(); - har.getLog().setBrowser(new HarNameVersion("Mozilla/5.0 (Linux; U; Android 4.3; zh-cn; R8007 Build/JLS36C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30", "1.0.0")); - } catch (RuntimeException e) { - log.warn("Failed to parse user agent string", e); - } - } - } - } - protected void captureRequestHeaderSize(HttpRequest httpRequest) { String requestLine = httpRequest.getMethod().toString() + ' ' + httpRequest.getUri() + ' ' + httpRequest.getProtocolVersion().toString(); // +2 => CRLF after status line, +4 => header/data separation @@ -496,7 +477,7 @@ protected void captureResponseContent(HttpResponse httpResponse, byte[] fullMess String text = BrowserMobHttpUtil.getContentAsString(fullMessage, charset); harEntry.getResponse().getContent().setText(text); } else if (dataToCapture.contains(CaptureType.RESPONSE_BINARY_CONTENT)) { - harEntry.getResponse().getContent().setText(new String(DatatypeConverter.parseBase64Binary(new String(fullMessage)))); + harEntry.getResponse().getContent().setText(BaseEncoding.base64().encode(fullMessage)); harEntry.getResponse().getContent().setEncoding("base64"); } @@ -782,7 +763,4 @@ public void serverToProxyResponseReceived() { harEntry.getTimings().setReceive(0L, TimeUnit.NANOSECONDS); } } - - - } diff --git a/app/src/main/java/net/lightbody/bmp/filters/HttpConnectHarCaptureFilter.java b/app/src/main/java/net/lightbody/bmp/filters/HttpConnectHarCaptureFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/HttpsAwareFiltersAdapter.java b/app/src/main/java/net/lightbody/bmp/filters/HttpsAwareFiltersAdapter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/HttpsHostCaptureFilter.java b/app/src/main/java/net/lightbody/bmp/filters/HttpsHostCaptureFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/HttpsOriginalHostCaptureFilter.java b/app/src/main/java/net/lightbody/bmp/filters/HttpsOriginalHostCaptureFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/LatencyFilter.java b/app/src/main/java/net/lightbody/bmp/filters/LatencyFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/ModifiedRequestAwareFilter.java b/app/src/main/java/net/lightbody/bmp/filters/ModifiedRequestAwareFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/RegisterRequestFilter.java b/app/src/main/java/net/lightbody/bmp/filters/RegisterRequestFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/RequestFilter.java b/app/src/main/java/net/lightbody/bmp/filters/RequestFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/RequestFilterAdapter.java b/app/src/main/java/net/lightbody/bmp/filters/RequestFilterAdapter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/ResolvedHostnameCacheFilter.java b/app/src/main/java/net/lightbody/bmp/filters/ResolvedHostnameCacheFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/ResponseFilter.java b/app/src/main/java/net/lightbody/bmp/filters/ResponseFilter.java old mode 100644 new mode 100755 index 8a11bbc..14ed7d4 --- a/app/src/main/java/net/lightbody/bmp/filters/ResponseFilter.java +++ b/app/src/main/java/net/lightbody/bmp/filters/ResponseFilter.java @@ -1,10 +1,9 @@ package net.lightbody.bmp.filters; +import io.netty.handler.codec.http.HttpResponse; import net.lightbody.bmp.util.HttpMessageContents; import net.lightbody.bmp.util.HttpMessageInfo; -import io.netty.handler.codec.http.HttpResponse; - /** * A functional interface to simplify modification and manipulation of responses. */ diff --git a/app/src/main/java/net/lightbody/bmp/filters/ResponseFilterAdapter.java b/app/src/main/java/net/lightbody/bmp/filters/ResponseFilterAdapter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/RewriteUrlFilter.java b/app/src/main/java/net/lightbody/bmp/filters/RewriteUrlFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java b/app/src/main/java/net/lightbody/bmp/filters/ServerResponseCaptureFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/UnregisterRequestFilter.java b/app/src/main/java/net/lightbody/bmp/filters/UnregisterRequestFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/WhitelistFilter.java b/app/src/main/java/net/lightbody/bmp/filters/WhitelistFilter.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/support/HttpConnectTiming.java b/app/src/main/java/net/lightbody/bmp/filters/support/HttpConnectTiming.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/filters/util/HarCaptureUtil.java b/app/src/main/java/net/lightbody/bmp/filters/util/HarCaptureUtil.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/CertificateAndKey.java b/app/src/main/java/net/lightbody/bmp/mitm/CertificateAndKey.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/CertificateAndKeySource.java b/app/src/main/java/net/lightbody/bmp/mitm/CertificateAndKeySource.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/CertificateInfo.java b/app/src/main/java/net/lightbody/bmp/mitm/CertificateInfo.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/CertificateInfoGenerator.java b/app/src/main/java/net/lightbody/bmp/mitm/CertificateInfoGenerator.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/ExistingCertificateSource.java b/app/src/main/java/net/lightbody/bmp/mitm/ExistingCertificateSource.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/HostnameCertificateInfoGenerator.java b/app/src/main/java/net/lightbody/bmp/mitm/HostnameCertificateInfoGenerator.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/KeyStoreCertificateSource.java b/app/src/main/java/net/lightbody/bmp/mitm/KeyStoreCertificateSource.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/KeyStoreFileCertificateSource.java b/app/src/main/java/net/lightbody/bmp/mitm/KeyStoreFileCertificateSource.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/PemFileCertificateSource.java b/app/src/main/java/net/lightbody/bmp/mitm/PemFileCertificateSource.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/RootCertificateGenerator.java b/app/src/main/java/net/lightbody/bmp/mitm/RootCertificateGenerator.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/TrustSource.java b/app/src/main/java/net/lightbody/bmp/mitm/TrustSource.java old mode 100644 new mode 100755 index ddb78ed..f4a4ed7 --- a/app/src/main/java/net/lightbody/bmp/mitm/TrustSource.java +++ b/app/src/main/java/net/lightbody/bmp/mitm/TrustSource.java @@ -7,7 +7,7 @@ import java.io.File; import java.io.IOException; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.KeyStore; import java.security.cert.X509Certificate; import java.util.List; @@ -174,7 +174,7 @@ public TrustSource add(File trustedCAPemFile) { String pemFileContents; try { - pemFileContents = Files.toString(trustedCAPemFile, Charset.forName("UTF-8")); + pemFileContents = Files.toString(trustedCAPemFile, StandardCharsets.UTF_8); } catch (IOException e) { throw new UncheckedIOException("Unable to read file containing PEM-encoded trusted CAs: " + trustedCAPemFile.getAbsolutePath(), e); } diff --git a/app/src/main/java/net/lightbody/bmp/mitm/exception/CertificateCreationException.java b/app/src/main/java/net/lightbody/bmp/mitm/exception/CertificateCreationException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/exception/CertificateSourceException.java b/app/src/main/java/net/lightbody/bmp/mitm/exception/CertificateSourceException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/exception/ExportException.java b/app/src/main/java/net/lightbody/bmp/mitm/exception/ExportException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/exception/ImportException.java b/app/src/main/java/net/lightbody/bmp/mitm/exception/ImportException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/exception/KeyGeneratorException.java b/app/src/main/java/net/lightbody/bmp/mitm/exception/KeyGeneratorException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/exception/KeyStoreAccessException.java b/app/src/main/java/net/lightbody/bmp/mitm/exception/KeyStoreAccessException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/exception/MitmException.java b/app/src/main/java/net/lightbody/bmp/mitm/exception/MitmException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/exception/SslContextInitializationException.java b/app/src/main/java/net/lightbody/bmp/mitm/exception/SslContextInitializationException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/exception/TrustSourceException.java b/app/src/main/java/net/lightbody/bmp/mitm/exception/TrustSourceException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/exception/UncheckedIOException.java b/app/src/main/java/net/lightbody/bmp/mitm/exception/UncheckedIOException.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/keys/ECKeyGenerator.java b/app/src/main/java/net/lightbody/bmp/mitm/keys/ECKeyGenerator.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/keys/KeyGenerator.java b/app/src/main/java/net/lightbody/bmp/mitm/keys/KeyGenerator.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/keys/RSAKeyGenerator.java b/app/src/main/java/net/lightbody/bmp/mitm/keys/RSAKeyGenerator.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/manager/ImpersonatingMitmManager.java b/app/src/main/java/net/lightbody/bmp/mitm/manager/ImpersonatingMitmManager.java old mode 100644 new mode 100755 index 5e62049..6e14426 --- a/app/src/main/java/net/lightbody/bmp/mitm/manager/ImpersonatingMitmManager.java +++ b/app/src/main/java/net/lightbody/bmp/mitm/manager/ImpersonatingMitmManager.java @@ -291,7 +291,7 @@ private SslContext createImpersonatingSslContext(CertificateInfo certificateInfo // to impersonate the real upstream server, and will use the private key to encrypt the channel. KeyPair serverKeyPair = serverKeyGenerator.generate(); - // get the CA root certificate and private key that will be used to sign the forced certificate + // get the CA root certificate and private key that will be used to sign the forged certificate X509Certificate caRootCertificate = rootCertificate.get().getCertificate(); PrivateKey caPrivateKey = rootCertificate.get().getPrivateKey(); if (caRootCertificate == null || caPrivateKey == null) { diff --git a/app/src/main/java/net/lightbody/bmp/mitm/stats/CertificateGenerationStatistics.java b/app/src/main/java/net/lightbody/bmp/mitm/stats/CertificateGenerationStatistics.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/tools/BouncyCastleSecurityProviderTool.java b/app/src/main/java/net/lightbody/bmp/mitm/tools/BouncyCastleSecurityProviderTool.java old mode 100644 new mode 100755 index 1d0ffb6..ae27cf5 --- a/app/src/main/java/net/lightbody/bmp/mitm/tools/BouncyCastleSecurityProviderTool.java +++ b/app/src/main/java/net/lightbody/bmp/mitm/tools/BouncyCastleSecurityProviderTool.java @@ -1,14 +1,12 @@ package net.lightbody.bmp.mitm.tools; import com.google.common.net.InetAddresses; - import net.lightbody.bmp.mitm.CertificateAndKey; import net.lightbody.bmp.mitm.CertificateInfo; import net.lightbody.bmp.mitm.exception.CertificateCreationException; import net.lightbody.bmp.mitm.exception.ExportException; import net.lightbody.bmp.mitm.exception.ImportException; import net.lightbody.bmp.mitm.util.EncryptionUtil; - import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; @@ -42,6 +40,7 @@ import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import javax.net.ssl.KeyManager; import java.io.File; import java.io.IOException; import java.io.Reader; @@ -59,8 +58,6 @@ import java.util.ArrayList; import java.util.List; -import javax.net.ssl.KeyManager; - public class BouncyCastleSecurityProviderTool implements SecurityProviderTool { static { Security.addProvider(new BouncyCastleProvider()); @@ -215,8 +212,7 @@ public String encodeCertificateAsPem(Certificate certificate) { @Override public PrivateKey decodePemEncodedPrivateKey(Reader privateKeyReader, String password) { - try { - PEMParser pemParser = new PEMParser(privateKeyReader); + try (PEMParser pemParser = new PEMParser(privateKeyReader)) { Object keyPair = pemParser.readObject(); // retrieve the PrivateKeyInfo from the returned keyPair object. if the key is encrypted, it needs to be @@ -379,8 +375,7 @@ private static SubjectKeyIdentifier createSubjectKeyIdentifier(Key key) { private static String encodeObjectAsPemString(Object object, PEMEncryptor encryptor) { StringWriter stringWriter = new StringWriter(); - try { - JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter); + try (JcaPEMWriter pemWriter = new JcaPEMWriter(stringWriter)) { pemWriter.writeObject(object, encryptor); pemWriter.flush(); } catch (IOException e) { diff --git a/app/src/main/java/net/lightbody/bmp/mitm/tools/DefaultSecurityProviderTool.java b/app/src/main/java/net/lightbody/bmp/mitm/tools/DefaultSecurityProviderTool.java old mode 100644 new mode 100755 index 8696148..501f5a6 --- a/app/src/main/java/net/lightbody/bmp/mitm/tools/DefaultSecurityProviderTool.java +++ b/app/src/main/java/net/lightbody/bmp/mitm/tools/DefaultSecurityProviderTool.java @@ -1,13 +1,13 @@ package net.lightbody.bmp.mitm.tools; import com.google.common.io.CharStreams; - import net.lightbody.bmp.mitm.CertificateAndKey; import net.lightbody.bmp.mitm.CertificateInfo; import net.lightbody.bmp.mitm.exception.ImportException; import net.lightbody.bmp.mitm.exception.KeyStoreAccessException; import net.lightbody.bmp.mitm.util.KeyStoreUtil; +import javax.net.ssl.KeyManager; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; @@ -15,7 +15,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.Reader; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.KeyPair; import java.security.KeyStore; import java.security.KeyStoreException; @@ -26,8 +26,6 @@ import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import javax.net.ssl.KeyManager; - /** * A {@link SecurityProviderTool} implementation that uses the default system Security provider where possible, but uses the * Bouncy Castle provider for operations that the JCA does not provide or implement (e.g. certificate generation and signing). @@ -103,8 +101,7 @@ public X509Certificate decodePemEncodedCertificate(Reader certificateReader) { // the JCA CertificateFactory takes an InputStream, so convert the reader to a stream first. converting to a String first // is not ideal, but is relatively straightforward. (PEM certificates should only contain US_ASCII-compatible characters.) - try { - InputStream certificateAsStream = new ByteArrayInputStream(CharStreams.toString(certificateReader).getBytes(Charset.forName("US-ASCII"))); + try (InputStream certificateAsStream = new ByteArrayInputStream(CharStreams.toString(certificateReader).getBytes(StandardCharsets.US_ASCII))) { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); certificate = certificateFactory.generateCertificate(certificateAsStream); } catch (CertificateException | IOException e) { @@ -135,8 +132,7 @@ public KeyStore loadKeyStore(File file, String keyStoreType, String password) { throw new KeyStoreAccessException("Unable to get KeyStore instance of type: " + keyStoreType, e); } - try { - InputStream keystoreAsStream = new FileInputStream(file); + try (InputStream keystoreAsStream = new FileInputStream(file)) { keyStore.load(keystoreAsStream, password.toCharArray()); } catch (IOException e) { throw new ImportException("Unable to read KeyStore from file: " + file.getName(), e); @@ -156,8 +152,7 @@ public KeyStore loadKeyStore(File file, String keyStoreType, String password) { */ @Override public void saveKeyStore(File file, KeyStore keyStore, String keystorePassword) { - try { - FileOutputStream fos = new FileOutputStream(file); + try (FileOutputStream fos = new FileOutputStream(file)) { keyStore.store(fos, keystorePassword.toCharArray()); } catch (CertificateException | NoSuchAlgorithmException | IOException | KeyStoreException e) { throw new KeyStoreAccessException("Unable to save KeyStore to file: " + file.getName(), e); diff --git a/app/src/main/java/net/lightbody/bmp/mitm/tools/SecurityProviderTool.java b/app/src/main/java/net/lightbody/bmp/mitm/tools/SecurityProviderTool.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureExtendedTrustManager.java b/app/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureExtendedTrustManager.java old mode 100644 new mode 100755 index 1595781..14d23f8 --- a/app/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureExtendedTrustManager.java +++ b/app/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureExtendedTrustManager.java @@ -1,8 +1,12 @@ package net.lightbody.bmp.mitm.trustmanager; +import io.netty.util.internal.EmptyArrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; import java.net.Socket; import java.security.KeyStore; import java.security.KeyStoreException; @@ -10,12 +14,7 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; - import cn.darkal.networkdiagnosis.Utils.X509ExtendedTrustManager; -import io.netty.util.internal.EmptyArrays; /** * An {@link X509ExtendedTrustManager} and {@link javax.net.ssl.X509TrustManager} that will accept all server and client @@ -25,11 +24,6 @@ public class InsecureExtendedTrustManager extends X509ExtendedTrustManager { private static final Logger log = LoggerFactory.getLogger(InsecureExtendedTrustManager.class); - /** - * The default extended trust manager, which will be used to determine if certificates would otherwise be trusted. - */ - protected static final X509ExtendedTrustManager DEFAULT_EXTENDED_TRUST_MANAGER = getDefaultExtendedTrustManager(); - /** * An {@link X509ExtendedTrustManager} that does no certificate validation whatsoever. */ @@ -64,11 +58,16 @@ public X509Certificate[] getAcceptedIssuers() { } }; + /** + * The default extended trust manager, which will be used to determine if certificates would otherwise be trusted. + */ + protected static final X509ExtendedTrustManager DEFAULT_EXTENDED_TRUST_MANAGER = getDefaultExtendedTrustManager(); + @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { try { DEFAULT_EXTENDED_TRUST_MANAGER.checkClientTrusted(x509Certificates, s, socket); - } catch (Exception e) { + } catch (CertificateException e) { log.debug("Accepting an untrusted client certificate: {}", x509Certificates[0].getSubjectDN(), e); } } @@ -77,7 +76,7 @@ public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Soc public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { try { DEFAULT_EXTENDED_TRUST_MANAGER.checkServerTrusted(x509Certificates, s, socket); - } catch (Exception e) { + } catch (CertificateException e) { log.debug("Accepting an untrusted server certificate: {}", x509Certificates[0].getSubjectDN(), e); } } @@ -86,7 +85,7 @@ public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Soc public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException { try { DEFAULT_EXTENDED_TRUST_MANAGER.checkClientTrusted(x509Certificates, s, sslEngine); - } catch (Exception e) { + } catch (CertificateException e) { log.debug("Accepting an untrusted client certificate: {}", x509Certificates[0].getSubjectDN(), e); } } @@ -95,7 +94,7 @@ public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSL public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException { try { DEFAULT_EXTENDED_TRUST_MANAGER.checkServerTrusted(x509Certificates, s, sslEngine); - } catch (Exception e) { + } catch (CertificateException e) { log.debug("Accepting an untrusted server certificate: {}", x509Certificates[0].getSubjectDN(), e); } } @@ -104,7 +103,7 @@ public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSL public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { try { DEFAULT_EXTENDED_TRUST_MANAGER.checkClientTrusted(x509Certificates, s); - } catch (Exception e) { + } catch (CertificateException e) { log.debug("Accepting an untrusted client certificate: {}", x509Certificates[0].getSubjectDN(), e); } } @@ -113,7 +112,7 @@ public void checkClientTrusted(X509Certificate[] x509Certificates, String s) thr public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { try { DEFAULT_EXTENDED_TRUST_MANAGER.checkServerTrusted(x509Certificates, s); - } catch (Exception e) { + } catch (CertificateException e) { log.debug("Accepting an untrusted server certificate: {}", x509Certificates[0].getSubjectDN(), e); } } diff --git a/app/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureTrustManagerFactory.java b/app/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureTrustManagerFactory.java old mode 100644 new mode 100755 index 9087acb..176e02b --- a/app/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureTrustManagerFactory.java +++ b/app/src/main/java/net/lightbody/bmp/mitm/trustmanager/InsecureTrustManagerFactory.java @@ -16,12 +16,12 @@ package net.lightbody.bmp.mitm.trustmanager; +import cn.darkal.networkdiagnosis.Utils.X509ExtendedTrustManager; import io.netty.handler.ssl.util.SimpleTrustManagerFactory; import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; -import cn.darkal.networkdiagnosis.Utils.X509ExtendedTrustManager; import java.security.KeyStore; /** diff --git a/app/src/main/java/net/lightbody/bmp/mitm/util/EncryptionUtil.java b/app/src/main/java/net/lightbody/bmp/mitm/util/EncryptionUtil.java old mode 100644 new mode 100755 index 44eb67a..8c4f8db --- a/app/src/main/java/net/lightbody/bmp/mitm/util/EncryptionUtil.java +++ b/app/src/main/java/net/lightbody/bmp/mitm/util/EncryptionUtil.java @@ -5,6 +5,7 @@ import org.apache.commons.io.FileUtils; +import javax.crypto.Cipher; import java.io.File; import java.io.IOException; import java.math.BigInteger; @@ -16,8 +17,6 @@ import java.security.interfaces.RSAKey; import java.util.Random; -import javax.crypto.Cipher; - /** * A collection of simple JCA-related utilities. */ diff --git a/app/src/main/java/net/lightbody/bmp/mitm/util/KeyStoreUtil.java b/app/src/main/java/net/lightbody/bmp/mitm/util/KeyStoreUtil.java old mode 100644 new mode 100755 index da987d2..7edfc47 --- a/app/src/main/java/net/lightbody/bmp/mitm/util/KeyStoreUtil.java +++ b/app/src/main/java/net/lightbody/bmp/mitm/util/KeyStoreUtil.java @@ -28,7 +28,7 @@ public class KeyStoreUtil { */ public static KeyStore createEmptyKeyStore(String keyStoreType, String provider) { if (keyStoreType == null) { - keyStoreType = "BKS"; + keyStoreType = KeyStore.getDefaultType(); } KeyStore keyStore; diff --git a/app/src/main/java/net/lightbody/bmp/mitm/util/MitmConstants.java b/app/src/main/java/net/lightbody/bmp/mitm/util/MitmConstants.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java b/app/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java old mode 100644 new mode 100755 index 85f34fd..198f194 --- a/app/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java +++ b/app/src/main/java/net/lightbody/bmp/mitm/util/SslUtil.java @@ -3,19 +3,25 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.io.CharStreams; - +import io.netty.handler.ssl.OpenSsl; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.SupportedCipherSuiteFilter; +import net.lightbody.bmp.mitm.trustmanager.InsecureTrustManagerFactory; import net.lightbody.bmp.mitm.TrustSource; import net.lightbody.bmp.mitm.exception.SslContextInitializationException; -import net.lightbody.bmp.mitm.trustmanager.InsecureTrustManagerFactory; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Arrays; @@ -23,16 +29,6 @@ import java.util.Collections; import java.util.List; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLException; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; - -import io.netty.handler.ssl.OpenSsl; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslContextBuilder; -import io.netty.handler.ssl.SupportedCipherSuiteFilter; - /** * Utility for creating SSLContexts. */ @@ -173,7 +169,7 @@ public static List getBuiltInCipherList() { return Collections.emptyList(); } - Reader reader = new InputStreamReader(cipherListStream, Charset.forName("UTF-8")); + Reader reader = new InputStreamReader(cipherListStream, StandardCharsets.UTF_8); return CharStreams.readLines(reader); } catch (IOException e) { diff --git a/app/src/main/java/net/lightbody/bmp/mitm/util/TrustUtil.java b/app/src/main/java/net/lightbody/bmp/mitm/util/TrustUtil.java old mode 100644 new mode 100755 index 01827ee..156f910 --- a/app/src/main/java/net/lightbody/bmp/mitm/util/TrustUtil.java +++ b/app/src/main/java/net/lightbody/bmp/mitm/util/TrustUtil.java @@ -2,19 +2,20 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; - import net.lightbody.bmp.mitm.exception.KeyStoreAccessException; import net.lightbody.bmp.mitm.exception.TrustSourceException; import net.lightbody.bmp.mitm.exception.UncheckedIOException; import net.lightbody.bmp.mitm.tools.DefaultSecurityProviderTool; import net.lightbody.bmp.mitm.tools.SecurityProviderTool; import net.lightbody.bmp.util.ClasspathResourceUtil; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; import java.io.StringReader; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; @@ -27,10 +28,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - /** * Utility class for interacting with the default trust stores on this JVM. */ @@ -84,7 +81,7 @@ public X509Certificate[] get() { public X509Certificate[] get() { try { // the file may contain UTF-8 characters, but the PEM-encoded certificate data itself must be US-ASCII - String allCAs = ClasspathResourceUtil.classpathResourceToString(DEFAULT_TRUSTED_CA_RESOURCE, Charset.forName("UTF-8")); + String allCAs = ClasspathResourceUtil.classpathResourceToString(DEFAULT_TRUSTED_CA_RESOURCE, StandardCharsets.UTF_8); return readX509CertificatesFromPem(allCAs); } catch (UncheckedIOException e) { diff --git a/app/src/main/java/net/lightbody/bmp/proxy/ActivityMonitor.java b/app/src/main/java/net/lightbody/bmp/proxy/ActivityMonitor.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/BlacklistEntry.java b/app/src/main/java/net/lightbody/bmp/proxy/BlacklistEntry.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/CaptureType.java b/app/src/main/java/net/lightbody/bmp/proxy/CaptureType.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/RewriteRule.java b/app/src/main/java/net/lightbody/bmp/proxy/RewriteRule.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/Whitelist.java b/app/src/main/java/net/lightbody/bmp/proxy/Whitelist.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/auth/AuthType.java b/app/src/main/java/net/lightbody/bmp/proxy/auth/AuthType.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/dns/AbstractHostNameRemapper.java b/app/src/main/java/net/lightbody/bmp/proxy/dns/AbstractHostNameRemapper.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/dns/AdvancedHostResolver.java b/app/src/main/java/net/lightbody/bmp/proxy/dns/AdvancedHostResolver.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/dns/BasicHostResolver.java b/app/src/main/java/net/lightbody/bmp/proxy/dns/BasicHostResolver.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/dns/ChainedHostResolver.java b/app/src/main/java/net/lightbody/bmp/proxy/dns/ChainedHostResolver.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/dns/DelegatingHostResolver.java b/app/src/main/java/net/lightbody/bmp/proxy/dns/DelegatingHostResolver.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/dns/DnsJavaResolver.java b/app/src/main/java/net/lightbody/bmp/proxy/dns/DnsJavaResolver.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/dns/HostResolver.java b/app/src/main/java/net/lightbody/bmp/proxy/dns/HostResolver.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/dns/NativeCacheManipulatingResolver.java b/app/src/main/java/net/lightbody/bmp/proxy/dns/NativeCacheManipulatingResolver.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/proxy/dns/NativeResolver.java b/app/src/main/java/net/lightbody/bmp/proxy/dns/NativeResolver.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java b/app/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java old mode 100644 new mode 100755 index ab866cc..5e00891 --- a/app/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java +++ b/app/src/main/java/net/lightbody/bmp/util/BrowserMobHttpUtil.java @@ -1,9 +1,8 @@ package net.lightbody.bmp.util; +import com.google.common.io.BaseEncoding; import com.google.common.net.HostAndPort; import com.google.common.net.MediaType; - -import cn.darkal.networkdiagnosis.Utils.DatatypeConverter; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpRequest; @@ -19,6 +18,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.zip.GZIPInputStream; import java.util.zip.InflaterInputStream; @@ -42,14 +42,18 @@ public class BrowserMobHttpUtil { public static final String UNKNOWN_CONTENT_TYPE = "application/octet-stream"; /** - * The default charset when the Content-Type header does not specify a charset. From the HTTP 1.1 spec section 3.7.1: + * The default charset when the Content-Type header does not specify a charset. According to RFC 7231 Appendix B: *

-     *     When no explicit charset parameter is provided by the sender, media subtypes of the "text" type are defined to have a default
-     *     charset value of "ISO-8859-1" when received via HTTP. Data in character sets other than "ISO-8859-1" or its subsets MUST be
-     *     labeled with an appropriate charset value.
+     *     The default charset of ISO-8859-1 for text media types has been
+     *     removed; the default is now whatever the media type definition says.
+     *     Likewise, special treatment of ISO-8859-1 has been removed from the
+     *     Accept-Charset header field.
      * 
+ * + * Technically, we would have to determine the charset on a per-content-type basis, but generally speaking, UTF-8 is a + * pretty safe default. (NOTE: In the previous HTTP/1.1 spec, section 3.7.1, the default charset was defined as ISO-8859-1.) */ - public static final Charset DEFAULT_HTTP_CHARSET = Charset.forName("ISO-8859-1"); + public static final Charset DEFAULT_HTTP_CHARSET = StandardCharsets.UTF_8; /** * Buffer size when decompressing content. @@ -290,7 +294,7 @@ public static String base64EncodeBasicCredentials(String username, String passwo String credentialsToEncode = username + ':' + password; // using UTF-8, which is the modern de facto standard, and which retains compatibility with US_ASCII for ASCII characters, // as required by RFC 7616, section 3: http://tools.ietf.org/html/rfc7617#section-3 - byte[] credentialsAsUtf8Bytes = credentialsToEncode.getBytes(Charset.forName("UTF-8")); - return new String(DatatypeConverter.parseBase64Binary(new String(credentialsAsUtf8Bytes))); + byte[] credentialsAsUtf8Bytes = credentialsToEncode.getBytes(StandardCharsets.UTF_8); + return BaseEncoding.base64().encode(credentialsAsUtf8Bytes); } } diff --git a/app/src/main/java/net/lightbody/bmp/util/BrowserMobProxyUtil.java b/app/src/main/java/net/lightbody/bmp/util/BrowserMobProxyUtil.java old mode 100644 new mode 100755 index f242fb0..62f33cf --- a/app/src/main/java/net/lightbody/bmp/util/BrowserMobProxyUtil.java +++ b/app/src/main/java/net/lightbody/bmp/util/BrowserMobProxyUtil.java @@ -7,12 +7,10 @@ import net.lightbody.bmp.core.har.HarLog; import net.lightbody.bmp.core.har.HarPage; import net.lightbody.bmp.mitm.exception.UncheckedIOException; -import net.sf.uadetector.UserAgentStringParser; -import net.sf.uadetector.service.UADetectorServiceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.HashSet; import java.util.Set; @@ -32,11 +30,6 @@ public class BrowserMobProxyUtil { */ private static final String UNKNOWN_VERSION_STRING = "UNKNOWN-VERSION"; - /** - * Singleton User Agent parser. - */ - private static volatile UserAgentStringParser parser; - /** * Singleton version string loader. */ @@ -47,27 +40,6 @@ public String get() { } }); - private static final Object PARSER_INIT_LOCK = new Object(); - - /** - * Retrieve the User Agent String Parser. Create the parser if it has not yet been initialized. - * - * @return singleton UserAgentStringParser object - */ - public static UserAgentStringParser getUserAgentStringParser() { - if (parser == null) { - synchronized (PARSER_INIT_LOCK) { - if (parser == null) { - // using resourceModuleParser for now because user-agent-string.info no longer exists. the updating - // parser will get incorrect data and wipe out its entire user agent repository. - parser = UADetectorServiceFactory.getResourceModuleParser(); - } - } - } - - return parser; - } - /** * Copies {@link HarEntry} and {@link HarPage} references from the specified har to a new har copy, up to and including * the specified pageRef. Does not perform a "deep copy", so any subsequent modification to the entries or pages will @@ -136,7 +108,7 @@ public static String getVersionString() { private static String readVersionFileOnClasspath() { String versionString; try { - versionString = ClasspathResourceUtil.classpathResourceToString(VERSION_CLASSPATH_RESOURCE, Charset.forName("UTF-8")); + versionString = ClasspathResourceUtil.classpathResourceToString(VERSION_CLASSPATH_RESOURCE, StandardCharsets.UTF_8); } catch (UncheckedIOException e) { log.debug("Unable to load version from classpath resource: {}", VERSION_CLASSPATH_RESOURCE, e); return UNKNOWN_VERSION_STRING; diff --git a/app/src/main/java/net/lightbody/bmp/util/ClasspathResourceUtil.java b/app/src/main/java/net/lightbody/bmp/util/ClasspathResourceUtil.java old mode 100644 new mode 100755 index 41e1f54..66bd3b1 --- a/app/src/main/java/net/lightbody/bmp/util/ClasspathResourceUtil.java +++ b/app/src/main/java/net/lightbody/bmp/util/ClasspathResourceUtil.java @@ -34,8 +34,7 @@ public static String classpathResourceToString(String resource, Charset charset) throw new IllegalArgumentException("Character set cannot be null"); } - try { - InputStream resourceAsStream = ClasspathResourceUtil.class.getResourceAsStream(resource); + try (InputStream resourceAsStream = ClasspathResourceUtil.class.getResourceAsStream(resource)) { if (resourceAsStream == null) { throw new UncheckedIOException(new FileNotFoundException("Unable to locate classpath resource: " + resource)); } diff --git a/app/src/main/java/net/lightbody/bmp/util/HttpMessageContents.java b/app/src/main/java/net/lightbody/bmp/util/HttpMessageContents.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/util/HttpMessageInfo.java b/app/src/main/java/net/lightbody/bmp/util/HttpMessageInfo.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/lightbody/bmp/util/HttpObjectUtil.java b/app/src/main/java/net/lightbody/bmp/util/HttpObjectUtil.java old mode 100644 new mode 100755 index 169a878..bb98333 --- a/app/src/main/java/net/lightbody/bmp/util/HttpObjectUtil.java +++ b/app/src/main/java/net/lightbody/bmp/util/HttpObjectUtil.java @@ -4,6 +4,8 @@ import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMessage; +import io.netty.handler.codec.http.LastHttpContent; + import net.lightbody.bmp.exception.UnsupportedCharsetException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,6 +54,31 @@ public static void replaceTextHttpEntityBody(FullHttpMessage message, String new replaceBinaryHttpEntityBody(message, contentBytes); } + + public static void replaceTextHttpEntityBody(LastHttpContent message, String newContents) { + // get the content type for this message so we can encode the newContents into a byte stream appropriately + String contentTypeHeader = message.trailingHeaders().get(HttpHeaders.Names.CONTENT_TYPE); + + Charset messageCharset; + try { + messageCharset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentTypeHeader); + } catch (UnsupportedCharsetException e) { + java.nio.charset.UnsupportedCharsetException cause = e.getUnsupportedCharsetExceptionCause() ; + log.error("Found unsupported character set in Content-Type header '{}' while attempting to replace contents of HTTP message.", contentTypeHeader, cause); + + throw cause; + } + + if (messageCharset == null) { + messageCharset = BrowserMobHttpUtil.DEFAULT_HTTP_CHARSET; + log.warn("No character set declared in HTTP message. Replacing text using default charset {}.", messageCharset); + } + + byte[] contentBytes = newContents.getBytes(messageCharset); + + replaceBinaryHttpEntityBody(message, contentBytes); + } + /** * Replaces an HTTP entity body with the specified binary contents. * TODO: Currently this method only works for FullHttpMessages, since it must modify the Content-Length header; determine if this may be applied to chunked messages as well @@ -69,6 +96,17 @@ public static void replaceBinaryHttpEntityBody(FullHttpMessage message, byte[] n message.headers().set(HttpHeaders.Names.CONTENT_LENGTH, newBinaryContents.length); } + public static void replaceBinaryHttpEntityBody(LastHttpContent message, byte[] newBinaryContents) { +// message.content().capacity(newBinaryContents.length); + message.content().clear(); + // resize the buffer if needed, since the new message may be longer than the old one + message.content().ensureWritable(newBinaryContents.length, true); + message.content().writeBytes(newBinaryContents); + + // update the Content-Length header, since the size may have changed +// message.trailingHeaders().set(HttpHeaders.Names.CONTENT_LENGTH, newBinaryContents.length); + } + /** * Extracts the entity body from an HTTP content object, according to the specified character set. The character set cannot be null. If * the character set is not specified or is unknown, you still must specify a suitable default charset (see {@link BrowserMobHttpUtil#DEFAULT_HTTP_CHARSET}). diff --git a/app/src/main/java/net/lightbody/bmp/util/HttpUtil.java b/app/src/main/java/net/lightbody/bmp/util/HttpUtil.java old mode 100644 new mode 100755 diff --git a/app/src/main/java/net/sf/uadetector/DeviceCategory.java b/app/src/main/java/net/sf/uadetector/DeviceCategory.java deleted file mode 100644 index c1df405..0000000 --- a/app/src/main/java/net/sf/uadetector/DeviceCategory.java +++ /dev/null @@ -1,199 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import java.io.Serializable; - -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.NotThreadSafe; - -import net.sf.qualitycheck.Check; - -@Immutable -public final class DeviceCategory implements ReadableDeviceCategory, Serializable { - - @NotThreadSafe - public static final class Builder { - - private Category category; - - private String icon; - - private String infoUrl; - - private String name; - - public Builder() { - // default constructor - } - - public Builder(@Nonnull final DeviceCategory deviceCategory) { - Check.notNull(deviceCategory, "deviceCategory"); - category = Check.notNull(deviceCategory.getCategory(), "deviceCategory.getCategory()"); - icon = Check.notNull(deviceCategory.getIcon(), "deviceCategory.getIcon()"); - infoUrl = Check.notNull(deviceCategory.getInfoUrl(), "deviceCategory.getInfoUrl()"); - name = Check.notNull(deviceCategory.getName(), "deviceCategory.getName()"); - } - - @Nonnull - public DeviceCategory build() { - return new DeviceCategory(category, icon, infoUrl, name); - } - - @Nonnull - public Builder setCategory(@Nonnull final Category category) { - this.category = Check.notNull(category, "category"); - return this; - } - - @Nonnull - public Builder setIcon(@Nonnull final String icon) { - this.icon = Check.notNull(icon, "icon"); - return this; - } - - @Nonnull - public Builder setInfoUrl(@Nonnull final String infoUrl) { - this.infoUrl = Check.notNull(infoUrl, "infoUrl"); - return this; - } - - @Nonnull - public Builder setName(@Nonnull final String name) { - this.name = Check.notNull(name, "name"); - return this; - } - - } - - private static final long serialVersionUID = 1L; - - /** - * Represents a not set device category. - */ - public static final DeviceCategory EMPTY = new DeviceCategory(); - - private static int buildHashCode(@Nonnull final Category category, @Nonnull final String icon, @Nonnull final String infoUrl, - @Nonnull final String name) { - final int prime = 31; - int result = 1; - result = prime * result + category.hashCode(); - result = prime * result + icon.hashCode(); - result = prime * result + infoUrl.hashCode(); - result = prime * result + name.hashCode(); - return result; - } - - @Nonnull - private final Category category; - - @Nonnull - private final String icon; - - @Nonnull - private final String infoUrl; - - @Nonnull - private final String name; - - private final int hash; - - /** - * Builds an instance that represents an empty device category. - *

- * Attention: This is only intended to build one instance at runtime to represent value behind the constant - * {@link #EMPTY}. - */ - private DeviceCategory() { - category = Category.UNKNOWN; - icon = ""; - infoUrl = ""; - name = ""; - hash = buildHashCode(category, icon, infoUrl, name); - } - - public DeviceCategory(@Nonnull final Category category, @Nonnull final String icon, @Nonnull final String infoUrl, - @Nonnull final String name) { - this.category = Check.notNull(category, "category"); - this.icon = Check.notNull(icon, "icon"); - this.infoUrl = Check.notNull(infoUrl, "infoUrl"); - this.name = Check.notEmpty(name, "name"); - hash = buildHashCode(category, icon, infoUrl, name); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final DeviceCategory other = (DeviceCategory) obj; - if (!category.equals(other.category)) { - return false; - } - if (!icon.equals(other.icon)) { - return false; - } - if (!infoUrl.equals(other.infoUrl)) { - return false; - } - if (!name.equals(other.name)) { - return false; - } - return true; - } - - @Override - @Nonnull - public Category getCategory() { - return category; - } - - @Override - @Nonnull - public String getIcon() { - return icon; - } - - @Override - @Nonnull - public String getInfoUrl() { - return infoUrl; - } - - @Override - @Nonnull - public String getName() { - return name; - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public String toString() { - return "DeviceCategory [category=" + category + ", icon=" + icon + ", infoUrl=" + infoUrl + ", name=" + name + "]"; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/OperatingSystem.java b/app/src/main/java/net/sf/uadetector/OperatingSystem.java deleted file mode 100644 index ece215c..0000000 --- a/app/src/main/java/net/sf/uadetector/OperatingSystem.java +++ /dev/null @@ -1,203 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import java.io.Serializable; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; - -/** - * {@code OperatingSystem} is an immutable entity that represents the informations about an operating system like Linux, - * Mac OS X or Windows. - * - * @author André Rouél - */ -public final class OperatingSystem implements ReadableOperatingSystem, Serializable { - - public static final OperatingSystem EMPTY = new OperatingSystem(OperatingSystemFamily.UNKNOWN, "unknown", "unknown.png", "unknown", "", - "", "", VersionNumber.UNKNOWN); - - /** - * Serialization version - */ - private static final long serialVersionUID = 1L; - - @Nonnull - private final OperatingSystemFamily family; - - @Nonnull - private final String familyName; - - @Nonnull - private final String icon; - - @Nonnull - private final String name; - - @Nonnull - private final String producer; - - @Nonnull - private final String producerUrl; - - @Nonnull - private final String url; - - @Nonnull - private final VersionNumber versionNumber; - - public OperatingSystem(@Nonnull final OperatingSystemFamily family, @Nonnull final String familyName, @Nonnull final String icon, - @Nonnull final String name, @Nonnull final String producer, @Nonnull final String producerUrl, @Nonnull final String url, - @Nonnull final VersionNumber versionNumber) { - Check.notNull(family, "family"); - Check.notNull(familyName, "familyName"); - Check.notNull(icon, "icon"); - Check.notNull(name, "name"); - Check.notNull(producer, "producer"); - Check.notNull(producerUrl, "producerUrl"); - Check.notNull(url, "url"); - Check.notNull(versionNumber, "versionNumber"); - - this.family = family; - this.familyName = familyName; - this.icon = icon; - this.name = name; - this.producer = producer; - this.producerUrl = producerUrl; - this.url = url; - this.versionNumber = versionNumber; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final OperatingSystem other = (OperatingSystem) obj; - if (family != other.family) { - return false; - } - if (!familyName.equals(other.familyName)) { - return false; - } - if (!icon.equals(other.icon)) { - return false; - } - if (!name.equals(other.name)) { - return false; - } - if (!producer.equals(other.producer)) { - return false; - } - if (!producerUrl.equals(other.producerUrl)) { - return false; - } - if (!url.equals(other.url)) { - return false; - } - if (!versionNumber.equals(other.versionNumber)) { - return false; - } - return true; - } - - @Override - public OperatingSystemFamily getFamily() { - return family; - } - - @Override - public String getFamilyName() { - return familyName; - } - - @Override - public String getIcon() { - return icon; - } - - @Override - public String getName() { - return name; - } - - @Override - public String getProducer() { - return producer; - } - - @Override - public String getProducerUrl() { - return producerUrl; - } - - @Override - public String getUrl() { - return url; - } - - @Override - public VersionNumber getVersionNumber() { - return versionNumber; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + family.hashCode(); - result = prime * result + familyName.hashCode(); - result = prime * result + icon.hashCode(); - result = prime * result + name.hashCode(); - result = prime * result + producer.hashCode(); - result = prime * result + producerUrl.hashCode(); - result = prime * result + url.hashCode(); - result = prime * result + versionNumber.hashCode(); - return result; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("OperatingSystem [family="); - builder.append(family); - builder.append(", familyName="); - builder.append(familyName); - builder.append(", icon="); - builder.append(icon); - builder.append(", name="); - builder.append(name); - builder.append(", producer="); - builder.append(producer); - builder.append(", producerUrl="); - builder.append(producerUrl); - builder.append(", url="); - builder.append(url); - builder.append(", versionNumber="); - builder.append(versionNumber); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/OperatingSystemFamily.java b/app/src/main/java/net/sf/uadetector/OperatingSystemFamily.java deleted file mode 100644 index a755390..0000000 --- a/app/src/main/java/net/sf/uadetector/OperatingSystemFamily.java +++ /dev/null @@ -1,394 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; - -/** - * This enum represents the more commonly used operating system families. It will never be complete, but can assist in - * identifying the version of an operating system. - * - * @author André Rouél - */ -public enum OperatingSystemFamily { - - /** - * AIX (Advanced Interactive eXecutive) is a Unix operating system from IBM. - */ - AIX("AIX", Pattern.compile("AIX")), - - /** - * AROS is a free operating system aiming at being compatible with AmigaOS at the API level. - */ - AROS("AROS", Pattern.compile("AROS")), - - /** - * AmigaOS is the native operating system for the Commodore Amiga, consisting of the components of Workbench, - * AmigaDOS with the command line interpreter CLI (later renamed to shell) and for many Amiga models in the ROM - * included kernel kickstart. - */ - AMIGA_OS("Amiga OS", Pattern.compile("Amiga OS")), - - /** - * Android is both an operating system and a software platform for mobile devices like smart phones, mobile phones, - * netbooks and tablets, which is developed by the Open Handset Alliance. - */ - ANDROID("Android", Pattern.compile("Android")), - - /** - * The Berkeley Software Distribution (BSD) is a version of the Unix operating system, which was created at the - * University of California at Berkeley in 1977. - */ - BSD("BSD", Pattern.compile("BSD")), - - /** - * Bada is a service-oriented operating system that is developed by Samsung Electronics and is designed for use in - * smartphones. - */ - BADA("Bada", Pattern.compile("Bada")), - - /** - * Be Operating System (BeOS) was an operating system of the company Be Incorporated and was called in later - * versions Be. Due to its multimedia capabilities it is also commonly called "Media OS". - */ - BEOS("BeOS", Pattern.compile("BeOS")), - - /** - * Chrome OS is an operating system based on the Linux kernel and designed by Google to work with web applications and installed applications. - */ - CHROME_OS("Chrome OS", Pattern.compile("Chome OS")), - - /** - * Danger OS is a smartphone operating system. It is used on Sidekick devices, which are sold in Germany by - * T-Mobile. - */ - DANGEROS("DangerOS", Pattern.compile("DangerOS")), - - /** - * Firefox OS is an open source operating system for smartphones and tablet computers being developed by Mozilla. - */ - FIREFOX_OS("Firefox OS", Pattern.compile("Firefox OS")), - - /** - * HP-UX (Hewlett Packard UniX) is a commercial Unix operating system from Hewlett-Packard and is based on UNIX - * System V. - */ - HPUX("HP-UX", Pattern.compile("HP-UX")), - - /** - * Haiku (formerly OpenBeOS) is an open-source project with the aim, to reprogram and expand that in 2001 abandoned - * operating system BeOS. - */ - HAIKU("Haiku OS", Pattern.compile("Haiku OS")), - - /** - * IRIX is a commercial Unix operating system of the company Silicon Graphics (SGI). - */ - IRIX("IRIX", Pattern.compile("IRIX")), - - /** - * Inferno is a distributed computer operating system that comes from Bell Laboratories. - */ - INFERNO_OS("Inferno OS", Pattern.compile("Inferno OS")), - - /** - * The Java Virtual Machine (abbreviated Java VM or JVM) is the part of the Java Runtime Environment (JRE) for Java - * programs, which is responsible for the execution of Java bytecode.
- *
- * This value is not an operating system family. - */ - JVM("JVM", Pattern.compile("JVM")), - - /** - * Linux or GNU/Linux are usually called free, unix-like multi-user operating systems running based on the Linux - * kernel and other GNU software. - */ - LINUX("Linux", Pattern.compile("Linux")), - - /** - * Mac OS is the name of the classic operating system (1984-2001) by Apple for Macintosh computers. - */ - MAC_OS("Mac OS", Pattern.compile("Mac OS")), - - /** - * Maemo is a linux based software platform for smartphones and Internet tablets. - */ - MAEMO("Maemo", Pattern.compile("Maemo")), - - /** - * MeeGo was a Linux kernel-based free mobile operating system project resulting from the fusion of Intel's Moblin - * and Nokia's Maemo operating systems. - */ - MEEGO("MeeGo", Pattern.compile("MeeGo")), - - /** - * Minix is a free unixoides operating system that was developed by Andrew S. Tanenbaum at the Free University of - * Amsterdam as a teaching tool. - */ - MINIX("MINIX", Pattern.compile("MINIX")), - - /** - * OpenVMS (Open Virtual Memory System), previously known as VAX-11/VMS, VAX/VMS or (informally) VMS, is a computer - * server operating system that runs on VAX, Alpha and Itanium-based families of computers. - */ - OPENVMS("OpenVMS", Pattern.compile("OpenVMS")), - - /** - * OS X, formerly Mac OS X, is a Unix-based operating systems developed by Apple. It is a proprietary distribution - * of the free Darwin operating system from Apple. - */ - OS_X("OS X", Pattern.compile("(Mac OS X|OS X)")), - - /** - * MorphOS is an Amiga-compatible computer operating system. It is a mixed proprietary and open source OS produced - * for the Pegasos PowerPC processor based computer. - */ - MORPHOS("MorphOS", Pattern.compile("MorphOS")), - - /** - * This value indicates the operating systems from Nintendo, which they developed for their devices.
- *
- * This value is not an operating system family. - */ - NINTENDO("Nintendo", Pattern.compile("Nintendo")), - - /** - * OS/2 is a multitasking operating system for PCs. It was originally developed by IBM and Microsoft together with - * the aim to replace DOS. - */ - OS_2("OS/2", Pattern.compile("OS/2")), - - /** - * Palm OS was the operating system for organizer of the Palm series (known as PDAs) and smartphones. - */ - PALM_OS("Palm OS", Pattern.compile("Palm OS")), - - /** - * The PlayStation Vita system software is the official, updatable firmware and operating system for the PlayStation - * Vita. - */ - PLAYSTATION_VITA("LiveArea", Pattern.compile("LiveArea")), - - /** - * QNX is a POSIX-compatible proprietary Unix-like real-time operating system that focused primarily at the embedded - * market. - */ - QNX("QNX", Pattern.compile("QNX")), - - /** - * RISC OS is a computer operating system originally designed by Acorn Computers Ltd. It was specifically designed - * to run on the ARM chipset, which Acorn had designed concurrently for use in its new line of Archimedes personal - * computers. - */ - RISC_OS("RISC OS", Pattern.compile("RISC OS|RISK OS")), - - /** - * Binary Runtime Environment for Wireless (Brew MP, Brew, or BREW) is an application development platform created - * by Qualcomm. - */ - BREW("Brew", Pattern.compile("Brew")), - - /** - * BlackBerry OS (up to the fifth edition known as the BlackBerry Device Software, also known as Research - * In Motion OS) is a proprietary mobile operating system developed by BlackBerry Ltd for its BlackBerry line of - * smartphone handheld devices. - */ - BLACKBERRY_OS("BlackBerry OS", Pattern.compile("(RIM OS|BlackBerry OS)")), - - /** - * Sailfish is a Linux-based mobile operating system developed by Jolla in cooperation with the Mer project and - * supported by the Sailfish Alliance. - */ - SAILFISH_OS("Sailfish", Pattern.compile("Sailfish")), - - /** - * Solaris is the name of an operating system distribution based on SunOS and is a Unix operating system. Since the - * takeover of Sun Microsystems in 2010 Solaris is part of Oracle. - */ - SOLARIS("Solaris", Pattern.compile("Solaris")), - - /** - * Syllable is a slim and fast desktop Unix-like operating system for x86 processors. - */ - SYLLABLE("Syllable", Pattern.compile("Syllable")), - - /** - * The Symbian platform, simply called Symbian, is an operating system for smartphones and PDAs. The Symbian - * platform is the successor to Symbian OS - */ - SYMBIAN("Symbian OS", Pattern.compile("Symbian OS")), - - /** - * Tizen is a free operating system based on Linux respectively Debian and was launched by the Linux Foundation and - * LiMo Foundation. - */ - TIZEN("Tizen", Pattern.compile("Tizen")), - - /** - * The Wii Operating System is based on Nintendo’s proprietary software and runs on the Wii video game console. - */ - WII_OS("Wii OS", Pattern.compile("Nintendo Wii|Wii OS")), - - /** - * Microsoft Windows is a trademark for operating systems of the Microsoft Corporation. Microsoft Windows was - * originally a graphical extension of the operating system MS-DOS. - */ - WINDOWS("Windows", Pattern.compile("Windows")), - - /** - * XrossMediaBar (XMB) is the name of the graphical user interface, which are used on PlayStation 3, PlayStation - * Portable, Sony Blu-Ray players and Sony Bravia TVs. Also some special versions of the PlayStation 2, PSX, already - * using the XMB. - */ - XROSSMEDIABAR("XrossMediaBar (XMB)", Pattern.compile("XrossMediaBar (XMB)")), - - /** - * iOS (until June 2010 iPhone OS) is the standard operating system of Apple products like iPhone, iPod touch, iPad, - * and the second generation of Apple TV. iOS is based on Mac OS X. - */ - IOS("iOS", Pattern.compile("iOS|iPhone OS")), - - /** - * webOS is a smartphone and tablet operating system from Hewlett-Packard (formerly HP Palm). It represents the - * follower of Palm OS. - */ - WEBOS("webOS", Pattern.compile("webOS")), - - /** - * Unknown operating system family
- *
- * This value will be returned if the operating system family cannot be determined. - */ - UNKNOWN("", Pattern.compile("^$")); - - /** - * This method try to find by the given family name a matching enum value. The family name must match against an - * operating system entry in UAS data file. - * - * @param family - * name of an operating system family - * @return the matching enum value or {@code OperatingSystemFamily#UNKNOWN} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - public static OperatingSystemFamily evaluate(@Nonnull final String family) { - Check.notNull(family, "family"); - - OperatingSystemFamily result = UNKNOWN; - - // search by name - result = evaluateByName(family); - - // search by pattern - if (result == UNKNOWN) { - result = evaluateByPattern(family); - } - - return result; - } - - /** - * This method try to find by the given family name a matching enum value. The family name will be evaluated against - * the stored name of an operating system entry. - * - * @param family - * name of an operating system family - * @return the matching enum value or {@code OperatingSystemFamily#UNKNOWN} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - protected static OperatingSystemFamily evaluateByName(@Nonnull final String family) { - Check.notNull(family, "family"); - - OperatingSystemFamily result = UNKNOWN; - for (final OperatingSystemFamily value : values()) { - if (value.getName().equals(family)) { - result = value; - break; - } - } - - return result; - } - - /** - * This method try to find by the given family name a matching enum value. The family name will be evaluated against - * the stored regular expression of an operating system entry. - * - * @param family - * name of an operating system family - * @return the matching enum value or {@code OperatingSystemFamily#UNKNOWN} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - protected static OperatingSystemFamily evaluateByPattern(@Nonnull final String family) { - Check.notNull(family, "family"); - - OperatingSystemFamily result = UNKNOWN; - for (final OperatingSystemFamily value : values()) { - final Matcher m = value.getPattern().matcher(family); - if (m.matches()) { - result = value; - break; - } - } - - return result; - } - - /** - * The internal family name in the UAS database. - */ - @Nonnull - private final String name; - - /** - * The regular expression which a family name must be match. - */ - @Nonnull - private final Pattern pattern; - - private OperatingSystemFamily(@Nonnull final String name, @Nonnull final Pattern pattern) { - this.name = name; - this.pattern = pattern; - } - - /** - * Gets the internal family name in the UAS database. - * - * @return the internal family name - */ - @Nonnull - public String getName() { - return this.name; - } - - /** - * Gets the regular expression which a family name must be match with. - * - * @return regular expression - */ - @Nonnull - public Pattern getPattern() { - return pattern; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/ReadableDeviceCategory.java b/app/src/main/java/net/sf/uadetector/ReadableDeviceCategory.java deleted file mode 100644 index 58a72fc..0000000 --- a/app/src/main/java/net/sf/uadetector/ReadableDeviceCategory.java +++ /dev/null @@ -1,163 +0,0 @@ -package net.sf.uadetector; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; - -/** - * Defines a category of devices.
- *

- * A device category is a group that is often determined by considering various aspects such as form factor, - * functionality and its application. Common to all devices within a group is that they have at least one specific user - * agent. - *

- * The implementation of this interface may be mutable or immutable. This interface only gives access to retrieve data, - * never to change it. - * - * @author André Rouél - */ -public interface ReadableDeviceCategory { - - /** - * Gets the enum value of a category. - *

- * It provides all known device categories at the time of implementing this and makes it easier and type-safe to - * query for a specific one rather the comparing the name. - * - * @return enum value of a category - */ - @Nonnull - Category getCategory(); - - /** - * Gets the icon of the category. - * - * @return icon of the category - */ - @Nonnull - String getIcon(); - - /** - * Returns the URL to get more informations behind a category. - * - * @return information URL - */ - @Nonnull - String getInfoUrl(); - - /** - * Gets the category name. - * - * @return name of the category - */ - @Nonnull - String getName(); - - /** - * Contains all at the time of implementation known device categories, so that a caller can easily and type-safe - * test against them. - */ - enum Category { - - /** - * A game console is an interactive computer that produces a video display signal which can be used with a - * display device (a television, monitor, etc.) to display a video game. The term "game console" is used to - * distinguish a machine designed for people to buy and use primarily for playing video games on a TV in - * contrast to arcade machines, handheld game consoles, or home computers. - */ - GAME_CONSOLE("Game console"), - - /** - * A device that doesn't match the other categories - */ - OTHER("Other"), - - /** - * A personal digital assistant (PDA), also known as a palmtop computer, or personal data assistant, is a mobile - * device that functions as a personal information manager. PDAs are largely considered obsolete with the - * widespread adoption of smartphones. - */ - PDA("PDA"), - - /** - * A personal computer (PC) is a general-purpose computer, whose size, capabilities, and original sale price - * makes it useful for individuals, and which is intended to be operated directly by an end-user with no - * intervening computer operator. - */ - PERSONAL_COMPUTER("Personal computer"), - - /** - * A smart TV, sometimes referred to as connected TV or hybrid TV - */ - SMART_TV("Smart TV"), - - /** - * A smartphone is a mobile phone built on a mobile operating system, with more advanced computing capability - * and connectivity than a feature phone - */ - SMARTPHONE("Smartphone"), - - /** - * A tablet computer, or simply tablet, is a mobile computer with display, circuitry and battery in a single - * unit. Tablets are often equipped with sensors, including cameras, microphone, accelerometer and touchscreen, - * with finger or stylus gestures replacing computer mouse and keyboard. - */ - TABLET("Tablet"), - - /** - * An unknown device category - */ - UNKNOWN(""), - - /** - * Wearable computers, also known as body-borne computers are miniature electronic devices that are worn by the - * bearer under, with or on top of clothing. - */ - WEARABLE_COMPUTER("Wearable computer"); - - /** - * Tries to find by the given category name a matching enum value. The category name must match against an - * device entry in the UAS data. - * - * @param categoryName - * name of an device category - * @return the matching enum value or {@link #UNKNOWN} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - public static Category evaluate(@Nonnull final String categoryName) { - Check.notNull(categoryName, "categoryName"); - - Category result = UNKNOWN; - for (final Category value : values()) { - if (value.getName().equals(categoryName)) { - result = value; - break; - } - } - return result; - } - - /** - * Name of the device category - */ - @Nonnull - private final String name; - - private Category(@Nonnull final String name) { - this.name = name; - } - - /** - * Returns the name of the device category. - * - * @return name of the category - */ - @Nonnull - public String getName() { - return name; - } - - } - -} diff --git a/app/src/main/java/net/sf/uadetector/ReadableOperatingSystem.java b/app/src/main/java/net/sf/uadetector/ReadableOperatingSystem.java deleted file mode 100644 index 038b5b1..0000000 --- a/app/src/main/java/net/sf/uadetector/ReadableOperatingSystem.java +++ /dev/null @@ -1,97 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import javax.annotation.Nonnull; - -/** - * Defines an operating system.
- *
- * An operating system (OS) is a set of software that manages the hardware of a computer and provides common services - * for programs. Popular examples of modern operating systems are Android, iOS, Linux, Mac OS X and Windows.
- *
- * The implementation of this interface may be mutable or immutable. This interface only gives access to retrieve data, - * never to change it. - * - * @author André Rouél - */ -public interface ReadableOperatingSystem { - - /** - * Gets the family of an operating system. - * - * @return the family of an operating system - */ - @Nonnull - OperatingSystemFamily getFamily(); - - /** - * Gets the family name of an operating system. - * - * @return the family of an operating system - */ - @Nonnull - String getFamilyName(); - - /** - * Gets the icon name of an operating system. - * - * @return the icon name of an operating system - */ - @Nonnull - String getIcon(); - - /** - * Gets the name of an operating system. - * - * @return the name of an operating system - */ - @Nonnull - String getName(); - - /** - * Returns the manufacturer of an operating system. - * - * @return the manufacturer - */ - @Nonnull - String getProducer(); - - /** - * Returns the URL to the main website of the manufacturer of an operating system. - * - * @return the URL to the website of the manufacturer - */ - @Nonnull - String getProducerUrl(); - - /** - * Returns the URL to the product or information page of an operating system. - * - * @return the URL to the product page - */ - @Nonnull - String getUrl(); - - /** - * Gets the version number of an operating system. - * - * @return version number an operating system - */ - @Nonnull - VersionNumber getVersionNumber(); - -} diff --git a/app/src/main/java/net/sf/uadetector/ReadableUserAgent.java b/app/src/main/java/net/sf/uadetector/ReadableUserAgent.java deleted file mode 100644 index 5d35fdc..0000000 --- a/app/src/main/java/net/sf/uadetector/ReadableUserAgent.java +++ /dev/null @@ -1,122 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import javax.annotation.Nonnull; - -/** - * Defines an user agent.
- *
- * An user agent is a client program with which a network service can be used. The user agent is the interface to - * representing contents and taking orders of an user. Examples of user agents are web browsers, email programs, news - * reader and web crawlers.
- *
- * The implementation of this interface may be mutable or immutable. This interface only gives access to retrieve data, - * never to change it. - * - * @author André Rouél - */ -public interface ReadableUserAgent { - - /** - * Gets the detected device category. - * - * @return detected device category - */ - @Nonnull - ReadableDeviceCategory getDeviceCategory(); - - /** - * Gets the family of an user agent. - * - * @return family of an user agent - */ - @Nonnull - UserAgentFamily getFamily(); - - /** - * Gets the icon name of an user agent. - * - * @return icon name of an user agent - */ - @Nonnull - String getIcon(); - - /** - * Gets the name of an user agent. - * - * @return name of an user agent - */ - @Nonnull - String getName(); - - /** - * Gets the operating system on which the user agent is running. - * - * @return the running operating system - */ - @Nonnull - OperatingSystem getOperatingSystem(); - - /** - * Returns the manufacturer of an user agent. - * - * @return the manufacturer - */ - @Nonnull - String getProducer(); - - /** - * Returns the URL to the main website of the manufacturer of an user agent. - * - * @return URL to the website of the manufacturer - */ - @Nonnull - String getProducerUrl(); - - /** - * Returns the type of an user agent, for example, mobile browser or email client. - * - * @return the type of an user agent - */ - @Nonnull - UserAgentType getType(); - - /** - * Returns the type name of an user agent, for example, mobile browser or email client. - * - * @return the type name of an user agent - */ - @Nonnull - String getTypeName(); - - /** - * Returns the URL to the product or information page of an user agent. - * - * @return the URL to the product page - */ - @Nonnull - String getUrl(); - - /** - * Gets the version number of an user agent. - * - * @return version number of an user agent - */ - @Nonnull - VersionNumber getVersionNumber(); - -} diff --git a/app/src/main/java/net/sf/uadetector/ReadableVersionNumber.java b/app/src/main/java/net/sf/uadetector/ReadableVersionNumber.java deleted file mode 100644 index 2ec312d..0000000 --- a/app/src/main/java/net/sf/uadetector/ReadableVersionNumber.java +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import java.util.List; - -import javax.annotation.Nonnull; - -/** - * Defines a version number of an operating system or user agent.
- *
- * Generally, a version number represents unique states of a software. Version numbers are different versions of a - * single software in order to distinguish different versions of development cycles.
- *
- * A classic version number is often composed of: - *

    - *
  • Major release
    - * indexes mostly very significant change in the program - for example when the program was completely rewritten or - * libraries can be maintained at no interface compatibility.
  • - *
  • Minor release
    - * usually referred to a functional extension of a program.
  • - *
  • Patch level
    - * contains mostly bug fixes.
  • - *
- * A version number may also contain additions, for example, to document a development stage of a software.
- *
- * The implementation of this interface may be mutable or immutable. This interface only gives access to retrieve data, - * never to change it. - * - * @author André Rouél - */ -public interface ReadableVersionNumber extends Comparable { - - /** - * Gets the bugfix category of the version number. - * - * @return bugfix segment - */ - @Nonnull - String getBugfix(); - - /** - * Gets the additions or extension of the version number. - * - * @return extension of the version number - */ - @Nonnull - String getExtension(); - - /** - * Get all groups (or categories) of this version number. The first element in the list is the major category, - * followed by the minor and bugfix segment of the version number.
- *
- * The returned list of the version number segments should be immutable. - * - * @return a list of segments of the version number - */ - @Nonnull - List getGroups(); - - /** - * Gets the major category of the version number. - * - * @return major segment - */ - @Nonnull - String getMajor(); - - /** - * Gets the minor category of the version number. - * - * @return minor segment - */ - @Nonnull - String getMinor(); - - /** - * Gets this version number as string. - * - * @return numeric groups as dot separated version string - */ - @Nonnull - String toVersionString(); - -} diff --git a/app/src/main/java/net/sf/uadetector/UserAgent.java b/app/src/main/java/net/sf/uadetector/UserAgent.java deleted file mode 100644 index ee29eee..0000000 --- a/app/src/main/java/net/sf/uadetector/UserAgent.java +++ /dev/null @@ -1,450 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import java.io.Serializable; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; - -/** - * {@code UserAgent} is an immutable entity that represents the informations about web-based client applications like - * Web browsers, search engines or crawlers (spiders) as well as mobile phones, screen readers and braille browsers. - * - * @author André Rouél - */ -public final class UserAgent implements ReadableUserAgent, Serializable { - - public static final class Builder implements ReadableUserAgent { - - private DeviceCategory deviceCategory = EMPTY.deviceCategory; - - private UserAgentFamily family = EMPTY.family; - - private String icon = EMPTY.icon; - - private String name = EMPTY.name; - - private OperatingSystem operatingSystem = OperatingSystem.EMPTY; - - private String producer = EMPTY.producer; - - private String producerUrl = EMPTY.producerUrl; - - private UserAgentType type = EMPTY.type; - - private String typeName = EMPTY.typeName; - - private String url = EMPTY.url; - - private String userAgentString = ""; - - private VersionNumber versionNumber = VersionNumber.UNKNOWN; - - public Builder() { - // default constructor - } - - public Builder(@Nonnull final String userAgentString) { - Check.notNull(userAgentString, "userAgentString"); - this.userAgentString = userAgentString; - } - - @Nonnull - public UserAgent build() { - return new UserAgent(deviceCategory, family, icon, name, operatingSystem, producer, producerUrl, type, typeName, url, - versionNumber); - } - - @Override - public DeviceCategory getDeviceCategory() { - return deviceCategory; - } - - @Override - public UserAgentFamily getFamily() { - return family; - } - - @Override - public String getIcon() { - return icon; - } - - @Override - public String getName() { - return name; - } - - @Override - public OperatingSystem getOperatingSystem() { - return operatingSystem; - } - - @Override - public String getProducer() { - return producer; - } - - @Override - public String getProducerUrl() { - return producerUrl; - } - - @Override - public UserAgentType getType() { - return type; - } - - @Override - public String getTypeName() { - return typeName; - } - - @Override - public String getUrl() { - return url; - } - - public String getUserAgentString() { - return userAgentString; - } - - @Override - public VersionNumber getVersionNumber() { - return versionNumber; - } - - @Nonnull - public Builder setDeviceCategory(@Nonnull final DeviceCategory deviceCategory) { - Check.notNull(deviceCategory, "deviceCategory"); - this.deviceCategory = deviceCategory; - return this; - } - - @Nonnull - public Builder setFamily(@Nonnull final UserAgentFamily family) { - Check.notNull(family, "family"); - this.family = family; - return this; - } - - @Nonnull - public Builder setIcon(@Nonnull final String icon) { - Check.notNull(icon, "icon"); - this.icon = icon; - return this; - } - - @Nonnull - public Builder setName(@Nonnull final String name) { - Check.notNull(name, "name"); - this.name = name; - return this; - } - - @Nonnull - public Builder setOperatingSystem(@Nonnull final OperatingSystem operatingSystem) { - Check.notNull(operatingSystem, "operatingSystem"); - this.operatingSystem = operatingSystem; - return this; - } - - @Nonnull - public Builder setOperatingSystem(@Nonnull final ReadableOperatingSystem os) { - Check.notNull(os, "os"); - this.operatingSystem = new OperatingSystem(os.getFamily(), os.getFamilyName(), os.getIcon(), os.getName(), os.getProducer(), - os.getProducerUrl(), os.getUrl(), os.getVersionNumber()); - return this; - } - - @Nonnull - public Builder setProducer(@Nonnull final String producer) { - Check.notNull(producer, "producer"); - this.producer = producer; - return this; - } - - @Nonnull - public Builder setProducerUrl(@Nonnull final String producerUrl) { - Check.notNull(producerUrl, "producerUrl"); - this.producerUrl = producerUrl; - return this; - } - - @Nonnull - public Builder setType(@Nonnull final UserAgentType type) { - Check.notNull(type, "type"); - this.type = type; - this.typeName = type.getName(); - return this; - } - - @Nonnull - public Builder setTypeName(@Nonnull final String typeName) { - Check.notNull(typeName, "typeName"); - this.type = UserAgentType.evaluateByTypeName(typeName); - this.typeName = typeName; - return this; - } - - @Nonnull - public Builder setUrl(@Nonnull final String url) { - Check.notNull(url, "url"); - this.url = url; - return this; - } - - @Nonnull - public Builder setUserAgentString(@Nonnull final String userAgentString) { - Check.notNull(userAgentString, "userAgentString"); - this.userAgentString = userAgentString; - return this; - } - - @Nonnull - public Builder setVersionNumber(@Nonnull final VersionNumber versionNumber) { - Check.notNull(versionNumber, "versionNumber"); - this.versionNumber = versionNumber; - return this; - } - - } - - public static final UserAgent EMPTY = new UserAgent(DeviceCategory.EMPTY, UserAgentFamily.UNKNOWN, "", "unknown", - OperatingSystem.EMPTY, "", "", UserAgentType.UNKNOWN, "", "", VersionNumber.UNKNOWN); - - /** - * Serialization version - */ - private static final long serialVersionUID = 1L; - - @Nonnull - private final DeviceCategory deviceCategory; - - @Nonnull - private final UserAgentFamily family; - - @Nonnull - private final String icon; - - @Nonnull - private final String name; - - @Nonnull - private final OperatingSystem operatingSystem; - - @Nonnull - private final String producer; - - @Nonnull - private final String producerUrl; - - @Nonnull - private final UserAgentType type; - - @Nonnull - private final String typeName; - - @Nonnull - private final String url; - - @Nonnull - private final VersionNumber versionNumber; - - public UserAgent(@Nonnull final DeviceCategory deviceType, @Nonnull final UserAgentFamily family, @Nonnull final String icon, - @Nonnull final String name, @Nonnull final OperatingSystem operatingSystem, @Nonnull final String producer, - @Nonnull final String producerUrl, @Nonnull final UserAgentType type, @Nonnull final String typeName, - @Nonnull final String url, @Nonnull final VersionNumber versionNumber) { - Check.notNull(deviceType, "deviceType"); - Check.notNull(family, "family"); - Check.notNull(icon, "icon"); - Check.notNull(name, "name"); - Check.notNull(operatingSystem, "operatingSystem"); - Check.notNull(producer, "producer"); - Check.notNull(producerUrl, "producerUrl"); - Check.notNull(type, "type"); - Check.notNull(typeName, "typeName"); - Check.notNull(url, "url"); - Check.notNull(versionNumber, "versionNumber"); - - this.deviceCategory = deviceType; - this.family = family; - this.icon = icon; - this.name = name; - this.operatingSystem = operatingSystem; - this.producer = producer; - this.producerUrl = producerUrl; - this.type = type; - this.typeName = typeName; - this.url = url; - this.versionNumber = versionNumber; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final UserAgent other = (UserAgent) obj; - if (deviceCategory != other.deviceCategory) { - return false; - } - if (!family.equals(other.family)) { - return false; - } - if (!icon.equals(other.icon)) { - return false; - } - if (!name.equals(other.name)) { - return false; - } - if (!operatingSystem.equals(other.operatingSystem)) { - return false; - } - if (!producer.equals(other.producer)) { - return false; - } - if (!producerUrl.equals(other.producerUrl)) { - return false; - } - if (!type.equals(other.type)) { - return false; - } - if (!typeName.equals(other.typeName)) { - return false; - } - if (!url.equals(other.url)) { - return false; - } - if (!versionNumber.equals(other.versionNumber)) { - return false; - } - return true; - } - - @Nonnull - @Override - public DeviceCategory getDeviceCategory() { - return deviceCategory; - } - - @Override - public UserAgentFamily getFamily() { - return family; - } - - @Override - public String getIcon() { - return icon; - } - - @Override - public String getName() { - return name; - } - - @Override - public OperatingSystem getOperatingSystem() { - return operatingSystem; - } - - @Override - public String getProducer() { - return producer; - } - - @Override - public String getProducerUrl() { - return producerUrl; - } - - @Override - public UserAgentType getType() { - return type; - } - - @Override - public String getTypeName() { - return typeName; - } - - @Override - public String getUrl() { - return url; - } - - @Override - public VersionNumber getVersionNumber() { - return versionNumber; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + deviceCategory.hashCode(); - result = prime * result + family.hashCode(); - result = prime * result + icon.hashCode(); - result = prime * result + name.hashCode(); - result = prime * result + operatingSystem.hashCode(); - result = prime * result + producer.hashCode(); - result = prime * result + producerUrl.hashCode(); - result = prime * result + type.hashCode(); - result = prime * result + typeName.hashCode(); - result = prime * result + url.hashCode(); - result = prime * result + versionNumber.hashCode(); - return result; - } - - @Nonnull - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("UserAgent [deviceCategory="); - builder.append(deviceCategory); - builder.append(", family="); - builder.append(family); - builder.append(", icon="); - builder.append(icon); - builder.append(", name="); - builder.append(name); - builder.append(", operatingSystem="); - builder.append(operatingSystem); - builder.append(", producer="); - builder.append(producer); - builder.append(", producerUrl="); - builder.append(producerUrl); - builder.append(", type="); - builder.append(type); - builder.append(", typeName="); - builder.append(typeName); - builder.append(", url="); - builder.append(url); - builder.append(", versionNumber="); - builder.append(versionNumber); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/UserAgentFamily.java b/app/src/main/java/net/sf/uadetector/UserAgentFamily.java deleted file mode 100644 index 9fcbdbe..0000000 --- a/app/src/main/java/net/sf/uadetector/UserAgentFamily.java +++ /dev/null @@ -1,4528 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; - -/** - * This enum represents the more commonly used user agent families. It will never be complete, but can assist in - * identifying an user agent. - * - * @author André Rouél - */ -public enum UserAgentFamily { - - /** - * Representation of an unknown User-Agent - * - *

- * Attention: This is not a known User-Agent family, but only a placeholder. - */ - UNKNOWN("unknown", Pattern.compile("^$")), - - /** - * 192.comAgent - */ - _192_COMAGENT("192.comAgent", Pattern.compile("192.comAgent")), - - /** - * 2Bone LinkChecker - */ - _2BONE_LINKCHECKER("2Bone LinkChecker", Pattern.compile("2Bone LinkChecker")), - - /** - * 50.nu - */ - _50_NU("50.nu", Pattern.compile("50.nu")), - - /** - * 80legs - */ - _80LEGS("80legs", Pattern.compile("80legs")), - - /** - * A1 Sitemap Generator - */ - A1_SITEMAP_GENERATOR("A1 Sitemap Generator", Pattern.compile("A1 Sitemap Generator")), - - /** - * AB (Apache Bench) - */ - AB_APACHE_BENCH("AB (Apache Bench)", Pattern.compile("AB \\(Apache Bench\\)")), - - /** - * abby - */ - ABBY("abby", Pattern.compile("abby")), - - /** - * Abilon - */ - ABILON("Abilon", Pattern.compile("Abilon")), - - /** - * Abolimba - */ - ABOLIMBA("Abolimba", Pattern.compile("Abolimba")), - - /** - * Aboundexbot - */ - ABOUNDEXBOT("Aboundexbot", Pattern.compile("Aboundexbot")), - - /** - * AboutUsBot - */ - ABOUTUSBOT("AboutUsBot", Pattern.compile("AboutUsBot")), - - /** - * Abrave Spider - */ - ABRAVE_SPIDER("Abrave Spider", Pattern.compile("Abrave Spider")), - - /** - * ABrowse - */ - ABROWSE("ABrowse", Pattern.compile("ABrowse")), - - /** - * Accelobot - */ - ACCELOBOT("Accelobot", Pattern.compile("Accelobot")), - - /** - * Accoona-AI-Agent - */ - ACCOONA_AI_AGENT("Accoona-AI-Agent", Pattern.compile("Accoona-AI-Agent")), - - /** - * Acoo Browser - */ - ACOO_BROWSER("Acoo Browser", Pattern.compile("Acoo Browser")), - - /** - * AcoonBot - */ - ACOONBOT("AcoonBot", Pattern.compile("AcoonBot")), - - /** - * Acorn - */ - ACORN("Acorn", Pattern.compile("Acorn")), - - /** - * ActiveXperts Network Monitor - */ - ACTIVEXPERTS_NETWORK_MONITOR("ActiveXperts Network Monitor", Pattern.compile("ActiveXperts Network Monitor")), - - /** - * AddThis.com - */ - ADDTHIS_COM("AddThis.com", Pattern.compile("AddThis.com")), - - /** - * Adobe AIR runtime - */ - ADOBE_AIR_RUNTIME("Adobe AIR runtime", Pattern.compile("Adobe AIR runtime")), - - /** - * adressendeutschland.de - */ - ADRESSENDEUTSCHLAND_DE("adressendeutschland.de", Pattern.compile("adressendeutschland.de")), - - /** - * AdsBot-Google - */ - ADSBOT_GOOGLE("AdsBot-Google", Pattern.compile("AdsBot-Google")), - - /** - * AhrefsBot - */ - AHREFSBOT("AhrefsBot", Pattern.compile("AhrefsBot")), - - /** - * aiHitBot - */ - AIHITBOT("aiHitBot", Pattern.compile("aiHitBot")), - - /** - * aippie - */ - AIPPIE("aippie", Pattern.compile("aippie")), - - /** - * AirMail - */ - AIRMAIL("AirMail", Pattern.compile("AirMail")), - - /** - * Akregator - */ - AKREGATOR("Akregator", Pattern.compile("Akregator")), - - /** - * akula - */ - AKULA("akula", Pattern.compile("akula")), - - /** - * Alienforce - */ - ALIENFORCE("Alienforce", Pattern.compile("Alienforce")), - - /** - * Almaden - */ - ALMADEN("Almaden", Pattern.compile("Almaden")), - - /** - * Amagit.COM - */ - AMAGIT_COM("Amagit.COM", Pattern.compile("Amagit.COM")), - - /** - * Amaya - */ - AMAYA("Amaya", Pattern.compile("Amaya")), - - /** - * Amazon Silk - */ - AMAZON_SILK("Amazon Silk", Pattern.compile("(Amazon Silk|Mobile Silk)")), - - /** - * Amfibibot - */ - AMFIBIBOT("Amfibibot", Pattern.compile("Amfibibot")), - - /** - * amibot - */ - AMIBOT("amibot", Pattern.compile("amibot")), - - /** - * Amiga Aweb - */ - AMIGA_AWEB("Amiga Aweb", Pattern.compile("Amiga Aweb")), - - /** - * Amiga Voyager - */ - AMIGA_VOYAGER("Amiga Voyager", Pattern.compile("Amiga Voyager")), - - /** - * Android Browser - */ - ANDROID_BROWSER("Android Browser", Pattern.compile("(Android Browser|Android Webkit)", Pattern.CASE_INSENSITIVE)), - - /** - * Anemone - */ - ANEMONE("Anemone", Pattern.compile("Anemone")), - - /** - * Anonymouse.org - */ - ANONYMOUSE_ORG("Anonymouse.org", Pattern.compile("Anonymouse.org")), - - /** - * AntBot - */ - ANTBOT("AntBot", Pattern.compile("AntBot")), - - /** - * anw HTMLChecker - */ - ANW_HTMLCHECKER("anw HTMLChecker", Pattern.compile("anw HTMLChecker")), - - /** - * anw LoadControl - */ - ANW_LOADCONTROL("anw LoadControl", Pattern.compile("anw LoadControl")), - - /** - * AOL Explorer - */ - AOL_EXPLORER("AOL Explorer", Pattern.compile("AOL Explorer")), - - /** - * Apache internal dummy connection - */ - APACHE_INTERNAL_DUMMY_CONNECTION("Apache internal dummy connection", Pattern.compile("Apache internal dummy connection")), - - /** - * Apache Synapse - */ - APACHE_SYNAPSE("Apache Synapse", Pattern.compile("Apache Synapse")), - - /** - * Apercite - */ - APERCITE("Apercite", Pattern.compile("Apercite")), - - /** - * AportWorm - */ - APORTWORM("AportWorm", Pattern.compile("AportWorm")), - - /** - * Apple-PubSub - */ - APPLE_MAIL("Apple Mail", Pattern.compile("Apple Mail")), - - /** - * Apple-PubSub - */ - APPLE_PUBSUB("Apple-PubSub", Pattern.compile("Apple-PubSub")), - - /** - * arachnode.net - */ - ARACHNODE_NET("arachnode.net", Pattern.compile("arachnode.net")), - - /** - * archive.org_bot - */ - ARCHIVE_ORG_BOT("archive.org_bot", Pattern.compile("archive.org_bot")), - - /** - * Arora - */ - ARORA("Arora", Pattern.compile("Arora")), - - /** - * ASAHA Search Engine Turkey - */ - ASAHA_SEARCH_ENGINE_TURKEY("ASAHA Search Engine Turkey", Pattern.compile("ASAHA Search Engine Turkey")), - - /** - * Ask Jeeves/Teoma - */ - ASK_JEEVES_TEOMA("Ask Jeeves/Teoma", Pattern.compile("Ask Jeeves/Teoma")), - - /** - * Atomic Email Hunter - */ - ATOMIC_EMAIL_HUNTER("Atomic Email Hunter", Pattern.compile("Atomic Email Hunter")), - - /** - * Atomic Web Browser - */ - ATOMIC_WEB_BROWSER("Atomic Web Browser", Pattern.compile("Atomic Web Browser")), - - /** - * Avant Browser - */ - AVANT_BROWSER("Avant Browser", Pattern.compile("Avant Browser")), - - /** - * AvantGo - */ - AVANTGO("AvantGo", Pattern.compile("AvantGo")), - - /** - * Awasu - */ - AWASU("Awasu", Pattern.compile("Awasu")), - - /** - * Axel - */ - AXEL("Axel", Pattern.compile("Axel")), - - /** - * BabalooSpider - */ - BABALOOSPIDER("BabalooSpider", Pattern.compile("BabalooSpider")), - - /** - * BacklinkCrawler - */ - BACKLINKCRAWLER("BacklinkCrawler", Pattern.compile("BacklinkCrawler")), - - /** - * Bad-Neighborhood - */ - BAD_NEIGHBORHOOD("Bad-Neighborhood", Pattern.compile("Bad-Neighborhood")), - - /** - * Baidu Browser - */ - BAIDU_BROWSER("Baidu Browser", Pattern.compile("Baidu Browser")), - - /** - * Baiduspider - */ - BAIDUSPIDER("Baiduspider", Pattern.compile("Baiduspider")), - - /** - * Banshee - */ - BANSHEE("Banshee", Pattern.compile("Banshee")), - - /** - * Barca - */ - BARCA("Barca", Pattern.compile("Barca")), - - /** - * baypup - */ - BAYPUP("baypup", Pattern.compile("baypup")), - - /** - * BDFetch - */ - BDFETCH("BDFetch", Pattern.compile("BDFetch")), - - /** - * Beamrise - */ - BEAMRISE("Beamrise", Pattern.compile("Beamrise")), - - /** - * BecomeBot - */ - BECOMEBOT("BecomeBot", Pattern.compile("BecomeBot")), - - /** - * Beonex - */ - BEONEX("Beonex", Pattern.compile("Beonex")), - - /** - * Bigsearch.ca - */ - BIGSEARCH_CA("Bigsearch.ca", Pattern.compile("Bigsearch.ca")), - - /** - * bingbot - */ - BINGBOT("bingbot", Pattern.compile("bingbot")), - - /** - * BinGet - */ - BINGET("BinGet", Pattern.compile("BinGet")), - - /** - * bitlybot - */ - BITLYBOT("bitlybot", Pattern.compile("bitlybot")), - - /** - * biwec - */ - BIWEC("biwec", Pattern.compile("biwec")), - - /** - * bixo - */ - BIXO("bixo", Pattern.compile("bixo")), - - /** - * bixolabs - */ - BIXOLABS("bixocrawler", Pattern.compile("(bixocrawler|bixolabs)")), - - /** - * BlackBerry Browser - */ - BLACKBERRY_BROWSER("BlackBerry Browser", Pattern.compile("BlackBerry Browser")), - - /** - * Blackbird - */ - BLACKBIRD("Blackbird", Pattern.compile("Blackbird")), - - /** - * BlackHawk - */ - BLACKHAWK("BlackHawk", Pattern.compile("BlackHawk")), - - /** - * Blaiz-Bee - */ - BLAIZ_BEE("Blaiz-Bee", Pattern.compile("Blaiz-Bee")), - - /** - * Blazer - */ - BLAZER("Blazer", Pattern.compile("Blazer")), - - /** - * Blekkobot - */ - BLEKKOBOT("Blekkobot", Pattern.compile("Blekkobot")), - - /** - * BlinkaCrawler - */ - BLINKACRAWLER("BlinkaCrawler", Pattern.compile("BlinkaCrawler")), - - /** - * BlogBridge - */ - BLOGBRIDGE("BlogBridge", Pattern.compile("BlogBridge")), - - /** - * Bloggsi - */ - BLOGGSI("Bloggsi", Pattern.compile("Bloggsi")), - - /** - * Bloglines - */ - BLOGLINES("Bloglines", Pattern.compile("Bloglines")), - - /** - * BlogPulse - */ - BLOGPULSE("BlogPulse", Pattern.compile("BlogPulse")), - - /** - * bnf.fr_bot - */ - BNF_FR_BOT("bnf.fr_bot", Pattern.compile("bnf.fr_bot")), - - /** - * boitho.com-dc - */ - BOITHO_COM_DC("boitho.com-dc", Pattern.compile("boitho.com-dc")), - - /** - * Bolt - */ - BOLT("Bolt", Pattern.compile("Bolt")), - - /** - * Bookdog - */ - BOOKDOG("Bookdog", Pattern.compile("Bookdog")), - - /** - * BookmarkTracker - */ - BOOKMARKTRACKER("BookmarkTracker", Pattern.compile("BookmarkTracker")), - - /** - * bot-pge.chlooe.com - */ - BOT_PGE_CHLOOE_COM("bot-pge.chlooe.com", Pattern.compile("bot-pge.chlooe.com")), - - /** - * botmobi - */ - BOTMOBI("botmobi", Pattern.compile("botmobi")), - - /** - * BotOnParade - */ - BOTONPARADE("BotOnParade", Pattern.compile("BotOnParade")), - - /** - * Boxxe - */ - BOXXE("Boxxe", Pattern.compile("Boxxe")), - - /** - * BrownRecluse - */ - BROWNRECLUSE("BrownRecluse", Pattern.compile("BrownRecluse")), - - /** - * Browsershots - */ - BROWSERSHOTS("Browsershots", Pattern.compile("Browsershots")), - - /** - * BrowseX - */ - BROWSEX("BrowseX", Pattern.compile("BrowseX")), - - /** - * Browzar - */ - BROWZAR("Browzar", Pattern.compile("Browzar")), - - /** - * btbot - */ - BTBOT("btbot", Pattern.compile("btbot")), - - /** - * Bunjalloo - */ - BUNJALLOO("Bunjalloo", Pattern.compile("Bunjalloo")), - - /** - * Butterfly - */ - BUTTERFLY("Butterfly", Pattern.compile("Butterfly")), - - /** - * BuzzRankingBot - */ - BUZZRANKINGBOT("BuzzRankingBot", Pattern.compile("BuzzRankingBot")), - - /** - * Camino - */ - CAMINO("Camino", Pattern.compile("Camino")), - - /** - * CamontSpider - */ - CAMONTSPIDER("CamontSpider", Pattern.compile("CamontSpider")), - - /** - * CareerBot - */ - CAREERBOT("CareerBot", Pattern.compile("CareerBot")), - - /** - * ^Nail - */ - CARET_NAIL("^Nail", Pattern.compile("^Nail")), - - /** - * Castabot - */ - CASTABOT("Castabot", Pattern.compile("Castabot")), - - /** - * CatchBot - */ - CATCHBOT("CatchBot", Pattern.compile("CatchBot")), - - /** - * CazoodleBot - */ - CAZOODLEBOT("CazoodleBot", Pattern.compile("CazoodleBot")), - - /** - * CCBot - */ - CCBOT("CCBot", Pattern.compile("CCBot")), - - /** - * ccubee - */ - CCUBEE("ccubee", Pattern.compile("ccubee")), - - /** - * ChangeDetection - */ - CHANGEDETECTION("ChangeDetection", Pattern.compile("ChangeDetection(/\\d+(\\.\\d+)*)?", Pattern.CASE_INSENSITIVE)), - - /** - * Charlotte - */ - CHARLOTTE("Charlotte", Pattern.compile("Charlotte")), - - /** - * Charon - */ - CHARON("Charon", Pattern.compile("Charon")), - - /** - * Checkbot - */ - CHECKBOT("Checkbot", Pattern.compile("Checkbot")), - - /** - * Cheshire - */ - CHESHIRE("Cheshire", Pattern.compile("Cheshire")), - - /** - * Chilkat HTTP .NET - */ - CHILKAT_HTTP_NET("Chilkat HTTP .NET", Pattern.compile("Chilkat HTTP .NET")), - - /** - * Chrome - */ - CHROME("Chrome", Pattern.compile("Chrome")), - - /** - * Chrome Mobile - */ - CHROME_MOBILE("Chrome Mobile", Pattern.compile("Chrome Mobile")), - - /** - * Chromium - */ - CHROMIUM("Chromium", Pattern.compile("Chromium")), - - /** - * City4you - */ - CITY4YOU("City4you", Pattern.compile("City4you")), - - /** - * cityreview - */ - CITYREVIEW("cityreview", Pattern.compile("cityreview")), - - /** - * CJB.NET Proxy - */ - CJB_NET_PROXY("CJB.NET Proxy", Pattern.compile("CJB.NET Proxy")), - - /** - * Claws Mail GtkHtml2 plugin - */ - CLAWS_MAIL_GTKHTML2_PLUGIN("Claws Mail GtkHtml2 plugin", Pattern.compile("Claws Mail GtkHtml2 plugin")), - - /** - * CligooRobot - */ - CLIGOOROBOT("CligooRobot", Pattern.compile("CligooRobot")), - - /** - * Coast - */ - COAST("Coast", Pattern.compile("Coast")), - - /** - * coccoc - */ - COCCOC("coccoc", Pattern.compile("coccoc")), - - /** - * Columbus - */ - COLUMBUS("Columbus", Pattern.compile("Columbus")), - - /** - * Combine - */ - COMBINE("Combine", Pattern.compile("Combine")), - - /** - * CometBird - */ - COMETBIRD("CometBird", Pattern.compile("CometBird")), - - /** - * Comodo Dragon - */ - COMODO_DRAGON("Comodo Dragon", Pattern.compile("Comodo Dragon")), - - /** - * CompSpyBot - Competitive Spying and Scraping - */ - COMPSPYBOT("CompSpyBot/1.0", Pattern.compile("CompSpyBot(/\\d+(\\.\\d+)*)?")), - - /** - * Conkeror - */ - CONKEROR("Conkeror", Pattern.compile("Conkeror")), - - /** - * ConveraCrawler - */ - CONVERACRAWLER("ConveraCrawler", Pattern.compile("ConveraCrawler")), - - /** - * CoolNovo - */ - COOLNOVO("CoolNovo", Pattern.compile("CoolNovo")), - - /** - * copyright sheriff - */ - COPYRIGHT_SHERIFF("copyright sheriff", Pattern.compile("copyright sheriff")), - - /** - * CorePlayer - */ - COREPLAYER("CorePlayer", Pattern.compile("CorePlayer")), - - /** - * CorpusCrawler - */ - CORPUSCRAWLER("CorpusCrawler", Pattern.compile("CorpusCrawler")), - - /** - * Covario-IDS - */ - COVARIO_IDS("Covario-IDS", Pattern.compile("Covario-IDS")), - - /** - * CPG Dragonfly RSS Module - */ - CPG_DRAGONFLY_RSS_MODULE("CPG Dragonfly RSS Module", Pattern.compile("CPG Dragonfly RSS Module")), - - /** - * Crawler4j - */ - CRAWLER4J("Crawler4j", Pattern.compile("Crawler4j")), - - /** - * Crazy Browser - */ - CRAZY_BROWSER("Crazy Browser", Pattern.compile("Crazy Browser")), - - /** - * csci_b659 - */ - CSCI_B659("csci_b659", Pattern.compile("csci_b659")), - - /** - * CSE HTML Validator - */ - CSE_HTML_VALIDATOR("CSE HTML Validator", Pattern.compile("CSE HTML Validator")), - - /** - * cURL - */ - CURL("cURL", Pattern.compile("cURL")), - - /** - * Cyberduck - */ - CYBERDUCK("Cyberduck", Pattern.compile("Cyberduck")), - - /** - * Cynthia - */ - CYNTHIA("Cynthia", Pattern.compile("Cynthia")), - - /** - * D+ - */ - D_PLUS("D+", Pattern.compile("D+")), - - /** - * DataFountains - */ - DATAFOUNTAINS("DataFountains", Pattern.compile("DataFountains")), - - /** - * DataparkSearch - */ - DATAPARKSEARCH("DataparkSearch", Pattern.compile("DataparkSearch")), - - /** - * Daumoa - */ - DAUMOA("Daumoa", Pattern.compile("Daumoa")), - - /** - * DBLBot - */ - DBLBOT("DBLBot", Pattern.compile("DBLBot")), - - /** - * DCPbot - */ - DCPBOT("DCPbot", Pattern.compile("DCPbot")), - - /** - * DealGates Bot - */ - DEALGATES_BOT("DealGates Bot", Pattern.compile("DealGates Bot")), - - /** - * Deepnet Explorer - */ - DEEPNET_EXPLORER("Deepnet Explorer", Pattern.compile("Deepnet Explorer")), - - /** - * del.icio.us-thumbnails - */ - DEL_ICIO_US_THUMBNAILS("del.icio.us-thumbnails", Pattern.compile("del.icio.us-thumbnails")), - - /** - * Dell Web Monitor - */ - DELL_WEB_MONITOR("Dell Web Monitor", Pattern.compile("Dell Web Monitor")), - - /** - * Demeter - */ - DEMETER("Demeter", Pattern.compile("Demeter")), - - /** - * DepSpid - */ - DEPSPID("DepSpid", Pattern.compile("DepSpid")), - - /** - * DeskBrowse - */ - DESKBROWSE("DeskBrowse", Pattern.compile("DeskBrowse")), - - /** - * Dillo - */ - DILLO("Dillo", Pattern.compile("Dillo")), - - /** - * Discoverybot is Discovery Engine's web crawler. It downloads text/html documents for use in building our full web - * search engine. - */ - DISCOBOT("discobot", Pattern.compile("(discobot|discoverybot)(/\\d+(\\.\\d+))?")), - - /** - * DKIMRepBot - */ - DKIMREPBOT("DKIMRepBot", Pattern.compile("DKIMRepBot")), - - /** - * DNS-Digger-Explorer - */ - DNS_DIGGER_EXPLORER("DNS-Digger-Explorer", Pattern.compile("DNS-Digger-Explorer")), - - /** - * DocZilla - */ - DOCZILLA("DocZilla", Pattern.compile("DocZilla")), - - /** - * Dolphin - */ - DOLPHIN("Dolphin", Pattern.compile("Dolphin")), - - /** - * DomainDB - */ - DOMAINDB("DomainDB", Pattern.compile("DomainDB")), - - /** - * Dooble - */ - DOOBLE("Dooble", Pattern.compile("Dooble")), - - /** - * Doris - */ - DORIS("Doris", Pattern.compile("Doris")), - - /** - * Dot TK - spider - */ - DOT_TK_SPIDER("Dot TK - spider", Pattern.compile("Dot TK - spider")), - - /** - * DotBot - */ - DOTBOT("DotBot", Pattern.compile("DotBot")), - - /** - * dotSemantic - */ - DOTSEMANTIC("dotSemantic", Pattern.compile("dotSemantic")), - - /** - * DownloadStudio - */ - DOWNLOADSTUDIO("DownloadStudio", Pattern.compile("DownloadStudio")), - - /** - * DripfeedBot - */ - DRIPFEEDBOT("DripfeedBot", Pattern.compile("DripfeedBot")), - - /** - * DuckDuckBot - */ - DUCKDUCKBOT("DuckDuckBot", Pattern.compile("DuckDuckBot")), - - /** - * DuckDuckPreview - */ - DUCKDUCKPREVIEW("DuckDuckPreview", Pattern.compile("DuckDuckPreview")), - - /** - * e-SocietyRobot - */ - E_SOCIETYROBOT("e-SocietyRobot", Pattern.compile("e-SocietyRobot")), - - /** - * EasyBib AutoCite - */ - EASYBIB_AUTOCITE("EasyBib AutoCite", Pattern.compile("EasyBib AutoCite")), - - /** - * eCairn-Grabber - */ - ECAIRN_GRABBER("eCairn-Grabber", Pattern.compile("eCairn-Grabber")), - - /** - * Edbrowse - */ - EDBROWSE("Edbrowse", Pattern.compile("Edbrowse")), - - /** - * EDI - */ - EDI("EDI", Pattern.compile("EDI")), - - /** - * EdisterBot - */ - EDISTERBOT("EdisterBot", Pattern.compile("EdisterBot")), - - /** - * egothor - */ - EGOTHOR("egothor", Pattern.compile("egothor")), - - /** - * ejupiter.com - */ - EJUPITER_COM("ejupiter.com", Pattern.compile("ejupiter.com")), - - /** - * Element Browser - */ - ELEMENT_BROWSER("Element Browser", Pattern.compile("Element Browser")), - - /** - * Elinks - */ - ELINKS("Elinks", Pattern.compile("Elinks")), - - /** - * EnaBot - */ - ENABOT("EnaBot", Pattern.compile("EnaBot")), - - /** - * Enigma browser - */ - ENIGMA_BROWSER("Enigma browser", Pattern.compile("Enigma browser")), - - /** - * Enterprise_Search - */ - ENTERPRISE_SEARCH("Enterprise_Search", Pattern.compile("Enterprise_Search")), - - /** - * envolk - */ - ENVOLK("envolk", Pattern.compile("envolk")), - - /** - * Epic - */ - EPIC("Epic", Pattern.compile("Epic")), - - /** - * Epiphany - */ - EPIPHANY("Epiphany", Pattern.compile("Epiphany")), - - /** - * Espial TV Browser - */ - ESPIAL_TV_BROWSER("Espial TV Browser", Pattern.compile("Espial TV Browser")), - - /** - * Eudora - */ - EUDORA("Eudora", Pattern.compile("Eudora")), - - /** - * EuripBot - */ - EURIPBOT("EuripBot", Pattern.compile("EuripBot")), - - /** - * Eurobot - */ - EUROBOT("Eurobot", Pattern.compile("Eurobot")), - - /** - * EventGuruBot - */ - EVENTGURUBOT("EventGuruBot", Pattern.compile("EventGuruBot")), - - /** - * EventMachine - */ - EVENTMACHINE("EventMachine", Pattern.compile("EventMachine")), - - /** - * Evolution/Camel.Stream - */ - EVOLUTION_CAMEL_STREAM("Evolution/Camel.Stream", Pattern.compile("Evolution/Camel.Stream")), - - /** - * EvriNid - */ - EVRINID("EvriNid", Pattern.compile("EvriNid")), - - /** - * Exabot - */ - EXABOT("Exabot", Pattern.compile("Exabot")), - - /** - * ExactSEEK - */ - EXACTSEEK("ExactSEEK", Pattern.compile("ExactSEEK")), - - /** - * Ezooms - */ - EZOOMS("Ezooms", Pattern.compile("Ezooms")), - - /** - * FacebookExternalHit - */ - FACEBOOKEXTERNALHIT("FacebookExternalHit", Pattern.compile("FacebookExternalHit")), - - /** - * factbot - */ - FACTBOT("factbot", Pattern.compile("factbot")), - - /** - * FairShare - */ - FAIRSHARE("FairShare", Pattern.compile("FairShare")), - - /** - * Falconsbot - */ - FALCONSBOT("Falconsbot", Pattern.compile("Falconsbot")), - - /** - * FAST Enterprise Crawler - */ - FAST_ENTERPRISE_CRAWLER("FAST Enterprise Crawler", Pattern.compile("FAST Enterprise Crawler")), - - /** - * FAST MetaWeb Crawler - */ - FAST_METAWEB_CRAWLER("FAST MetaWeb Crawler", Pattern.compile("FAST MetaWeb Crawler")), - - /** - * Fastladder FeedFetcher - */ - FASTLADDER_FEEDFETCHER("Fastladder FeedFetcher", Pattern.compile("Fastladder FeedFetcher")), - - /** - * FauBot - */ - FAUBOT("FauBot", Pattern.compile("FauBot")), - - /** - * favorstarbot - */ - FAVORSTARBOT("favorstarbot", Pattern.compile("favorstarbot")), - - /** - * Feed::Find - */ - FEED_FIND("Feed::Find", Pattern.compile("Feed::Find")), - - /** - * Feed Viewer - */ - FEED_VIEWER("Feed Viewer", Pattern.compile("Feed Viewer")), - - /** - * FeedCatBot - */ - FEEDCATBOT("FeedCatBot", Pattern.compile("FeedCatBot")), - - /** - * FeedDemon - */ - FEEDDEMON("FeedDemon", Pattern.compile("FeedDemon")), - - /** - * Feedfetcher-Google - */ - FEEDFETCHER_GOOGLE("Feedfetcher-Google", Pattern.compile("Feedfetcher-Google")), - - /** - * FeedFinder/bloggz.se - */ - FEEDFINDER_BLOGGZ_SE("FeedFinder/bloggz.se", Pattern.compile("FeedFinder/bloggz.se")), - - /** - * FeedParser - */ - FEEDPARSER("FeedParser", Pattern.compile("FeedParser")), - - /** - * FeedValidator - */ - FEEDVALIDATOR("FeedValidator", Pattern.compile("FeedValidator")), - - /** - * Findexa Crawler - */ - FINDEXA_CRAWLER("Findexa Crawler", Pattern.compile("Findexa Crawler")), - - /** - * findlinks - */ - FINDLINKS("findlinks", Pattern.compile("findlinks")), - - /** - * Firebird (old name for Firefox) - */ - FIREBIRD("Firebird (old name for Firefox)", Pattern.compile("Firebird \\(old name for Firefox\\)")), - - /** - * Firefox - */ - FIREFOX("Firefox", Pattern.compile("Firefox")), - - /** - * Firefox (BonEcho) - */ - FIREFOX_BONECHO("Firefox (BonEcho)", Pattern.compile("Firefox \\(BonEcho\\)")), - - /** - * Firefox (GranParadiso) - */ - FIREFOX_GRANPARADISO("Firefox (GranParadiso)", Pattern.compile("Firefox \\(GranParadiso\\)")), - - /** - * Firefox (Lorentz) - */ - FIREFOX_LORENTZ("Firefox (Lorentz)", Pattern.compile("Firefox \\(Lorentz\\)")), - - /** - * Firefox (Minefield) - */ - FIREFOX_MINEFIELD("Firefox (Minefield)", Pattern.compile("Firefox \\(Minefield\\)")), - - /** - * Firefox (Namoroka) - */ - FIREFOX_NAMOROKA("Firefox (Namoroka)", Pattern.compile("Firefox \\(Namoroka\\)")), - - /** - * Firefox (Shiretoko) - */ - FIREFOX_SHIRETOKO("Firefox (Shiretoko)", Pattern.compile("Firefox \\(Shiretoko\\)")), - - /** - * Fireweb Navigator - */ - FIREWEB_NAVIGATOR("Fireweb Navigator", Pattern.compile("Fireweb Navigator")), - - /** - * Flatland Industries Web Spider - */ - FLATLAND_INDUSTRIES_WEB_SPIDER("Flatland Industries Web Spider", Pattern.compile("Flatland Industries Web Spider")), - - /** - * flatlandbot - */ - FLATLANDBOT("flatlandbot", Pattern.compile("flatlandbot")), - - /** - * FlightDeckReportsBot - */ - FLIGHTDECKREPORTSBOT("FlightDeckReportsBot", Pattern.compile("FlightDeckReportsBot")), - - /** - * FlipboardProxy - */ - FLIPBOARDPROXY("FlipboardProxy", Pattern.compile("FlipboardProxy")), - - /** - * Flock - */ - FLOCK("Flock", Pattern.compile("Flock")), - - /** - * Flocke bot - */ - FLOCKE_BOT("Flocke bot", Pattern.compile("Flocke bot")), - - /** - * Fluid - */ - FLUID("Fluid", Pattern.compile("Fluid")), - - /** - * FlyCast - */ - FLYCAST("FlyCast", Pattern.compile("FlyCast")), - - /** - * FollowSite Bot - */ - FOLLOWSITE_BOT("FollowSite Bot", Pattern.compile("FollowSite Bot")), - - /** - * foobar2000 - */ - FOOBAR2000("foobar2000", Pattern.compile("foobar2000")), - - /** - * Fooooo_Web_Video_Crawl - */ - FOOOOO_WEB_VIDEO_CRAWL("Fooooo_Web_Video_Crawl", Pattern.compile("Fooooo_Web_Video_Crawl")), - - /** - * Forschungsportal - */ - FORSCHUNGSPORTAL("Forschungsportal", Pattern.compile("Forschungsportal")), - - /** - * Francis - */ - FRANCIS("Francis", Pattern.compile("Francis")), - - /** - * Funambol Mozilla Sync Client - */ - FUNAMBOL_MOZILLA_SYNC_CLIENT("Funambol Mozilla Sync Client", Pattern.compile("Funambol Mozilla Sync Client")), - - /** - * Funambol Outlook Sync Client - */ - FUNAMBOL_OUTLOOK_SYNC_CLIENT("Funambol Outlook Sync Client", Pattern.compile("Funambol Outlook Sync Client")), - - /** - * FunnelBack - */ - FUNNELBACK("FunnelBack", Pattern.compile("FunnelBack")), - - /** - * FurlBot - */ - FURLBOT("FurlBot", Pattern.compile("FurlBot")), - - /** - * FyberSpider - */ - FYBERSPIDER("FyberSpider", Pattern.compile("FyberSpider")), - - /** - * g2crawler - */ - G2CRAWLER("g2crawler", Pattern.compile("g2crawler")), - - /** - * Gaisbot - */ - GAISBOT("Gaisbot", Pattern.compile("Gaisbot")), - - /** - * Galeon - */ - GALEON("Galeon", Pattern.compile("Galeon")), - - /** - * Gallent Search Spider - */ - GALLENT_SEARCH_SPIDER("Gallent Search Spider", Pattern.compile("Gallent Search Spider")), - - /** - * GarlikCrawler - */ - GARLIKCRAWLER("GarlikCrawler", Pattern.compile("GarlikCrawler")), - - /** - * GcMail - */ - GCMAIL("GcMail", Pattern.compile("GcMail")), - - /** - * genieBot - */ - GENIEBOT("genieBot", Pattern.compile("genieBot")), - - /** - * GeonaBot - */ - GEONABOT("GeonaBot", Pattern.compile("GeonaBot")), - - /** - * GetRight - */ - GETRIGHT("GetRight", Pattern.compile("GetRight")), - - /** - * Giant/1.0 - */ - GIANT("Giant", Pattern.compile("Giant/(\\d+(\\.\\d+)*)")), - - /** - * Gigabot - */ - GIGABOT("Gigabot", Pattern.compile("Gigabot")), - - /** - * GingerCrawler - */ - GINGERCRAWLER("GingerCrawler", Pattern.compile("GingerCrawler")), - - /** - * Girafabot - */ - GIRAFABOT("Girafabot", Pattern.compile("Girafabot")), - - /** - * GlobalMojo - */ - GLOBALMOJO("GlobalMojo", Pattern.compile("GlobalMojo")), - - /** - * Gmail image proxy - */ - GMAIL_IMAGE_PROXY("Gmail image proxy", Pattern.compile("Gmail image proxy")), - - /** - * GnomeVFS - */ - GNOMEVFS("GnomeVFS", Pattern.compile("GnomeVFS")), - - /** - * GO Browser - */ - GO_BROWSER("GO Browser", Pattern.compile("GO Browser")), - - /** - * GOFORITBOT - */ - GOFORITBOT("GOFORITBOT", Pattern.compile("GOFORITBOT")), - - /** - * GoldenPod - */ - GOLDENPOD("GoldenPod", Pattern.compile("GoldenPod")), - - /** - * GOM Player - */ - GOM_PLAYER("GOM Player", Pattern.compile("GOM Player")), - - /** - * gonzo - */ - GONZO("gonzo", Pattern.compile("gonzo")), - - /** - * Google App Engine - */ - GOOGLE_APP_ENGINE("Google App Engine", Pattern.compile("Google App Engine")), - - /** - * Google Earth - */ - GOOGLE_EARTH("Google Earth", Pattern.compile("Google Earth")), - - /** - * Google Friend Connect - */ - GOOGLE_FRIEND_CONNECT("Google Friend Connect", Pattern.compile("Google Friend Connect")), - - /** - * Google Listen - */ - GOOGLE_LISTEN("Google Listen", Pattern.compile("Google Listen")), - - /** - * Google Rich Snippets Testing Tool - */ - GOOGLE_RICH_SNIPPETS_TESTING_TOOL("Google Rich Snippets Testing Tool", Pattern.compile("Google Rich Snippets Testing Tool")), - - /** - * Google Wireless Transcoder - */ - GOOGLE_WIRELESS_TRANSCODER("Google Wireless Transcoder", Pattern.compile("Google Wireless Transcoder")), - - /** - * Googlebot - */ - GOOGLEBOT("Googlebot", Pattern.compile("Googlebot")), - - /** - * Googlebot-Mobile - */ - GOOGLEBOT_MOBILE("Googlebot-Mobile", Pattern.compile("Googlebot-Mobile")), - - /** - * gPodder - */ - GPODDER("gPodder", Pattern.compile("gPodder")), - - /** - * GrapeshotCrawler - */ - GRAPESHOTCRAWLER("GrapeshotCrawler", Pattern.compile("GrapeshotCrawler")), - - /** - * GreatNews - */ - GREATNEWS("GreatNews", Pattern.compile("GreatNews")), - - /** - * GreenBrowser - */ - GREENBROWSER("GreenBrowser", Pattern.compile("GreenBrowser")), - - /** - * Gregarius - */ - GREGARIUS("Gregarius", Pattern.compile("Gregarius")), - - /** - * GSiteCrawler - */ - GSITECRAWLER("GSiteCrawler", Pattern.compile("GSiteCrawler")), - - /** - * GStreamer - */ - GSTREAMER("GStreamer", Pattern.compile("GStreamer")), - - /** - * GurujiBot - */ - GURUJIBOT("GurujiBot", Pattern.compile("GurujiBot")), - - /** - * Hailoobot - */ - HAILOOBOT("Hailoobot", Pattern.compile("Hailoobot")), - - /** - * HatenaScreenshot - */ - HATENASCREENSHOT("HatenaScreenshot", Pattern.compile("HatenaScreenshot")), - - /** - * HeartRails_Capture - */ - HEARTRAILS_CAPTURE("HeartRails_Capture", Pattern.compile("HeartRailsBot")), - - /** - * heritrix - */ - HERITRIX("heritrix", Pattern.compile("heritrix")), - - /** - * HiddenMarket - */ - HIDDENMARKET("HiddenMarket", Pattern.compile("HiddenMarket")), - - /** - * Holmes - */ - HOLMES("Holmes", Pattern.compile("Holmes")), - - /** - * HolmesBot - */ - HOLMESBOT("HolmesBot", Pattern.compile("HolmesBot")), - - /** - * HomeTags - */ - HOMETAGS("HomeTags", Pattern.compile("HomeTags")), - - /** - * HooWWWer - */ - HOOWWWER("HooWWWer", Pattern.compile("HooWWWer")), - - /** - * HostTracker.com - */ - HOSTTRACKER_COM("HostTracker", Pattern.compile("HostTracker")), - - /** - * HotJava - */ - HOTJAVA("HotJava", Pattern.compile("HotJava")), - - /** - * ht://Dig - */ - HT_DIG("ht://Dig", Pattern.compile("ht://Dig")), - - /** - * HTML2JPG - */ - HTML2JPG("HTML2JPG", Pattern.compile("HTML2JPG")), - - /** - * HTMLayout - */ - HTMLAYOUT("HTMLayout", Pattern.compile("HTMLayout")), - - /** - * HTMLParser - */ - HTMLPARSER("HTMLParser", Pattern.compile("HTMLParser")), - - /** - * HTTP nagios plugin - */ - HTTP_NAGIOS_PLUGIN("HTTP nagios plugin", Pattern.compile("HTTP nagios plugin")), - - /** - * HTTP_Request2 - */ - HTTP_REQUEST2("HTTP_Request2", Pattern.compile("HTTP_Request2")), - - /** - * HTTrack - */ - HTTRACK("HTTrack", Pattern.compile("HTTrack")), - - /** - * HuaweiSymantecSpider - */ - HUAWEISYMANTECSPIDER("HuaweiSymantecSpider", Pattern.compile("HuaweiSymantecSpider")), - - /** - * Hv3 - */ - HV3("Hv3", Pattern.compile("Hv3")), - - /** - * Hydra Browser - */ - HYDRA_BROWSER("Hydra Browser", Pattern.compile("Hydra Browser")), - - /** - * ia_archiver - */ - IA_ARCHIVER("ia_archiver", Pattern.compile("ia_archiver")), - - /** - * iaskspider - */ - IASKSPIDER("iaskspider", Pattern.compile("iaskspider")), - - /** - * IBrowse - */ - IBROWSE("IBrowse", Pattern.compile("IBrowse")), - - /** - * iCab - */ - ICAB("iCab", Pattern.compile("iCab")), - - /** - * iCatcher! - */ - ICATCHER("iCatcher!", Pattern.compile("iCatcher!")), - - /** - * ICC-Crawler - */ - ICC_CRAWLER("ICC-Crawler", Pattern.compile("ICC-Crawler")), - - /** - * ICE browser - */ - ICE_BROWSER("ICE browser", Pattern.compile("ICE browser")), - - /** - * IceApe - */ - ICEAPE("IceApe", Pattern.compile("IceApe")), - - /** - * IceCat - */ - ICECAT("IceCat", Pattern.compile("IceCat")), - - /** - * IceDragon: A faster, more secure version of Firefox - */ - ICEDRAGON("IceDragon", Pattern.compile("IceDragon")), - - /** - * IceWeasel - */ - ICEWEASEL("IceWeasel", Pattern.compile("IceWeasel")), - - /** - * ICF_Site_Crawler - */ - ICF_SITE_CRAWLER("ICF_Site_Crawler", Pattern.compile("ICF_Site_Crawler")), - - /** - * ichiro - */ - ICHIRO("ichiro", Pattern.compile("ichiro")), - - /** - * iCjobs - */ - ICJOBS("iCjobs", Pattern.compile("iCjobs")), - - /** - * Internet Explorer - */ - IE("IE", Pattern.compile("IE")), - - /** - * Internet Explorer Mobile - */ - IE_MOBILE("IE Mobile", Pattern.compile("IE Mobile")), - - /** - * Internet Explorer RSS reader - */ - IE_RSS_READER("IE RSS reader", Pattern.compile("IE RSS reader")), - - /** - * iGetter - */ - IGETTER("iGetter", Pattern.compile("iGetter")), - - /** - * iGooMap - */ - IGOOMAP("iGooMap", Pattern.compile("iGooMap")), - - /** - * IlseBot - */ - ILSEBOT("IlseBot", Pattern.compile("IlseBot")), - - /** - * IlTrovatore - */ - ILTROVATORE("IlTrovatore", Pattern.compile("IlTrovatore")), - - /** - * IlTrovatore-Setaccio - */ - ILTROVATORE_SETACCIO("IlTrovatore-Setaccio", Pattern.compile("IlTrovatore-Setaccio")), - - /** - * imbot - */ - IMBOT("imbot", Pattern.compile("imbot")), - - /** - * Indy Library - */ - INDY_LIBRARY("Indy Library", Pattern.compile("Indy Library")), - - /** - * Influencebot - */ - INFLUENCEBOT("Influencebot", Pattern.compile("Influencebot")), - - /** - * InfociousBot - */ - INFOCIOUSBOT("InfociousBot", Pattern.compile("InfociousBot")), - - /** - * Infohelfer - */ - INFOHELFER("Infohelfer", Pattern.compile("Infohelfer")), - - /** - * InternetSeer - */ - INTERNETSEER("InternetSeer", Pattern.compile("InternetSeer")), - - /** - * InternetSurfboard - */ - INTERNETSURFBOARD("InternetSurfboard", Pattern.compile("InternetSurfboard")), - - /** - * Ipselonbot - */ - IPSELONBOT("Ipselonbot", Pattern.compile("Ipselonbot")), - - /** - * iRider - */ - IRIDER("iRider", Pattern.compile("iRider")), - - /** - * IRLbot - */ - IRLBOT("IRLbot", Pattern.compile("IRLbot")), - - /** - * Iron - */ - IRON("Iron", Pattern.compile("Iron")), - - /** - * iSiloX - */ - ISILOX("iSiloX", Pattern.compile("iSiloX")), - - /** - * iSiloXC - */ - ISILOXC("iSiloXC", Pattern.compile("iSiloXC")), - - /** - * iTunes - */ - ITUNES("iTunes", Pattern.compile("iTunes")), - - /** - * iVideo - */ - IVIDEO("iVideo", Pattern.compile("iVideo")), - - /** - * IXR lib - */ - IXR_LIB("IXR lib", Pattern.compile("IXR lib")), - - /** - * JadynAve - */ - JADYNAVE("JadynAve", Pattern.compile("JadynAve")), - - /** - * JadynAveBot - */ - JADYNAVEBOT("JadynAveBot", Pattern.compile("JadynAveBot")), - - /** - * Jakarta Commons-HttpClient - */ - JAKARTA_COMMONS_HTTPCLIENT("Jakarta Commons-HttpClient", Pattern.compile("(Apache-HttpClient|Jakarta Commons-HttpClient)")), - - /** - * Jambot - */ - JAMBOT("Jambot", Pattern.compile("Jambot")), - - /** - * Jamcast - */ - JAMCAST("Jamcast", Pattern.compile("Jamcast")), - - /** - * Jasmine - */ - JASMINE("Jasmine", Pattern.compile("Jasmine")), - - /** - * Java - */ - JAVA("Java", Pattern.compile("Java")), - - /** - * JikeSpider - */ - JIKESPIDER("JikeSpider", Pattern.compile("JikeSpider")), - - /** - * Job Roboter Spider - */ - JOB_ROBOTER_SPIDER("Job Roboter Spider", Pattern.compile("Job Roboter Spider")), - - /** - * JoBo - */ - JOBO("JoBo", Pattern.compile("JoBo")), - - /** - * JS-Kit/Echo - */ - JS_KIT_ECHO("JS-Kit/Echo", Pattern.compile("JS-Kit/Echo")), - - /** - * JUST-CRAWLER - */ - JUST_CRAWLER("JUST-CRAWLER", Pattern.compile("JUST-CRAWLER")), - - /** - * Jyxobot - */ - JYXOBOT("Jyxobot", Pattern.compile("Jyxobot")), - - /** - * K-Meleon - */ - K_MELEON("K-Meleon", Pattern.compile("K-Meleon")), - - /** - * K-Ninja - */ - K_NINJA("K-Ninja", Pattern.compile("K-Ninja")), - - /** - * Kakle Bot - */ - KAKLE_BOT("Kakle Bot", Pattern.compile("Kakle Bot")), - - /** - * Kalooga - */ - KALOOGA("Kalooga", Pattern.compile("Kalooga")), - - /** - * Kapiko - */ - KAPIKO("Kapiko", Pattern.compile("Kapiko")), - - /** - * Karneval-Bot - */ - KARNEVAL_BOT("Karneval-Bot", Pattern.compile("Karneval-Bot")), - - /** - * Kazehakase - */ - KAZEHAKASE("Kazehakase", Pattern.compile("Kazehakase")), - - /** - * KeywenBot - */ - KEYWENBOT("KeywenBot", Pattern.compile("KeywenBot")), - - /** - * KeywordDensityRobot - */ - KEYWORDDENSITYROBOT("KeywordDensityRobot", Pattern.compile("KeywordDensityRobot")), - - /** - * Kindle Browser - */ - KINDLE_BROWSER("Kindle Browser", Pattern.compile("Kindle Browser")), - - /** - * Kirix Strata - */ - KIRIX_STRATA("Kirix Strata", Pattern.compile("Kirix Strata")), - - /** - * KKman - */ - KKMAN("KKman", Pattern.compile("KKman")), - - /** - * Klondike - */ - KLONDIKE("Klondike", Pattern.compile("Klondike")), - - /** - * Kongulo - */ - KONGULO("Kongulo", Pattern.compile("Kongulo")), - - /** - * Konqueror - */ - KONQUEROR("Konqueror", Pattern.compile("Konqueror")), - - /** - * KRetrieve - */ - KRETRIEVE("KRetrieve", Pattern.compile("KRetrieve")), - - /** - * Krugle - */ - KRUGLE("Krugle", Pattern.compile("Krugle")), - - /** - * ksibot - */ - KSIBOT("ksibot", Pattern.compile("ksibot")), - - /** - * Kylo - */ - KYLO("Kylo", Pattern.compile("Kylo")), - - /** - * L.webis - */ - L_WEBIS("L.webis", Pattern.compile("L.webis")), - - /** - * LapozzBot - */ - LAPOZZBOT("LapozzBot", Pattern.compile("LapozzBot")), - - /** - * Larbin - */ - LARBIN("Larbin", Pattern.compile("Larbin")), - - /** - * LBrowser - */ - LBROWSER("LBrowser", Pattern.compile("LBrowser")), - - /** - * LeechCraft - */ - LEECHCRAFT("LeechCraft", Pattern.compile("LeechCraft")), - - /** - * LemurWebCrawler - */ - LEMURWEBCRAWLER("LemurWebCrawler", Pattern.compile("LemurWebCrawler")), - - /** - * LexxeBot - */ - LEXXEBOT("LexxeBot", Pattern.compile("LexxeBot")), - - /** - * LFTP - */ - LFTP("LFTP", Pattern.compile("LFTP")), - - /** - * LG Web Browser - */ - LG_WEB_BROWSER("LG Web Browser", Pattern.compile("LG Web Browser")), - - /** - * LibSoup - */ - LIBSOUP("LibSoup", Pattern.compile("LibSoup")), - - /** - * libwww-perl - */ - LIBWWW_PERL("libwww-perl", Pattern.compile("libwww-perl")), - - /** - * Liferea - */ - LIFEREA("Liferea", Pattern.compile("Liferea")), - - /** - * Lijit - */ - LIJIT("Lijit", Pattern.compile("Lijit")), - - /** - * LinguaBot - */ - LINGUABOT("LinguaBot", Pattern.compile("LinguaBot")), - - /** - * Linguee Bot - */ - LINGUEE_BOT("Linguee Bot", Pattern.compile("Linguee Bot")), - - /** - * Link Valet Online - */ - LINK_VALET_ONLINE("Link Valet Online", Pattern.compile("Link Valet Online")), - - /** - * LinkAider - */ - LINKAIDER("LinkAider", Pattern.compile("LinkAider")), - - /** - * LinkbackPlugin for Laconica - */ - LINKBACKPLUGIN_FOR_LACONICA("LinkbackPlugin for Laconica", Pattern.compile("LinkbackPlugin for Laconica")), - - /** - * LinkChecker - */ - LINKCHECKER("LinkChecker", Pattern.compile("LinkChecker")), - - /** - * linkdex.com - */ - LINKDEX_COM("linkdex.com", Pattern.compile("linkdex.com")), - - /** - * LinkExaminer - */ - LINKEXAMINER("LinkExaminer", Pattern.compile("LinkExaminer")), - - /** - * Links - */ - LINKS("Links", Pattern.compile("Links")), - - /** - * linksmanager_bot - */ - LINKSMANAGER_BOT("linksmanager_bot", Pattern.compile("linksmanager_bot")), - - /** - * LinkWalker - */ - LINKWALKER("LinkWalker", Pattern.compile("LinkWalker")), - - /** - * livedoor ScreenShot - */ - LIVEDOOR_SCREENSHOT("livedoor ScreenShot", Pattern.compile("livedoor ScreenShot")), - - /** - * lmspider - */ - LMSPIDER("lmspider", Pattern.compile("lmspider")), - - /** - * Lobo - */ - LOBO("Lobo", Pattern.compile("Lobo")), - - /** - * lolifox - */ - LOLIFOX("lolifox", Pattern.compile("lolifox")), - - /** - * Lotus Notes - */ - LOTUS_NOTES("Lotus Notes", Pattern.compile("Lotus Notes")), - - /** - * Lunascape - */ - LUNASCAPE("Lunascape", Pattern.compile("Lunascape")), - - /** - * LWP::Simple - */ - LWP_SIMPLE("LWP::Simple", Pattern.compile("LWP::Simple")), - - /** - * Lynx - */ - LYNX("Lynx", Pattern.compile("Lynx")), - - /** - * Madfox - */ - MADFOX("Madfox", Pattern.compile("Madfox")), - - /** - * magpie-crawler - */ - MAGPIE_CRAWLER("magpie-crawler", Pattern.compile("magpie-crawler")), - - /** - * MagpieRSS - */ - MAGPIERSS("MagpieRSS", Pattern.compile("MagpieRSS")), - - /** - * Mahiti Crawler - */ - MAHITI_CRAWLER("Mahiti Crawler", Pattern.compile("Mahiti Crawler")), - - /** - * Mail.RU - */ - MAIL_RU("Mail.Ru", Pattern.compile("Mail.RU(_Bot)?(/\\d+(\\.\\d+)*)?", Pattern.CASE_INSENSITIVE)), - - /** - * Maple browser - */ - MAPLE_BROWSER("Maple browser", Pattern.compile("Maple browser")), - - /** - * Maxthon - */ - MAXTHON("Maxthon", Pattern.compile("Maxthon")), - - /** - * Mechanize - */ - MECHANIZE("Mechanize", Pattern.compile("Mechanize")), - - /** - * Megatext - */ - MEGATEXT("Megatext", Pattern.compile("Megatext")), - - /** - * MetaGeneratorCrawler - */ - METAGENERATORCRAWLER("MetaGeneratorCrawler", Pattern.compile("MetaGeneratorCrawler")), - - /** - * MetaJobBot - */ - METAJOBBOT("MetaJobBot", Pattern.compile("MetaJobBot")), - - /** - * MetamojiCrawler - */ - METAMOJICRAWLER("MetamojiCrawler", Pattern.compile("MetamojiCrawler")), - - /** - * Metaspinner/0.01 - */ - METASPINNER("Metaspinner", Pattern.compile("Metaspinner(/\\d+(\\.\\d+)*)?")), - - /** - * MetaTagRobot - */ - METATAGROBOT("MetaTagRobot", Pattern.compile("MetaTagRobot")), - - /** - * MetaURI - */ - METAURI("MetaURI", Pattern.compile("MetaURI")), - - /** - * MIA Bot - */ - MIA_BOT("MIA Bot", Pattern.compile("MIA Bot")), - - /** - * MicroB - */ - MICROB("MicroB", Pattern.compile("MicroB")), - - /** - * Microsoft Edge - */ - MICROSOFT_EDGE("Microsoft Edge", Pattern.compile("Microsoft Edge")), - - /** - * Microsoft Edge - */ - MICROSOFT_EDGE_MOBILE("Microsoft Edge mobile", Pattern.compile("Microsoft Edge mobile")), - - /** - * Microsoft Office Existence Discovery - */ - MICROSOFT_OFFICE_EXISTENCE_DISCOVERY("Microsoft Office Existence Discovery", Pattern.compile("Microsoft Office Existence Discovery")), - - /** - * Microsoft WebDAV client - */ - MICROSOFT_WEBDAV_CLIENT("Microsoft WebDAV client", Pattern.compile("Microsoft WebDAV client")), - - /** - * Midori - */ - MIDORI("Midori", Pattern.compile("Midori")), - - /** - * Mini Browser - */ - MINI_BROWSER("Mini Browser", Pattern.compile("Mini Browser")), - - /** - * Minimo - */ - MINIMO("Minimo", Pattern.compile("Minimo")), - - /** - * miniRank - */ - MINIRANK("miniRank", Pattern.compile("miniRank")), - - /** - * Miro - */ - MIRO("Miro", Pattern.compile("Miro")), - - /** - * MJ12bot - */ - MJ12BOT("MJ12bot", Pattern.compile("MJ12bot")), - - /** - * MLBot - */ - MLBOT("MLBot", Pattern.compile("MLBot")), - - /** - * MnoGoSearch - */ - MNOGOSEARCH("MnoGoSearch", Pattern.compile("MnoGoSearch")), - - /** - * Moatbot - */ - MOATBOT("Moatbot", Pattern.compile("Moatbot")), - - /** - * moba-crawler - */ - MOBA_CRAWLER("moba-crawler", Pattern.compile("moba-crawler")), - - /** - * Mobile Firefox - */ - MOBILE_FIREFOX("Mobile Firefox", Pattern.compile("Firefox mobile")), - - /** - * Mobile Safari - */ - MOBILE_SAFARI("Mobile Safari", Pattern.compile("Safari mobile")), - - /** - * MojeekBot - */ - MOJEEKBOT("MojeekBot", Pattern.compile("MojeekBot")), - - /** - * Motoricerca-Robots.txt-Checker - */ - MOTORICERCA_ROBOTS_TXT_CHECKER("Motoricerca-Robots.txt-Checker", Pattern.compile("Motoricerca-Robots.txt-Checker")), - - /** - * Motorola Internet Browser - */ - MOTOROLA_INTERNET_BROWSER("Motorola Internet Browser", Pattern.compile("Motorola Internet Browser")), - - /** - * mozDex - */ - MOZDEX("mozDex", Pattern.compile("mozDex")), - - /** - * Mozilla - */ - MOZILLA("Mozilla", Pattern.compile("Mozilla")), - - /** - * Mp3Bot - */ - MP3BOT("Mp3Bot", Pattern.compile("Mp3Bot")), - - /** - * MPlayer - */ - MPLAYER("MPlayer", Pattern.compile("MPlayer")), - - /** - * MPlayer2 - */ - MPLAYER2("MPlayer2", Pattern.compile("MPlayer2")), - - /** - * MQbot - */ - MQBOT("MQbot", Pattern.compile("MQbot")), - - /** - * MSNBot - */ - MSNBOT("MSNBot", Pattern.compile("MSNBot")), - - /** - * MSRBOT - */ - MSRBOT("MSRBOT", Pattern.compile("MSRBOT")), - - /** - * muCommander - */ - MUCOMMANDER("muCommander", Pattern.compile("muCommander")), - - /** - * Multi-Browser XP - */ - MULTI_BROWSER_XP("Multi-Browser XP", Pattern.compile("Multi-Browser XP")), - - /** - * MultiCrawler - */ - MULTICRAWLER("MultiCrawler", Pattern.compile("MultiCrawler")), - - /** - * Multipage Validator - */ - MULTIPAGE_VALIDATOR("Multipage Validator", Pattern.compile("Multipage Validator")), - - /** - * MultiZilla - */ - MULTIZILLA("MultiZilla", Pattern.compile("MultiZilla")), - - /** - * My Internet Browser - */ - MY_INTERNET_BROWSER("My Internet Browser", Pattern.compile("My Internet Browser")), - - /** - * MyFamilyBot - */ - MYFAMILYBOT("MyFamilyBot", Pattern.compile("MyFamilyBot")), - - /** - * Najdi.si - */ - NAJDI_SI("Najdi.si", Pattern.compile("Najdi.si")), - - /** - * NaverBot - */ - NAVERBOT("NaverBot", Pattern.compile("NaverBot")), - - /** - * navissobot - */ - NAVISSOBOT("navissobot", Pattern.compile("navissobot")), - - /** - * NCSA Mosaic - */ - NCSA_MOSAIC("NCSA Mosaic", Pattern.compile("NCSA Mosaic")), - - /** - * NerdByNature.Bot - */ - NERDBYNATURE_BOT("NerdByNature.Bot", Pattern.compile("NerdByNature.Bot")), - - /** - * nestReader - */ - NESTREADER("nestReader", Pattern.compile("nestReader")), - - /** - * NetBox - */ - NETBOX("NetBox", Pattern.compile("NetBox")), - - /** - * NetCaptor - */ - NETCAPTOR("NetCaptor", Pattern.compile("NetCaptor")), - - /** - * NetcraftSurveyAgent - */ - NETCRAFTSURVEYAGENT("NetcraftSurveyAgent", Pattern.compile("NetcraftSurveyAgent")), - - /** - * netEstate Crawler - */ - NETESTATE_CRAWLER("netEstate Crawler", Pattern.compile("netEstate Crawler")), - - /** - * NetFront - */ - NETFRONT("NetFront", Pattern.compile("NetFront")), - - /** - * NetFront Mobile Content Viewer - */ - NETFRONT_MOBILE_CONTENT_VIEWER("NetFront Mobile Content Viewer", Pattern.compile("NetFront Mobile Content Viewer")), - - /** - * Netintelligence LiveAssessment - */ - NETINTELLIGENCE_LIVEASSESSMENT("Netintelligence LiveAssessment", Pattern.compile("Netintelligence LiveAssessment")), - - /** - * NetNewsWire - */ - NETNEWSWIRE("NetNewsWire", Pattern.compile("NetNewsWire")), - - /** - * NetPositive - */ - NETPOSITIVE("NetPositive", Pattern.compile("NetPositive")), - - /** - * NetResearchServer - */ - NETRESEARCHSERVER("NetResearchServer", Pattern.compile("NetResearchServer")), - - /** - * Netscape Navigator - */ - NETSCAPE_NAVIGATOR("Netscape Navigator", Pattern.compile("Netscape Navigator")), - - /** - * Netseer - */ - NETSEER("Netseer", Pattern.compile("Netseer")), - - /** - * NetSurf - */ - NETSURF("NetSurf", Pattern.compile("NetSurf")), - - /** - * Netvibes feed reader - */ - NETVIBES_FEED_READER("Netvibes feed reader", Pattern.compile("Netvibes feed reader")), - - /** - * NetWhatCrawler - */ - NETWHATCRAWLER("NetWhatCrawler", Pattern.compile("NetWhatCrawler")), - - /** - * Newsbeuter - */ - NEWSBEUTER("Newsbeuter", Pattern.compile("Newsbeuter")), - - /** - * NewsBreak - */ - NEWSBREAK("NewsBreak", Pattern.compile("NewsBreak")), - - /** - * NewsFox - */ - NEWSFOX("NewsFox", Pattern.compile("NewsFox")), - - /** - * NewsGatorOnline - */ - NEWSGATORONLINE("NewsGatorOnline", Pattern.compile("NewsGatorOnline")), - - /** - * NextGenSearchBot - */ - NEXTGENSEARCHBOT("NextGenSearchBot", Pattern.compile("NextGenSearchBot")), - - /** - * nextthing.org - */ - NEXTTHING_ORG("nextthing.org", Pattern.compile("nextthing.org")), - - /** - * NFReader - */ - NFREADER("NFReader", Pattern.compile("NFReader")), - - /** - * NG - */ - NG("NG", Pattern.compile("NG")), - - /** - * NG-Search - */ - NG_SEARCH("NG-Search", Pattern.compile("NG-Search")), - - /** - * Nigma.ru - */ - NIGMA_RU("Nigma.ru", Pattern.compile("Nigma.ru")), - - /** - * NimbleCrawler - */ - NIMBLECRAWLER("NimbleCrawler", Pattern.compile("NimbleCrawler")), - - /** - * NineSky - */ - NINESKY("NineSky", Pattern.compile("NineSky")), - - /** - * Nintendo Browser - */ - NINTENDO("Nintendo Browser", Pattern.compile("Nintendo Browser")), - - /** - * nodestackbot - */ - NODESTACKBOT("nodestackbot", Pattern.compile("nodestackbot")), - - /** - * Nokia SyncML Client - */ - NOKIA_SYNCML_CLIENT("Nokia SyncML Client", Pattern.compile("Nokia SyncML Client")), - - /** - * Nokia Web Browser - */ - NOKIA_WEB_BROWSER("Nokia Web Browser", Pattern.compile("Nokia Web Browser")), - - /** - * Novell BorderManager - */ - NOVELL_BORDERMANAGER("Novell BorderManager", Pattern.compile("Novell BorderManager")), - - /** - * noyona - */ - NOYONA("noyona", Pattern.compile("noyona")), - - /** - * NPBot - */ - NPBOT("NPBot", Pattern.compile("NPBot")), - - /** - * Nuhk - */ - NUHK("Nuhk", Pattern.compile("Nuhk")), - - /** - * NuSearch Spider - */ - NUSEARCH_SPIDER("NuSearch Spider", Pattern.compile("NuSearch Spider")), - - /** - * Nutch - */ - NUTCH("Nutch", Pattern.compile("Nutch")), - - /** - * nworm - */ - NWORM("nworm", Pattern.compile("nworm")), - - /** - * Nymesis - */ - NYMESIS("Nymesis", Pattern.compile("Nymesis")), - - /** - * Obigo - */ - OBIGO("Obigo", Pattern.compile("Obigo")), - - /** - * oBot - */ - OBOT("oBot", Pattern.compile("oBot")), - - /** - * Ocelli - */ - OCELLI("Ocelli", Pattern.compile("Ocelli")), - - /** - * Off By One - */ - OFF_BY_ONE("Off By One", Pattern.compile("Off By One")), - - /** - * Offline Explorer - */ - OFFLINE_EXPLORER("Offline Explorer", Pattern.compile("Offline Explorer")), - - /** - * Omea Reader - */ - OMEA_READER("Omea Reader", Pattern.compile("Omea Reader")), - - /** - * OmniExplorer_Bot - */ - OMNIEXPLORER_BOT("OmniExplorer_Bot", Pattern.compile("OmniExplorer_Bot")), - - /** - * OmniWeb - */ - OMNIWEB("OmniWeb", Pattern.compile("OmniWeb")), - - /** - * OnetSzukaj - */ - ONETSZUKAJ("OnetSzukaj", Pattern.compile("OnetSzukaj")), - - /** - * Openbot - */ - OPENBOT("Openbot", Pattern.compile("Openbot")), - - /** - * OpenCalaisSemanticProxy - */ - OPENCALAISSEMANTICPROXY("OpenCalaisSemanticProxy", Pattern.compile("OpenCalaisSemanticProxy")), - - /** - * OpenindexSpider - */ - OPENINDEXSPIDER("OpenindexSpider", Pattern.compile("OpenindexSpider")), - - /** - * Openwave Mobile Browser - */ - OPENWAVE_MOBILE_BROWSER("Openwave Mobile Browser", Pattern.compile("Openwave Mobile Browser")), - - /** - * Opera - */ - OPERA("Opera", Pattern.compile("Opera")), - - /** - * Opera Mini - */ - OPERA_MINI("Opera Mini", Pattern.compile("Opera Mini")), - - /** - * Opera Mobile - */ - OPERA_MOBILE("Opera Mobile", Pattern.compile("Opera Mobile")), - - /** - * Orbiter - */ - ORBITER("Orbiter", Pattern.compile("Orbiter")), - - /** - * Orca - */ - ORCA("Orca", Pattern.compile("Orca")), - - /** - * Oregano - */ - OREGANO("Oregano", Pattern.compile("Oregano")), - - /** - * OrgbyBot - */ - ORGBYBOT("OrgbyBot", Pattern.compile("OrgbyBot")), - - /** - * OsObot - */ - OSOBOT("OsObot", Pattern.compile("OsObot")), - - /** - * Outlook 2007 - */ - OUTLOOK_2007("Outlook 2007", Pattern.compile("Outlook 2007")), - - /** - * Outlook 2010 - */ - OUTLOOK_2010("Outlook 2010", Pattern.compile("Outlook 2010")), - - /** - * Outlook 2013 - */ - OUTLOOK_2013("Outlook 2013", Pattern.compile("Outlook 2013")), - - /** - * OWB - */ - OWB("OWB", Pattern.compile("OWB")), - - /** - * owsBot - */ - OWSBOT("owsBot", Pattern.compile("owsBot")), - - /** - * P3P Validator - */ - P3P_VALIDATOR("P3P Validator", Pattern.compile("P3P Validator")), - - /** - * page_verifier - */ - PAGE_VERIFIER("page_verifier", Pattern.compile("page_verifier")), - - /** - * Page2RSS - */ - PAGE2RSS("Page2RSS", Pattern.compile("Page2RSS")), - - /** - * PageBitesHyperBot - */ - PAGEBITESHYPERBOT("PageBitesHyperBot", Pattern.compile("PageBitesHyperBot")), - - /** - * PagePeeker - */ - PAGEPEEKER("PagePeeker", Pattern.compile("PagePeeker")), - - /** - * Pale Moon - */ - PALE_MOON("Pale Moon", Pattern.compile("Pale Moon")), - - /** - * Palm Pre web browser - */ - PALM_PRE_WEB_BROWSER("Palm Pre web browser", Pattern.compile("Palm Pre web browser")), - - /** - * Panscient web crawler - */ - PANSCIENT_WEB_CRAWLER("panscient.com", Pattern.compile("panscient.com")), - - /** - * Paparazzi! - */ - PAPARAZZI("Paparazzi!", Pattern.compile("Paparazzi!")), - - /** - * PaperLiBot - */ - PAPERLIBOT("PaperLiBot", Pattern.compile("PaperLiBot")), - - /** - * ParchBot - */ - PARCHBOT("ParchBot", Pattern.compile("ParchBot")), - - /** - * Patriott - */ - PATRIOTT("Patriott", Pattern.compile("Patriott")), - - /** - * Pattern is a web mining module for the Python programming language. - */ - PATTERN("Pattern", Pattern.compile("Pattern")), - - /** - * PEAR HTTP_Request - */ - PEAR_HTTP_REQUEST("PEAR HTTP_Request", Pattern.compile("PEAR HTTP_Request")), - - /** - * Peew - */ - PEEW("Peew", Pattern.compile("Peew")), - - /** - * percbotspider - */ - PERCBOTSPIDER("percbotspider", Pattern.compile("percbotspider")), - - /** - * Phaseout - */ - PHASEOUT("Phaseout", Pattern.compile("Phaseout")), - - /** - * Phoenix (old name for Firefox) - */ - PHOENIX("Phoenix (old name for Firefox)", Pattern.compile("Phoenix \\(old name for Firefox\\)")), - - /** - * PHP - */ - PHP("PHP", Pattern.compile("PHP")), - - /** - * PHP link checker - */ - PHP_LINK_CHECKER("PHP link checker", Pattern.compile("PHP link checker")), - - /** - * PHP OpenID library - */ - PHP_OPENID_LIBRARY("PHP OpenID library", Pattern.compile("PHP OpenID library")), - - /** - * PHPcrawl - */ - PHPCRAWL("PHPcrawl", Pattern.compile("PHPcrawl")), - - /** - * pingdom.com_bot - */ - PINGDOM_COM_BOT("pingdom.com_bot", Pattern.compile("pingdom.com_bot")), - - /** - * Pixray-Seeker - */ - PIXRAY_SEEKER("Pixray-Seeker", Pattern.compile("Pixray-Seeker")), - - /** - * Plex Media Center - */ - PLEX_MEDIA_CENTER("Plex Media Center", Pattern.compile("Plex Media Center")), - - /** - * Plukkie - */ - PLUKKIE("Plukkie", Pattern.compile("Plukkie")), - - /** - * Pocket Tunes - */ - POCKET_TUNES("Pocket Tunes", Pattern.compile("Pocket Tunes")), - - /** - * PocoMail - */ - POCOMAIL("PocoMail", Pattern.compile("PocoMail")), - - /** - * Podkicker - */ - PODKICKER("Podkicker", Pattern.compile("Podkicker")), - - /** - * POE-Component-Client-HTTP - */ - POE_COMPONENT_CLIENT_HTTP("POE-Component-Client-HTTP", Pattern.compile("POE-Component-Client-HTTP")), - - /** - * Pogodak.co.yu - */ - POGODAK_CO_YU("Pogodak.co.yu", Pattern.compile("Pogodak.co.yu")), - - /** - * Polaris - */ - POLARIS("Polaris", Pattern.compile("Polaris")), - - /** - * polixea.de - */ - POLIXEA_DE("polixea.de", Pattern.compile("polixea.de")), - - /** - * Pompos - */ - POMPOS("Pompos", Pattern.compile("Pompos")), - - /** - * Postbox - */ - POSTBOX("Postbox", Pattern.compile("Postbox")), - - /** - * posterus - */ - POSTERUS("posterus", Pattern.compile("posterus")), - - /** - * PostPost - */ - POSTPOST("PostPost", Pattern.compile("PostPost")), - - /** - * Powermarks - */ - POWERMARKS("Powermarks", Pattern.compile("Powermarks")), - - /** - * Prism - */ - PRISM("Prism", Pattern.compile("Prism")), - - /** - * ProCogBot - */ - PROCOGBOT("ProCogBot", Pattern.compile("ProCogBot")), - - /** - * proximic - */ - PROXIMIC("proximic", Pattern.compile("proximic")), - - /** - * PRTG Network Monitor - */ - PRTG_NETWORK_MONITOR("PRTG Network Monitor", Pattern.compile("PRTG Network Monitor")), - - /** - * PS Vita browser - */ - PS_VITA_BROWSER("PS Vita browser", Pattern.compile("PS Vita browser")), - - /** - * psbot - */ - PSBOT("psbot", Pattern.compile("psbot")), - - /** - * ptd-crawler - */ - PTD_CRAWLER("ptd-crawler", Pattern.compile("ptd-crawler")), - - /** - * Public Radio Player - */ - PUBLIC_RADIO_PLAYER("Public Radio Player", Pattern.compile("Public Radio Player")), - - /** - * PycURL - */ - PYCURL("PycURL", Pattern.compile("PycURL")), - - /** - * Python-requests - */ - PYTHON_REQUESTS("Python-requests", Pattern.compile("Python-requests")), - - /** - * Python-urllib - */ - PYTHON_URLLIB("Python-urllib", Pattern.compile("Python-urllib")), - - /** - * Python-webchecker - */ - PYTHON_WEBCHECKER("Python-webchecker", Pattern.compile("Python-webchecker")), - - /** - * Qirina Hurdler - */ - QIRINA_HURDLER("Qirina Hurdler", Pattern.compile("Qirina Hurdler")), - - /** - * QQbrowser - */ - QQBROWSER("QQbrowser", Pattern.compile("QQbrowser")), - - /** - * Qseero - */ - QSEERO("Qseero", Pattern.compile("Qseero")), - - /** - * QtWeb - */ - QTWEB("QtWeb", Pattern.compile("QtWeb")), - - /** - * Qualidator.com Bot - */ - QUALIDATOR_COM_BOT("Qualidator.com Bot", Pattern.compile("Qualidator.com Bot")), - - /** - * Quantcastbot - */ - QUANTCASTBOT("Quantcastbot", Pattern.compile("Quantcastbot")), - - /** - * quickobot - */ - QUICKOBOT("quickobot", Pattern.compile("quickobot")), - - /** - * QuickTime - */ - QUICKTIME("QuickTime", Pattern.compile("QuickTime")), - - /** - * QupZilla - */ - QUPZILLA("QupZilla", Pattern.compile("QupZilla")), - - /** - * R6 bot - */ - R6_BOT("R6 bot", Pattern.compile("R6 bot")), - - /** - * RADaR-Bot - */ - RADAR_BOT("RADaR-Bot", Pattern.compile("RADaR-Bot")), - - /** - * Radio Downloader - */ - RADIO_DOWNLOADER("Radio Downloader", Pattern.compile("Radio Downloader")), - - /** - * RankurBot - */ - RANKURBOT("RankurBot", Pattern.compile("RankurBot")), - - /** - * RedBot - */ - REDBOT("RedBot", Pattern.compile("RedBot")), - - /** - * Reeder - */ - REEDER("Reeder", Pattern.compile("Reeder")), - - /** - * Rekonq - */ - REKONQ("Rekonq", Pattern.compile("Rekonq")), - - /** - * REL Link Checker Lite - */ - REL_LINK_CHECKER_LITE("REL Link Checker Lite", Pattern.compile("REL Link Checker Lite")), - - /** - * retawq - */ - RETAWQ("retawq", Pattern.compile("retawq")), - - /** - * Robo Crawler - */ - ROBO_CRAWLER("Robo Crawler", Pattern.compile("Robo Crawler")), - - /** - * Robots_Tester - */ - ROBOTS_TESTER("Robots_Tester", Pattern.compile("Robots_Tester")), - - /** - * Robozilla - */ - ROBOZILLA("Robozilla", Pattern.compile("Robozilla")), - - /** - * RockMelt - */ - ROCKMELT("RockMelt", Pattern.compile("RockMelt")), - - /** - * ROME library - */ - ROME_LIBRARY("ROME library", Pattern.compile("ROME library")), - - /** - * Ronzoobot - */ - RONZOOBOT("Ronzoobot", Pattern.compile("Ronzoobot")), - - /** - * Rss Bandit - */ - RSS_BANDIT("Rss Bandit", Pattern.compile("Rss Bandit")), - - /** - * RSS Menu - */ - RSS_MENU("RSS Menu", Pattern.compile("RSS Menu")), - - /** - * RSS Popper - */ - RSS_POPPER("RSS Popper", Pattern.compile("RSS Popper")), - - /** - * RSS Radio - */ - RSS_RADIO("RSS Radio", Pattern.compile("RSS Radio")), - - /** - * RSSMicro.com RSS/Atom Feed Robot - */ - RSSMICRO_COM("RSSMicro.com RSS/Atom Feed Robot", Pattern.compile("RSSMicro.com RSS/Atom Feed Robot")), - - /** - * RSSOwl - */ - RSSOWL("RSSOwl", Pattern.compile("RSSOwl")), - - /** - * Ruky-Roboter - */ - RUKY_ROBOTER("Ruky-Roboter", Pattern.compile("Ruky-Roboter")), - - /** - * Ryouko - */ - RYOUKO("Ryouko", Pattern.compile("Ryouko")), - - /** - * RyzeCrawler - */ - RYZECRAWLER("RyzeCrawler", Pattern.compile("RyzeCrawler")), - - /** - * SaaYaa Explorer - */ - SAAYAA_EXPLORER("SaaYaa Explorer", Pattern.compile("SaaYaa Explorer")), - - /** - * Safari - */ - SAFARI("Safari", Pattern.compile("Safari")), - - /** - * Safari RSS reader - */ - SAFARI_RSS_READER("Safari RSS reader", Pattern.compile("Safari RSS reader")), - - /** - * Sage - */ - SAGE("Sage", Pattern.compile("Sage")), - - /** - * SAI Crawler - */ - SAI_CRAWLER("SAI Crawler", Pattern.compile("SAI Crawler")), - - /** - * SanszBot - */ - SANSZBOT("SanszBot", Pattern.compile("SanszBot")), - - /** - * SBIder - */ - SBIDER("SBIder", Pattern.compile("SBIder")), - - /** - * SBSearch - */ - SBSEARCH("SBSearch", Pattern.compile("SBSearch")), - - /** - * Scarlett - */ - SCARLETT("Scarlett", Pattern.compile("Scarlett")), - - /** - * schibstedsokbot - */ - SCHIBSTEDSOKBOT("schibstedsokbot", Pattern.compile("schibstedsokbot")), - - /** - * ScollSpider - */ - SCOLLSPIDER("ScollSpider", Pattern.compile("ScollSpider")), - - /** - * Scooter - */ - SCOOTER("Scooter", Pattern.compile("Scooter")), - - /** - * ScoutJet - */ - SCOUTJET("ScoutJet", Pattern.compile("ScoutJet")), - - /** - * SeaMonkey - */ - SEAMONKEY("SeaMonkey", Pattern.compile("SeaMonkey")), - - /** - * Search Engine World Robots.txt Validator - */ - SEARCH_ENGINE_WORLD_ROBOTS_TXT_VALIDATOR("Search Engine World Robots.txt Validator", Pattern - .compile("Search Engine World Robots.txt Validator")), - - /** - * search.KumKie.com - */ - SEARCH_KUMKIE_COM("search.KumKie.com", Pattern.compile("search.KumKie.com")), - - /** - * Search17Bot - */ - SEARCH17BOT("Search17Bot", Pattern.compile("Search17Bot")), - - /** - * Semager - */ - SEMAGER("Semager", Pattern.compile("Semager")), - - /** - * SEMC Browser - */ - SEMC_BROWSER("SEMC Browser", Pattern.compile("SEMC Browser")), - - /** - * SemrushBot - */ - SEMRUSHBOT("SemrushBot", Pattern.compile("SemrushBot")), - - /** - * Sensis Web Crawler - */ - SENSIS_WEB_CRAWLER("Sensis Web Crawler", Pattern.compile("Sensis Web Crawler")), - - /** - * SEODat - */ - SEODAT("SEODat", Pattern.compile("SEODat")), - - /** - * SEOENGBot - */ - SEOENGBOT("SEOENGBot", Pattern.compile("SEOENGBot")), - - /** - * SEOkicks-Robot - */ - SEOKICKS_ROBOT("SEOkicks-Robot", Pattern.compile("SEOkicks-Robot")), - - /** - * Setoozbot - */ - SETOOZBOT("Setoozbot", Pattern.compile("Setoozbot")), - - /** - * Seznam RSS reader - */ - SEZNAM_RSS_READER("Seznam RSS reader", Pattern.compile("Seznam RSS reader")), - - /** - * Seznam WAP Proxy - */ - SEZNAM_WAP_PROXY("Seznam WAP Proxy", Pattern.compile("Seznam WAP Proxy")), - - /** - * SeznamBot - */ - SEZNAMBOT("SeznamBot", Pattern.compile("SeznamBot")), - - /** - * SharpReader - */ - SHARPREADER("SharpReader", Pattern.compile("SharpReader")), - - /** - * Shelob - */ - SHELOB("Shelob", Pattern.compile("Shelob")), - - /** - * Shiira - */ - SHIIRA("Shiira", Pattern.compile("Shiira")), - - /** - * Shim-Crawler - */ - SHIM_CRAWLER("Shim-Crawler", Pattern.compile("Shim-Crawler")), - - /** - * ShopWiki - */ - SHOPWIKI("ShopWiki", Pattern.compile("ShopWiki")), - - /** - * ShowyouBot - */ - SHOWYOUBOT("ShowyouBot", Pattern.compile("ShowyouBot")), - - /** - * Shredder - */ - SHREDDER("Shredder", Pattern.compile("Shredder")), - - /** - * Siege - */ - SIEGE("Siege", Pattern.compile("Siege")), - - /** - * silk - */ - SILK("Silk", Pattern.compile("silk", Pattern.CASE_INSENSITIVE)), - - /** - * SimplePie - */ - SIMPLEPIE("SimplePie", Pattern.compile("SimplePie")), - - /** - * Sirketce/Busiverse - */ - SIRKETCE_BUSIVERSE("Sirketce/Busiverse", Pattern.compile("Sirketce/Busiverse")), - - /** - * sistrix - */ - SISTRIX("sistrix", Pattern.compile("sistrix")), - - /** - * Sitedomain-Bot - */ - SITEDOMAIN_BOT("Sitedomain-Bot", Pattern.compile("Sitedomain-Bot")), - - /** - * SiteKiosk - */ - SITEKIOSK("SiteKiosk", Pattern.compile("SiteKiosk")), - - /** - * SiteSucker - */ - SITESUCKER("SiteSucker", Pattern.compile("SiteSucker")), - - /** - * SkipStone - */ - SKIPSTONE("SkipStone", Pattern.compile("SkipStone")), - - /** - * SkreemRBot - */ - SKREEMRBOT("SkreemRBot", Pattern.compile("SkreemRBot")), - - /** - * Skyfire - */ - SKYFIRE("Skyfire", Pattern.compile("Skyfire")), - - /** - * Sleipnir - */ - SLEIPNIR("Sleipnir", Pattern.compile("Sleipnir")), - - /** - * SlimBoat - */ - SLIMBOAT("SlimBoat", Pattern.compile("SlimBoat")), - - /** - * SlimBrowser - */ - SLIMBROWSER("SlimBrowser", Pattern.compile("SlimBrowser")), - - /** - * smart.apnoti.com Robot - */ - SMART_APNOTI_COM_ROBOT("smart.apnoti.com Robot", Pattern.compile("smart.apnoti.com Robot")), - - /** - * snap.com - */ - SNAP_COM("snap.com", Pattern.compile("snap.com")), - - /** - * SnapBot - */ - SNAPBOT("SnapBot", Pattern.compile("SnapBot")), - - /** - * Snappy - */ - SNAPPY("Snappy", Pattern.compile("Snappy")), - - /** - * SniffRSS - */ - SNIFFRSS("SniffRSS", Pattern.compile("SniffRSS")), - - /** - * Snoopy - */ - SNOOPY("Snoopy", Pattern.compile("Snoopy")), - - /** - * Sogou - */ - SOGOU("Sogou", Pattern.compile("Sogou")), - - /** - * Sogou Explorer - */ - SOGOU_EXPLORER("Sogou Explorer", Pattern.compile("Sogou Explorer")), - - /** - * sogou spider - */ - SOGOU_SPIDER("sogou spider", Pattern.compile("sogou spider")), - - /** - * Songbird - */ - SONGBIRD("Songbird", Pattern.compile("Songbird")), - - /** - * Sosospider - */ - SOSOSPIDER("Sosospider", Pattern.compile("Sosospider")), - - /** - * Sparrow - */ - SPARROW("Sparrow", Pattern.compile("Sparrow")), - - /** - * spbot - */ - SPBOT("spbot", Pattern.compile("spbot")), - - /** - * Speedy - */ - SPEEDY("Speedy Spider", Pattern.compile("Speedy Spider")), - - /** - * Spicebird - */ - SPICEBIRD("Spicebird", Pattern.compile("Spicebird")), - - /** - * SpiderLing - */ - SPIDERLING("SpiderLing", Pattern.compile("SpiderLing")), - - /** - * Spinn3r - */ - SPINN3R("Spinn3r", Pattern.compile("Spinn3r")), - - /** - * Spock Crawler - */ - SPOCK_CRAWLER("Spock Crawler", Pattern.compile("Spock Crawler")), - - /** - * SpokeSpider - */ - SPOKESPIDER("SpokeSpider", Pattern.compile("SpokeSpider")), - - /** - * Sproose - */ - SPROOSE("Sproose", Pattern.compile("Sproose")), - - /** - * SrevBot - */ - SREVBOT("SrevBot", Pattern.compile("SrevBot")), - - /** - * SSLBot - */ - SSLBOT("SSLBot", Pattern.compile("SSLBot")), - - /** - * StackRambler - */ - STACKRAMBLER("StackRambler", Pattern.compile("StackRambler")), - - /** - * Stainless - */ - STAINLESS("Stainless", Pattern.compile("Stainless")), - - /** - * StatoolsBot - */ - STATOOLSBOT("StatoolsBot", Pattern.compile("StatoolsBot")), - - /** - * Steeler - */ - STEELER("Steeler", Pattern.compile("Steeler")), - - /** - * Strokebot - */ - STROKEBOT("Strokebot", Pattern.compile("Strokebot")), - - /** - * SubStream - */ - SUBSTREAM("SubStream", Pattern.compile("SubStream")), - - /** - * suggybot - */ - SUGGYBOT("suggybot", Pattern.compile("suggybot")), - - /** - * Summer - */ - SUMMER("Summer", Pattern.compile("Summer")), - - /** - * Sundance - */ - SUNDANCE("Sundance", Pattern.compile("Sundance")), - - /** - * Sundial - */ - SUNDIAL("Sundial", Pattern.compile("Sundial")), - - /** - * Sunrise - */ - SUNRISE("Sunrise", Pattern.compile("Sunrise")), - - /** - * SuperBot - */ - SUPERBOT("SuperBot", Pattern.compile("SuperBot")), - - /** - * Surf - */ - SURF("Surf", Pattern.compile("Surf")), - - /** - * Surphace Scout - */ - SURPHACE_SCOUT("Surphace Scout", Pattern.compile("Surphace Scout")), - - /** - * SurveyBot - */ - SURVEYBOT("SurveyBot", Pattern.compile("SurveyBot")), - - /** - * SWEBot - */ - SWEBOT("SWEBot", Pattern.compile("SWEBot")), - - /** - * Swiftfox - */ - SWIFTFOX("Swiftfox", Pattern.compile("Swiftfox")), - - /** - * Swiftweasel - */ - SWIFTWEASEL("Swiftweasel", Pattern.compile("Swiftweasel")), - - /** - * SygolBot - */ - SYGOLBOT("SygolBot", Pattern.compile("SygolBot")), - - /** - * SynooBot - */ - SYNOOBOT("SynooBot", Pattern.compile("SynooBot")), - - /** - * Szukacz - */ - SZUKACZ("Szukacz", Pattern.compile("Szukacz")), - - /** - * Szukankobot - */ - SZUKANKOBOT("Szukankobot", Pattern.compile("Szukankobot")), - - /** - * Tagoobot - */ - TAGOOBOT("Tagoobot", Pattern.compile("Tagoobot")), - - /** - * taptubot - */ - TAPTUBOT("taptubot", Pattern.compile("taptubot")), - - /** - * Tear - */ - TEAR("Tear", Pattern.compile("Tear")), - - /** - * TeaShark - */ - TEASHARK("TeaShark", Pattern.compile("TeaShark")), - - /** - * Technoratibot - */ - TECHNORATIBOT("Technoratibot", Pattern.compile("Technoratibot")), - - /** - * Teleport Pro - */ - TELEPORT_PRO("Teleport Pro", Pattern.compile("Teleport Pro")), - - /** - * TenFourFox - */ - TENFOURFOX("TenFourFox", Pattern.compile("TenFourFox")), - - /** - * TeragramCrawler - */ - TERAGRAMCRAWLER("TeragramCrawler", Pattern.compile("TeragramCrawler")), - - /** - * textractor - */ - TEXTRACTOR("textractor", Pattern.compile("textractor")), - - /** - * The Bat! - */ - THE_BAT("The Bat!", Pattern.compile("The Bat!")), - - /** - * Theophrastus - */ - THEOPHRASTUS("Theophrastus", Pattern.compile("Theophrastus")), - - /** - * TheWorld Browser - */ - THEWORLD_BROWSER("TheWorld Browser", Pattern.compile("TheWorld Browser")), - - /** - * Thumbnail.CZ robot - */ - THUMBNAIL_CZ_ROBOT("Thumbnail.CZ robot", Pattern.compile("Thumbnail.CZ robot")), - - /** - * ThumbShots-Bot - */ - THUMBSHOTS_BOT("ThumbShots-Bot", Pattern.compile("ThumbShots-Bot")), - - /** - * thumbshots-de-Bot - */ - THUMBSHOTS_DE_BOT("thumbshots-de-Bot", Pattern.compile("thumbshots-de-Bot")), - - /** - * Thumbshots.ru - */ - THUMBSHOTS_RU("Thumbshots.ru", Pattern.compile("Thumbshots.ru")), - - /** - * Thunderbird - */ - THUNDERBIRD("Thunderbird", Pattern.compile("Thunderbird")), - - /** - * TinEye - */ - TINEYE("TinEye", Pattern.compile("TinEye")), - - /** - * Tizen Browser - */ - TIZEN_BROWSER("Tizen Browser", Pattern.compile("Tizen Browser")), - - /** - * Tjusig - */ - TJUSIG("Tjusig", Pattern.compile("Tjusig")), - - /** - * Topicbot - */ - TOPICBOT("Topicbot", Pattern.compile("Topicbot")), - - /** - * Toread-Crawler - */ - TOREAD_CRAWLER("Toread-Crawler", Pattern.compile("Toread-Crawler")), - - /** - * Touche - */ - TOUCHE("Touche", Pattern.compile("Touche")), - - /** - * trendictionbot - */ - TRENDICTIONBOT("trendictionbot", Pattern.compile("trendictionbot")), - - /** - * Trileet NewsRoom - */ - TRILEET_NEWSROOM("Trileet NewsRoom", Pattern.compile("Trileet NewsRoom")), - - /** - * TT Explorer - */ - TT_EXPLORER("TT Explorer", Pattern.compile("TT Explorer")), - - /** - * Tulip Chain - */ - TULIP_CHAIN("Tulip Chain", Pattern.compile("Tulip Chain")), - - /** - * TurnitinBot - */ - TURNITINBOT("TurnitinBot", Pattern.compile("TurnitinBot")), - - /** - * TutorGigBot - */ - TUTORGIGBOT("TutorGigBot", Pattern.compile("TutorGigBot")), - - /** - * TwengaBot - */ - TWENGABOT("TwengaBot", Pattern.compile("TwengaBot")), - - /** - * Twiceler - */ - TWICELER("Twiceler", Pattern.compile("Twiceler")), - - /** - * Twikle - */ - TWIKLE("Twikle", Pattern.compile("Twikle")), - - /** - * Typhoeus - */ - TYPHOEUS("Typhoeus", Pattern.compile("Typhoeus")), - - /** - * UASlinkChecker - */ - UASLINKCHECKER("UASlinkChecker", Pattern.compile("UASlinkChecker")), - - /** - * UC Browser - */ - UC_BROWSER("UC Browser", Pattern.compile("UC Browser")), - - /** - * UltraBrowser - */ - ULTRABROWSER("UltraBrowser ", Pattern.compile("UltraBrowser ")), - - /** - * UnisterBot - */ - UNISTERBOT("UnisterBot", Pattern.compile("UnisterBot")), - - /** - * UnwindFetchor - */ - UNWINDFETCHOR("UnwindFetchor", Pattern.compile("UnwindFetchor")), - - /** - * updated - */ - UPDATED("updated", Pattern.compile("updated")), - - /** - * Updownerbot - */ - UPDOWNERBOT("Updownerbot", Pattern.compile("Updownerbot")), - - /** - * UptimeDog - */ - UPTIMEDOG("UptimeDog", Pattern.compile("UptimeDog")), - - /** - * UptimeRobot - */ - UPTIMEROBOT("UptimeRobot", Pattern.compile("UptimeRobot")), - - /** - * urlfan-bot - */ - URLFAN_BOT("urlfan-bot", Pattern.compile("urlfan-bot")), - - /** - * Urlfilebot (Urlbot) - */ - URLFILEBOT("Urlfilebot (Urlbot)", Pattern.compile("Urlfilebot \\(Urlbot\\)")), - - /** - * urlgrabber - */ - URLGRABBER("urlgrabber", Pattern.compile("urlgrabber")), - - /** - * Usejump - */ - USEJUMP("Usejump", Pattern.compile("Usejump")), - - /** - * uZard Web - */ - UZARD_WEB("uZard Web", Pattern.compile("uZard Web")), - - /** - * Uzbl - */ - UZBL("Uzbl", Pattern.compile("Uzbl")), - - /** - * Vagabondo - */ - VAGABONDO("Vagabondo", Pattern.compile("Vagabondo")), - - /** - * Validator.nu - */ - VALIDATOR_NU("Validator.nu", Pattern.compile("Validator.nu")), - - /** - * VERASYS 2k - */ - VERASYS_2K("VERASYS 2k", Pattern.compile("VERASYS 2k")), - - /** - * Vermut - */ - VERMUT("Vermut", Pattern.compile("Vermut")), - - /** - * Vespa Crawler - */ - VESPA_CRAWLER("Vespa Crawler", Pattern.compile("Vespa Crawler")), - - /** - * VideoSurf_bot - */ - VIDEOSURF_BOT("VideoSurf_bot", Pattern.compile("VideoSurf_bot")), - - /** - * virus_detector - */ - VIRUS_DETECTOR("virus_detector", Pattern.compile("virus_detector")), - - /** - * Vivaldi - */ - VIVALDI("Vivaldi", Pattern.compile("Vivaldi")), - - /** - * Visbot - */ - VISBOT("Visbot", Pattern.compile("Visbot")), - - /** - * VLC media player - */ - VLC_MEDIA_PLAYER("VLC media player", Pattern.compile("VLC media player")), - - /** - * VMBot - */ - VMBOT("VMBot", Pattern.compile("VMBot")), - - /** - * void-bot - */ - VOID_BOT("void-bot", Pattern.compile("void-bot")), - - /** - * VoilaBot - */ - VOILABOT("OrangeBot", Pattern.compile("OrangeBot")), - - /** - * Vonkeror - */ - VONKEROR("Vonkeror", Pattern.compile("Vonkeror")), - - /** - * VORTEX - */ - VORTEX("VORTEX", Pattern.compile("VORTEX")), - - /** - * voyager - */ - VOYAGER("voyager", Pattern.compile("voyager")), - - /** - * Vuze - */ - VUZE("Vuze", Pattern.compile("Vuze")), - - /** - * VWBot - */ - VWBOT("VWBot", Pattern.compile("VWBot")), - - /** - * W3C Checklink - */ - W3C_CHECKLINK("W3C Checklink", Pattern.compile("W3C Checklink")), - - /** - * W3C CSS Validator - */ - W3C_CSS_VALIDATOR("W3C CSS Validator", Pattern.compile("W3C CSS Validator")), - - /** - * W3C mobileOK Checker - */ - W3C_MOBILEOK_CHECKER("W3C mobileOK Checker", Pattern.compile("W3C mobileOK Checker")), - - /** - * W3C Validator - */ - W3C_VALIDATOR("W3C Validator", Pattern.compile("W3C Validator")), - - /** - * w3m - */ - W3M("w3m", Pattern.compile("w3m")), - - /** - * WapTiger - */ - WAPTIGER("WapTiger", Pattern.compile("WapTiger")), - - /** - * WASALive-Bot - */ - WASALIVE_BOT("WASALive-Bot", Pattern.compile("WASALive-Bot")), - - /** - * WatchMouse - */ - WATCHMOUSE("WatchMouse", Pattern.compile("WatchMouse")), - - /** - * WBSearchBot - */ - WBSEARCHBOT("WBSearchBot", Pattern.compile("WBSearchBot")), - - /** - * WDG CSSCheck - */ - WDG_CSSCHECK("WDG CSSCheck", Pattern.compile("WDG CSSCheck")), - - /** - * WDG Page Valet - */ - WDG_PAGE_VALET("WDG Page Valet", Pattern.compile("Page Valet")), - - /** - * WDG Validator - */ - WDG_VALIDATOR("WDG Validator", Pattern.compile("WDG Validator")), - - /** - * Web-sniffer - */ - WEB_SNIFFER("Web-sniffer", Pattern.compile("Web-sniffer")), - - /** - * WebAlta Crawler - */ - WEBALTA_CRAWLER("WebAlta Crawler", Pattern.compile("WebAlta Crawler")), - - /** - * WebarooBot - */ - WEBAROOBOT("WebarooBot", Pattern.compile("WebarooBot")), - - /** - * WebCollage - */ - WEBCOLLAGE("WebCollage", Pattern.compile("WebCollage")), - - /** - * WebCopier - */ - WEBCOPIER("WebCopier", Pattern.compile("WebCopier")), - - /** - * webfetch - */ - WEBFETCH("webfetch", Pattern.compile("webfetch")), - - /** - * webfs - */ - WEBFS("webfs", Pattern.compile("webfs")), - - /** - * Webian Shell - */ - WEBIAN_SHELL("Webian Shell", Pattern.compile("Webian Shell")), - - /** - * WebImages - */ - WEBIMAGES("WebImages", Pattern.compile("WebImages")), - - /** - * webinatorbot - */ - WEBINATORBOT("webinatorbot", Pattern.compile("webinatorbot")), - - /** - * webmastercoffee - */ - WEBMASTERCOFFEE("webmastercoffee", Pattern.compile("webmastercoffee")), - - /** - * WebNL - */ - WEBNL("WebNL", Pattern.compile("WebNL")), - - /** - * WebRankSpider - */ - WEBRANKSPIDER("WebRankSpider", Pattern.compile("WebRankSpider")), - - /** - * WebRender - */ - WEBRENDER("WebRender", Pattern.compile("WebRender")), - - /** - * Webscope Crawler - */ - WEBSCOPE_CRAWLER("Webscope Crawler", Pattern.compile("Webscope Crawler")), - - /** - * WebStripper - */ - WEBSTRIPPER("WebStripper", Pattern.compile("WebStripper")), - - /** - * WebWatch/Robot_txtChecker - */ - WEBWATCH_ROBOT_TXT_CHECKER("WebWatch/Robot_txtChecker", Pattern.compile("WebWatch/Robot_txtChecker")), - - /** - * WebZIP - */ - WEBZIP("WebZIP", Pattern.compile("WebZIP")), - - /** - * wectar - */ - WECTAR("wectar", Pattern.compile("wectar")), - - /** - * Weltweitimnetz Browser - */ - WELTWEITIMNETZ_BROWSER("Weltweitimnetz Browser", Pattern.compile("Weltweitimnetz Browser")), - - /** - * WeSEE:Search - */ - WESEE_SEARCH("WeSEE:Search", Pattern.compile("WeSEE:Search")), - - /** - * Wget - */ - WGET("Wget", Pattern.compile("Wget")), - - /** - * Whoismindbot - */ - WHOISMINDBOT("Whoismindbot", Pattern.compile("Whoismindbot")), - - /** - * WikioFeedBot - */ - WIKIOFEEDBOT("WikioFeedBot", Pattern.compile("WikioFeedBot")), - - /** - * wikiwix-bot - */ - WIKIWIX_BOT("wikiwix-bot", Pattern.compile("wikiwix-bot")), - - /** - * Willow Internet Crawler - */ - WILLOW_INTERNET_CRAWLER("Willow Internet Crawler", Pattern.compile("Willow Internet Crawler")), - - /** - * Winamp for Android - */ - WINAMP_FOR_ANDROID("Winamp for Android", Pattern.compile("Winamp for Android")), - - /** - * Windows Live Mail - */ - WINDOWS_LIVE_MAIL("Windows Live Mail", Pattern.compile("Windows Live Mail")), - - /** - * Windows Media Player - */ - WINDOWS_MEDIA_PLAYER("Windows Media Player", Pattern.compile("Windows Media Player")), - - /** - * WinHTTP - */ - WINHTTP("WinHTTP", Pattern.compile("WinHTTP")), - - /** - * WinkBot - */ - WINKBOT("WinkBot", Pattern.compile("WinkBot")), - - /** - * WinPodder - */ - WINPODDER("WinPodder", Pattern.compile("WinPodder")), - - /** - * WinWap - */ - WINWAP("WinWap", Pattern.compile("WinWap")), - - /** - * WinWebBot - */ - WINWEBBOT("WinWebBot", Pattern.compile("WinWebBot")), - - /** - * WIRE - */ - WIRE("WIRE", Pattern.compile("WIRE")), - - /** - * wKiosk - */ - WKIOSK("wKiosk", Pattern.compile("wKiosk")), - - /** - * WMCAI_robot - */ - WMCAI_ROBOT("WMCAI_robot", Pattern.compile("WMCAI_robot")), - - /** - * Woko - */ - WOKO("Woko", Pattern.compile("Woko")), - - /** - * WordPress pingback - */ - WORDPRESS_PINGBACK("WordPress pingback", Pattern.compile("WordPress pingback")), - - /** - * woriobot - */ - WORIOBOT("woriobot", Pattern.compile("woriobot")), - - /** - * WorldWideWeb - */ - WORLDWIDEWEB("WorldWideWeb", Pattern.compile("WorldWideWeb")), - - /** - * wOSBrowser - */ - WOSBROWSER("wOSBrowser", Pattern.compile("wOSBrowser")), - - /** - * Wotbox - */ - WOTBOX("Wotbox", Pattern.compile("Wotbox")), - - /** - * wsAnalyzer - */ - WSANALYZER("wsAnalyzer", Pattern.compile("wsAnalyzer")), - - /** - * www.fi crawler - */ - WWW_FI_CRAWLER("www.fi crawler", Pattern.compile("www.fi crawler")), - - /** - * WWW::Mechanize - */ - WWW_MECHANIZE("WWW::Mechanize", Pattern.compile("WWW::Mechanize")), - - /** - * wwwster - */ - WWWSTER("wwwster", Pattern.compile("wwwster")), - - /** - * Wyzo - */ - WYZO("Wyzo", Pattern.compile("Wyzo")), - - /** - * X-Smiles - */ - X_SMILES("X-Smiles", Pattern.compile("X-Smiles")), - - /** - * Xaldon WebSpider - */ - XALDON_WEBSPIDER("Xaldon WebSpider", Pattern.compile("Xaldon WebSpider")), - - /** - * XBMC - */ - XBMC("XBMC", Pattern.compile("XBMC")), - - /** - * Xenu - */ - XENU("Xenu", Pattern.compile("Xenu")), - - /** - * xine - */ - XINE("xine", Pattern.compile("xine")), - - /** - * XmarksFetch - */ - XMARKSFETCH("XmarksFetch", Pattern.compile("XmarksFetch")), - - /** - * XML-RPC for PHP - */ - XML_RPC_FOR_PHP("XML-RPC for PHP", Pattern.compile("XML-RPC for PHP")), - - /** - * XML-RPC for Ruby - */ - XML_RPC_FOR_RUBY("XML-RPC for Ruby", Pattern.compile("XML-RPC for Ruby")), - - /** - * XML Sitemaps Generator - */ - XML_SITEMAPS_GENERATOR("XML Sitemaps Generator", Pattern.compile("XML Sitemaps Generator")), - - /** - * XMPlay - */ - XMPLAY("XMPlay", Pattern.compile("XMPlay")), - - /** - * Yaanb - */ - YAANB("Yaanb", Pattern.compile("Yaanb")), - - /** - * yacybot - */ - YACYBOT("yacybot", Pattern.compile("yacybot")), - - /** - * Yahoo! - */ - YAHOO("Yahoo!", Pattern.compile("Yahoo!")), - - /** - * Yahoo Link Preview - */ - YAHOO_LINK_PREVIEW("Yahoo Link Preview", Pattern.compile("Yahoo Link Preview")), - - /** - * Yahoo! JAPAN - */ - YAHOO_JAPAN("Yahoo! JAPAN", Pattern.compile("Yahoo! JAPAN")), - - /** - * YahooFeedSeeker - */ - YAHOOFEEDSEEKER("YahooFeedSeeker", Pattern.compile("YahooFeedSeeker")), - - /** - * Yandex.Browser - */ - YANDEX_BROWSER("Yandex.Browser", Pattern.compile("Yandex\\.Browser")), - - /** - * YandexBot - */ - YANDEXBOT("YandexBot", Pattern.compile("YandexBot")), - - /** - * Yanga - */ - YANGA("Yanga", Pattern.compile("Yanga")), - - /** - * YeahReader - */ - YEAHREADER("YeahReader", Pattern.compile("YeahReader")), - - /** - * YioopBot - */ - YIOOPBOT("YioopBot", Pattern.compile("YioopBot")), - - /** - * YodaoBot - */ - YODAOBOT("YodaoBot", Pattern.compile("YodaoBot")), - - /** - * Yoono Bot - */ - YOONO_BOT("Yoono Bot", Pattern.compile("Yoono Bot")), - - /** - * YoudaoBot - */ - YOUDAOBOT("YoudaoBot", Pattern.compile("YoudaoBot")), - - /** - * YowedoBot - */ - YOWEDOBOT("YowedoBot", Pattern.compile("YowedoBot")), - - /** - * YRSpider - */ - YRSPIDER("YRSpider", Pattern.compile("YRSpider")), - - /** - * ZACATEK_CZ - */ - ZACATEK_CZ("ZACATEK_CZ", Pattern.compile("ZACATEK_CZ")), - - /** - * zBrowser - */ - ZBROWSER("zBrowser", Pattern.compile("zBrowser")), - - /** - * Zend_Http_Client - */ - ZEND_HTTP_CLIENT("Zend_Http_Client", Pattern.compile("Zend_Http_Client")), - - /** - * Zeusbot - */ - ZEUSBOT("Zeusbot", Pattern.compile("Zeusbot")), - - /** - * ZipZap - */ - ZIPZAP("ZipZap", Pattern.compile("ZipZap")), - - /** - * ZookaBot - */ - ZOOKABOT("ZookaBot", Pattern.compile("ZookaBot(/\\d+(\\.\\d+)*)?", Pattern.CASE_INSENSITIVE)), - - /** - * ZoomSpider (ZSEBOT) - */ - ZOOMSPIDER("ZoomSpider (ZSEBOT)", Pattern.compile("ZoomSpider \\(ZSEBOT\\)")), - - /** - * ZyBorg - */ - ZYBORG("ZyBorg", Pattern.compile("ZyBorg")); - - /** - * This method try to find by the given family name a matching enum value. The family name must match against an - * user agent entry in UAS data file. - * - * @param family - * name of an user agent family - * @return the matching enum value or {@code UserAgentFamily#UNKNOWN} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - @Nonnull - public static UserAgentFamily evaluate(@Nonnull final String family) { - Check.notNull(family, "family"); - - UserAgentFamily result = UNKNOWN; - - // search by name - result = evaluateByName(family); - - // search by pattern - if (result == UNKNOWN) { - result = evaluateByPattern(family); - } - - return result; - } - - /** - * This method try to find by the given family name a matching enum value. The family name will be evaluated against - * the stored name of an user agent entry. - * - * @param family - * name of an user agent family - * @return the matching enum value or {@code UserAgentFamily#UNKNOWN} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - @Nonnull - protected static UserAgentFamily evaluateByName(@Nonnull final String family) { - Check.notNull(family, "family"); - - UserAgentFamily result = UNKNOWN; - for (final UserAgentFamily value : values()) { - if (value.getName().equalsIgnoreCase(family)) { - result = value; - break; - } - } - - return result; - } - - /** - * This method try to find by the given family name a matching enum value. The family name will be evaluated against - * the stored regular expression of an user agent entry. - * - * @param family - * name of an user agent family - * @return the matching enum value or {@code UserAgentFamily#UNKNOWN} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - @Nonnull - protected static UserAgentFamily evaluateByPattern(@Nonnull final String family) { - Check.notNull(family, "family"); - - UserAgentFamily result = UNKNOWN; - for (final UserAgentFamily value : values()) { - final Matcher m = value.getPattern().matcher(family); - if (m.matches()) { - result = value; - break; - } - } - - return result; - } - - /** - * The internal family name in the UAS database. - */ - @Nonnull - private final String name; - - /** - * The regular expression which a family name must be match. - */ - @Nonnull - private final Pattern pattern; - - private UserAgentFamily(@Nonnull final String name, @Nonnull final Pattern pattern) { - this.name = name; - this.pattern = pattern; - } - - /** - * Gets the internal family name in the UAS database. - * - * @return the internal family name - */ - @Nonnull - public String getName() { - return this.name; - } - - /** - * Gets the regular expression which a family name must be match with. - * - * @return regular expression - */ - @Nonnull - public Pattern getPattern() { - return pattern; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/UserAgentStringParser.java b/app/src/main/java/net/sf/uadetector/UserAgentStringParser.java deleted file mode 100644 index 79cc6b5..0000000 --- a/app/src/main/java/net/sf/uadetector/UserAgentStringParser.java +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import javax.annotation.PreDestroy; - -/** - * Basic interface for user agent string parsers. - * - * @author André Rouél - */ -public interface UserAgentStringParser { - - /** - * Returns the current version information of the used UAS data. - * - *

- * The version will be set by the UAS data provider (http://user-agent-string.info/) and the version should - * look like this:
- *
- * format: YYYYMMDD-counter (counter is two digits long)
- * example: 20120931-02 - * - * @return version of the current UAS data - */ - String getDataVersion(); - - /** - * Detects informations about a network client based on a user agent string.
- *
- * Typically user agent string will be read by an instance of {@code HttpServletRequest}. With the method - * {@code getHeader("User-Agent")} you can get direct access to this string. - * - * @param userAgent - * user agent string - * @return the detected information of an user agent - */ - ReadableUserAgent parse(final String userAgent); - - /** - * In environments where the JVM will never shut down while reinstalling UADetector, it is necessary to manually - * shutdown running threads of UserAgentStringParsers with updating functionality like - * UADetectorServiceFactory.getCachingAndUpdatingParser() or - * UADetectorServiceFactory.getOnlineUpdatingParser(). - *

- * For example, if a web application with UADetector will be re-deployed within an Apache Tomcat you must - * shutdown your self-created or via UADetectorServiceFactory retrieved updating - * UserAgentStringParser otherwise more and more threads will be registered. - *

- * An implementation of UserAgentStringParser has updating functionality if it works with a - * {@link net.sf.uadetector.datastore.RefreshableDataStore}. - *

- * If you call shutdown on a non-updating UserAgentStringParser implementation nothing will happen. - *

- * A number of Dependency Injection containers support the annotation {@link PreDestroy} which is be useful for - * indicating methods that should be called when the container is shutting down. This annotation is available by - * default in Java SE 7 and can be made available through the external library jsr250-api-1.0.jar for earlier - * versions of Java. - *

- * We recommend to annotate an implementation of {@link #shutdown()} with {@link PreDestroy} to inform a container - * (for example Spring Framework) to trigger the shutdown method automatically by convention during the - * shutdown lifecycle. This saves developers to call explicitly the shutdown method. - *

- * To shutdown all managed {@code ExecutorService} by UADetector at once, you can call also - * {@link net.sf.uadetector.internal.util.ExecutorServices#shutdownAll()}. - */ - void shutdown(); - -} diff --git a/app/src/main/java/net/sf/uadetector/UserAgentType.java b/app/src/main/java/net/sf/uadetector/UserAgentType.java deleted file mode 100644 index 9bdda7d..0000000 --- a/app/src/main/java/net/sf/uadetector/UserAgentType.java +++ /dev/null @@ -1,142 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.internal.data.domain.Robot; - -/** - * This enum represents the type of an user agent. The assignment to a type is performed within the UAS data. - * - * @author André Rouél - */ -public enum UserAgentType { - - /** - * A web browser, especially on a desktop, notebook or workstation - */ - BROWSER("Browser"), - - /** - * An email client, email reader - */ - EMAIL_CLIENT("Email client"), - - /** - * A news aggregator, also termed a feed aggregator, feed reader, news reader, RSS - * reader or simply aggregator - */ - FEED_READER("Feed Reader"), - - /** - * A library is a collection of resources used to develop software. - */ - LIBRARY("Library"), - - /** - * Media player, also called multimedia player, is a term typically used to describe computer software for playing - * back multimedia files. - */ - MEDIAPLAYER("Multimedia Player"), - - /** - * A mobile browser, also called a microbrowser, minibrowser, or wireless internet browser (WIB), is a web browser - * designed for use on a mobile device. - */ - MOBILE_BROWSER("Mobile Browser"), - - /** - * Offline browser which may download completely or partially a website to a hard drive - */ - OFFLINE_BROWSER("Offline Browser"), - - /** - * A software that doesn't match the other types - */ - OTHER("Other"), - - /** - * Bots, such as Web crawlers - */ - ROBOT(Robot.TYPENAME), - - /** - * An unknown user agent type - */ - UNKNOWN(""), - - /** - * A software to hide the real user agent information - */ - USERAGENT_ANONYMIZER("Useragent Anonymizer"), - - /** - * A software or service that validates parts of a website or webservice, such as CSS, HTML, JSON, XML - */ - VALIDATOR("Validator"), - - /** - * A WAP browser is a web browser for mobile devices such as mobile phones that uses the Wireless Application - * Protocol (WAP). WAP is a technical standard for accessing information over a mobile wireless network. - */ - WAP_BROWSER("Wap Browser"); - - /** - * This method try to find by the given type name a matching enum value. The type name must match against an user - * agent entry in UAS data file. - * - * @param typeName - * name of an user agent type - * @return the matching enum value or {@code UserAgentType#UNKNOWN} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - public static UserAgentType evaluateByTypeName(@Nonnull final String typeName) { - Check.notNull(typeName, "typeName"); - - UserAgentType result = UNKNOWN; - for (final UserAgentType value : values()) { - if (value.getName().equals(typeName)) { - result = value; - break; - } - } - return result; - } - - /** - * Name of the user agent type - */ - @Nonnull - private final String name; - - private UserAgentType(@Nonnull final String name) { - this.name = name; - } - - /** - * Returns the name of the user agent type. - * - * @return name of the type - */ - @Nonnull - public String getName() { - return name; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/VersionNumber.java b/app/src/main/java/net/sf/uadetector/VersionNumber.java deleted file mode 100644 index 3e941ee..0000000 --- a/app/src/main/java/net/sf/uadetector/VersionNumber.java +++ /dev/null @@ -1,435 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.regex.Pattern; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import net.sf.qualitycheck.Check; -import net.sf.qualitycheck.exception.IllegalStateOfArgumentException; -import net.sf.uadetector.internal.util.AlphanumComparator; - -/** - * The {@code VersionNumber} class represents the version number of an operating system or User-Agent.
- *
- * A {@code VersionNumber} object is immutable, their values cannot be changed after creation. - * - * @author André Rouél - */ -public final class VersionNumber implements ReadableVersionNumber, Serializable { - - /** - * Empty extension or addition of a version number - */ - public static final String EMPTY_EXTENSION = ""; - - /** - * Empty group or category of a version number - */ - public static final String EMPTY_GROUP = ""; - - /** - * Minimum number of numeric group a version number - */ - private static final int MIN_GROUP_SIZE = 3; - - /** - * Regular expression to find only numerical values ​​in strings - */ - private static final Pattern NUMERIC = Pattern.compile("\\d+"); - - /** - * Separator between numeric groups of a version number - */ - private static final char SEPARATOR = '.'; - - /** - * Serialization version - */ - private static final long serialVersionUID = 1L; - - /** - * Defines an empty or not set version number - */ - public static final VersionNumber UNKNOWN = new VersionNumber(EMPTY_GROUP); - - /** - * Checks a string that only numerical values ​​are present. Negative numbers are not included. - * - * @param text - * string to be tested - * @return {@code true} if only numeric characters are present, otherwise {@code false} - */ - private static boolean isNumeric(final String text) { - return NUMERIC.matcher(text).matches(); - } - - /** - * Interprets a string with version information. The last version number in the string will be searched and - * processed. - * - * @param text - * string with version information - * @return an object of {@code VersionNumber}, never {@code null} - */ - public static VersionNumber parseLastVersionNumber(@Nonnull final String text) { - return VersionParser.parseLastVersionNumber(Check.notNull(text, "text")); - } - - /** - * Try to determine the version number of the operating system by parsing the user agent string. - * - * - * @param family - * family of the operating system - * @param userAgent - * user agent string - * @return extracted version number - */ - public static VersionNumber parseOperatingSystemVersion(@Nonnull final OperatingSystemFamily family, @Nonnull final String userAgent) { - Check.notNull(family, "family"); - Check.notNull(userAgent, "userAgent"); - return VersionParser.parseOperatingSystemVersion(family, userAgent); - } - - /** - * Interprets a string with version information. The first found group will be taken and processed. - * - * @param version - * version as string - * @return an object of {@code VersionNumber}, never {@code null} - */ - public static VersionNumber parseVersion(@Nonnull final String version) { - return VersionParser.parseVersion(Check.notNull(version, "version")); - } - - /** - * Replaces all {@code null} values in the given list of groups with {@code VersionNumber#EMPTY_GROUP}. - * - * @param groups - * list of numbers of a version number - * @return a new list of groups without {@code null} values - */ - public static List replaceNullValueWithEmptyGroup(@Nonnull final List groups) { - Check.notNull(groups, "groups"); - - final List result = new ArrayList(groups.size()); - for (final String group : groups) { - if (group == null) { - result.add(EMPTY_GROUP); - } else { - result.add(group); - } - } - for (int i = result.size(); i < MIN_GROUP_SIZE; i++) { - result.add(EMPTY_GROUP); - } - return result; - } - - /** - * Converts the given list of numbers in a version string. The groups of the version number will be separated by a - * dot. - * - * @param groups - * list of numbers of a version number - * @return a formated version string - */ - private static String toVersionString(@Nonnull final List groups) { - final StringBuilder builder = new StringBuilder(6); - int count = 0; - for (final String segment : groups) { - if (EMPTY_GROUP.equals(segment)) { - break; - } else { - if (count > 0) { - builder.append(SEPARATOR); - } - builder.append(segment); - } - count++; - } - return builder.toString(); - } - - /** - * Extension or suffix of the version number consisting of alphanumeric and special characters - */ - @Nonnull - private final String extension; - - /** - * Groups, segments or categories of the version number - */ - @Nonnull - private final List groups; - - /** - * Constructs a {@code VersionNumber} with the given numeric groups, such as major, minor and bugfix number. - * - * @param groups - * list of numbers of a version number - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if one of the segments of the version number is smaller than 0 and not empty - */ - public VersionNumber(@Nonnull final List groups) { - this(groups, EMPTY_EXTENSION); - } - - /** - * Constructs a {@code VersionNumber} with the given numeric groups, such as major, minor and bugfix number and - * extension. - * - * @param groups - * list of numbers of a version number - * @param extension - * extension of a version number - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if one of the groups of the version number is not empty or a positive number - */ - public VersionNumber(@Nonnull final List groups, @Nonnull final String extension) { - Check.notNull(groups, "groups"); - Check.notNull(extension, "extension"); - - final List segments = replaceNullValueWithEmptyGroup(groups); - int i = 0; - for (final String segment : segments) { - if (!EMPTY_GROUP.equals(segment) && !isNumeric(segment)) { - throw new IllegalStateOfArgumentException("The segment on position " + i + " (" + segment + ") must be a number."); - } - i++; - } - - this.groups = segments; - this.extension = extension; - } - - /** - * Constructs a {@code VersionNumber} with the given major number and without a minor and bugfix number. - * - * @param major - * major group of the version number - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the major segment is smaller than 0 and not empty - */ - public VersionNumber(@Nonnull final String major) { - this(Check.notNull(major, "major"), EMPTY_GROUP); - } - - /** - * Constructs a {@code VersionNumber} with the given major, minor number and without a bugfix number. - * - * @param major - * major group of the version number - * @param minor - * minor group of the version number - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the major or minor segment is smaller than 0 and not empty - */ - public VersionNumber(@Nonnull final String major, @Nonnull final String minor) { - this(Check.notNull(major, "major"), Check.notNull(minor, "minor"), EMPTY_GROUP); - } - - /** - * Constructs a {@code VersionNumber} with the given major, minor and bugfix number. - * - * @param major - * major group of the version number - * @param minor - * minor group of the version number - * @param bugfix - * bugfix group of the version number - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the major, minor or bugfix segment is smaller than 0 and not empty - */ - public VersionNumber(@Nonnull final String major, @Nonnull final String minor, @Nonnull final String bugfix) { - this(Check.notNull(major, "major"), Check.notNull(minor, "minor"), Check.notNull(bugfix, "bugfix"), EMPTY_EXTENSION); - } - - /** - * Constructs a {@code VersionNumber} with the given major, minor and bugfix number and extension. - * - * @param major - * major group of the version number - * @param minor - * minor group of the version number - * @param bugfix - * bugfix group of the version number - * @param extension - * extension of a version number - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the major, minor or bugfix segment is smaller than 0 and not empty - */ - public VersionNumber(@Nonnull final String major, @Nonnull final String minor, @Nonnull final String bugfix, - @Nonnull final String extension) { - this(Arrays.asList(Check.notNull(major, "major"), Check.notNull(minor, "minor"), Check.notNull(bugfix, "bugfix")), Check.notNull( - extension, "extension")); - } - - /** - * Compares this version number with the specified version number for order. Returns a negative integer, zero, or a - * positive integer as this version number is less than, equal to, or greater than the specified version number. - * - * @return a negative integer, zero, or a positive integer as this version number is less than, equal to, or greater - * than the specified version number. - */ - @Override - public int compareTo(@Nullable final ReadableVersionNumber other) { - int result = 0; - if (other == null) { - result = -1; - } else { - Check.notNull(other.getGroups(), "other.getGroups()"); - final int length = groups.size() < other.getGroups().size() ? groups.size() : other.getGroups().size(); - final AlphanumComparator comparator = new AlphanumComparator(); - result = comparator.compare(toVersionString(groups.subList(0, length)), toVersionString(other.getGroups().subList(0, length))); - if (result == 0) { - result = groups.size() > other.getGroups().size() ? 1 : groups.size() < other.getGroups().size() ? -1 : 0; - } - if (result == 0) { - result = extension.compareTo(other.getExtension()); - } - if (result == 0) { - result = comparator.compare(toVersionString(), other.toVersionString()); - } - } - return result; - } - - /** - * Indicates whether some other object is "equal to" this version number. - * - * @return {@code true} if the given version number is equal to this one - */ - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final VersionNumber other = (VersionNumber) obj; - if (!groups.equals(other.groups)) { - return false; - } - if (!extension.equals(other.extension)) { - return false; - } - return true; - } - - /** - * Gets the bugfix category of the version number. - */ - @Override - public String getBugfix() { - return groups.get(2); - } - - /** - * Gets the addition or extension of the version number. - * - * @return extension of the version number - */ - @Override - public String getExtension() { - return extension; - } - - /** - * Get all groups (or categories) of this version number. The first element in the list is the major category, - * followed by the minor and bugfix segment of the version number.
- *
- * The returned list of the version number segments is immutable. - * - * @return an unmodifiable view of the of the version number groups - */ - @Override - public List getGroups() { - return Collections.unmodifiableList(groups); - } - - /** - * Gets the major category of the version number. - */ - @Override - public String getMajor() { - return groups.get(0); - } - - /** - * Gets the major category of the version number. - */ - @Override - public String getMinor() { - return groups.get(1); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + groups.hashCode(); - result = prime * result + extension.hashCode(); - return result; - } - - /** - * Returns a string representation of the version number. - * - * @return a string representation of this version number - */ - @Nonnull - @Override - public String toString() { - return "VersionNumber [groups=" + groups + ", extension=" + extension + "]"; - } - - /** - * Gets this version number as string. - * - * @return numeric groups as dot separated version number string - */ - @Nonnull - @Override - public String toVersionString() { - return toVersionString(groups) + extension; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/VersionParser.java b/app/src/main/java/net/sf/uadetector/VersionParser.java deleted file mode 100644 index 190ac07..0000000 --- a/app/src/main/java/net/sf/uadetector/VersionParser.java +++ /dev/null @@ -1,379 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; - -/** - * This class is used to detect version information within User-Agent strings. - * - * @author André Rouél - */ -final class VersionParser { - - /** - * Index number of the group in a matching {@link Pattern} which contains the extension/suffix of a version string - */ - private static final int EXTENSION_INDEX = 5; - - /** - * Index number of the group in a matching {@link Pattern} which contains the first/major number of a version string - */ - private static final int MAJOR_INDEX = 1; - - /** - * Regular expression to analyze a version number separated by a dot - */ - private static final Pattern VERSIONNUMBER = Pattern.compile("((\\d+)((\\.\\d+)+)?)"); - - /** - * Regular expression to analyze a version number separated by a dot with suffix - */ - private static final Pattern VERSIONNUMBER_WITH_SUFFIX = Pattern.compile(VERSIONNUMBER.pattern() + "((\\s|\\-|\\.|\\[|\\]|\\w+)+)?"); - - /** - * Regular expression to analyze segments of a version string, consisting of prefix, numeric groups and suffix - */ - private static final Pattern VERSIONSTRING = Pattern.compile("^" + VERSIONNUMBER_WITH_SUFFIX.pattern()); - - /** - * This method try to determine the version number of the operating system Android more accurately. - * - * @param userAgent - * user agent string - * @return more accurately identified version number or {@code null} - */ - static VersionNumber identifyAndroidVersion(@Nonnull final String userAgent) { - VersionNumber version = VersionNumber.UNKNOWN; - final List patterns = new ArrayList(); - patterns.add(Pattern.compile("Android\\s?((\\d+)((\\.\\d+)+)?(\\-(\\w|\\d)+)?);")); - patterns.add(Pattern.compile("Android\\-((\\d+)((\\.\\d+)+)?(\\-(\\w|\\d)+)?);")); - for (final Pattern pattern : patterns) { - final Matcher m = pattern.matcher(userAgent); - if (m.find()) { - version = parseFirstVersionNumber(m.group(MAJOR_INDEX)); - break; - } - } - return version; - } - - /** - * This method try to determine the version number of the operating system Bada more accurately. - * - * @param userAgent - * user agent string - * @return more accurately identified version number or {@code null} - */ - static VersionNumber identifyBadaVersion(final String userAgent) { - VersionNumber version = VersionNumber.UNKNOWN; - final Pattern pattern = Pattern.compile("Bada/((\\d+)((\\.\\d+)+)?)"); - final Matcher m = pattern.matcher(userAgent); - if (m.find()) { - version = parseFirstVersionNumber(m.group(MAJOR_INDEX)); - } - return version; - } - - /** - * This method try to determine the version number of an operating system of a BSD platform more accurately. - * - * @param userAgent - * user agent string - * @return more accurately identified version number or {@code null} - */ - static VersionNumber identifyBSDVersion(final String userAgent) { - VersionNumber version = VersionNumber.UNKNOWN; - final Pattern pattern = Pattern.compile("\\w+bsd\\s?((\\d+)((\\.\\d+)+)?((\\-|_)[\\w\\d\\-]+)?)", Pattern.CASE_INSENSITIVE); - final Matcher m = pattern.matcher(userAgent); - if (m.find()) { - version = parseFirstVersionNumber(m.group(MAJOR_INDEX)); - } - return version; - } - - /** - * This method try to determine the version number of the operating system iOS more accurately. - * - * @param userAgent - * user agent string - * @return more accurately identified version number or {@code null} - */ - static VersionNumber identifyIOSVersion(final String userAgent) { - VersionNumber version = VersionNumber.UNKNOWN; - final List patterns = new ArrayList(); - patterns.add(Pattern.compile("iPhone OS\\s?((\\d+)((\\_\\d+)+)?) like Mac OS X")); - patterns.add(Pattern.compile("CPU OS\\s?((\\d+)((\\_\\d+)+)?) like Mac OS X")); - patterns.add(Pattern.compile("iPhone OS\\s?((\\d+)((\\.\\d+)+)?);")); - for (final Pattern pattern : patterns) { - final Matcher m = pattern.matcher(userAgent); - if (m.find()) { - version = parseFirstVersionNumber(m.group(MAJOR_INDEX).replaceAll("_", ".")); - break; - } - } - return version; - } - - /** - * This method try to determine the version number of the running JVM more accurately. - * - * @param userAgent - * user agent string - * @return more accurately identified version number or {@code null} - */ - static VersionNumber identifyJavaVersion(final String userAgent) { - VersionNumber version = VersionNumber.UNKNOWN; - final List patterns = new ArrayList(); - patterns.add(Pattern.compile("Java/((\\d+)((\\.\\d+)+)?((\\-|_)[\\w\\d\\-]+)?)")); - patterns.add(Pattern.compile("Java((\\d+)((\\.\\d+)+)?((\\-|_)[\\w\\d\\-]+)?)")); - for (final Pattern pattern : patterns) { - final Matcher m = pattern.matcher(userAgent); - if (m.find()) { - version = parseFirstVersionNumber(m.group(MAJOR_INDEX)); - break; - } - } - return version; - } - - /** - * This method try to determine the version number of the operating system OS X more accurately. - * - * @param userAgent - * user agent string - * @return more accurately identified version number or {@code null} - */ - static VersionNumber identifyOSXVersion(final String userAgent) { - VersionNumber version = VersionNumber.UNKNOWN; - final List patterns = new ArrayList(); - patterns.add(Pattern.compile("Mac OS X\\s?((\\d+)((\\.\\d+)+)?);")); - patterns.add(Pattern.compile("Mac OS X\\s?((\\d+)((\\_\\d+)+)?);")); - patterns.add(Pattern.compile("Mac OS X\\s?((\\d+)((\\_\\d+)+)?)\\)")); - for (final Pattern pattern : patterns) { - final Matcher m = pattern.matcher(userAgent); - if (m.find()) { - version = parseFirstVersionNumber(m.group(MAJOR_INDEX).replaceAll("_", ".")); - break; - } - } - return version; - } - - /** - * This method try to determine the version number of the operating system Symbian more accurately. - * - * @param userAgent - * user agent string - * @return more accurately identified version number or {@code null} - */ - static VersionNumber identifySymbianVersion(final String userAgent) { - VersionNumber version = VersionNumber.UNKNOWN; - final Pattern pattern = Pattern.compile("SymbianOS/((\\d+)((\\.\\d+)+)?s?)"); - final Matcher m = pattern.matcher(userAgent); - if (m.find()) { - version = parseFirstVersionNumber(m.group(MAJOR_INDEX)); - } - return version; - } - - /** - * This method try to determine the version number of the operating system webOS more accurately. - * - * @param userAgent - * user agent string - * @return more accurately identified version number or {@code null} - */ - static VersionNumber identifyWebOSVersion(final String userAgent) { - VersionNumber version = VersionNumber.UNKNOWN; - final List patterns = new ArrayList(); - patterns.add(Pattern.compile("hpwOS/((\\d+)((\\.\\d+)+)?);")); - patterns.add(Pattern.compile("webOS/((\\d+)((\\.\\d+)+)?);")); - for (final Pattern pattern : patterns) { - final Matcher m = pattern.matcher(userAgent); - if (m.find()) { - version = parseFirstVersionNumber(m.group(MAJOR_INDEX)); - break; - } - } - return version; - } - - /** - * This method try to determine the version number of the operating system Windows more accurately. - * - * @param userAgent - * user agent string - * @return more accurately identified version number or {@code null} - */ - static VersionNumber identifyWindowsVersion(final String userAgent) { - VersionNumber version = VersionNumber.UNKNOWN; - final List patterns = new ArrayList(); - patterns.add(Pattern.compile("Windows NT\\s?((\\d+)((\\.\\d+)+)?)")); - patterns.add(Pattern.compile("Windows Phone OS ((\\d+)((\\.\\d+)+)?)")); - patterns.add(Pattern.compile("Windows CE ((\\d+)((\\.\\d+)+)?)")); - patterns.add(Pattern.compile("Windows 2000\\s?((\\d+)((\\.\\d+)+)?)")); - patterns.add(Pattern.compile("Windows XP\\s?((\\d+)((\\.\\d+)+)?)")); - patterns.add(Pattern.compile("Windows 7\\s?((\\d+)((\\.\\d+)+)?)")); - patterns.add(Pattern.compile("Win 9x ((\\d+)((\\.\\d+)+)?)")); - patterns.add(Pattern.compile("Windows ((\\d+)((\\.\\d+)+)?)")); - patterns.add(Pattern.compile("WebTV/((\\d+)((\\.\\d+)+)?)")); - for (final Pattern pattern : patterns) { - final Matcher m = pattern.matcher(userAgent); - if (m.find()) { - version = parseFirstVersionNumber(m.group(MAJOR_INDEX)); - break; - } - } - return version; - } - - /** - * Interprets a string with version information. The first occurrence of a version number in the string will be - * searched and processed. - * - * @param text - * string with version information - * @return an object of {@code VersionNumber}, never {@code null} - */ - static VersionNumber parseFirstVersionNumber(@Nonnull final String text) { - Check.notNull(text, "text"); - - final Matcher matcher = VERSIONNUMBER_WITH_SUFFIX.matcher(text); - String[] split = null; - String ext = null; - if (matcher.find()) { - split = matcher.group(MAJOR_INDEX).split("\\."); - ext = matcher.group(EXTENSION_INDEX); - } - - final String extension = ext == null ? VersionNumber.EMPTY_EXTENSION : trimRight(ext); - - return split == null ? VersionNumber.UNKNOWN : new VersionNumber(Arrays.asList(split), extension); - } - - /** - * Interprets a string with version information. The last version number in the string will be searched and - * processed. - * - * @param text - * string with version information - * @return an object of {@code VersionNumber}, never {@code null} - */ - public static VersionNumber parseLastVersionNumber(@Nonnull final String text) { - Check.notNull(text, "text"); - - final Matcher matcher = VERSIONNUMBER_WITH_SUFFIX.matcher(text); - String[] split = null; - String ext = null; - while (matcher.find()) { - split = matcher.group(MAJOR_INDEX).split("\\."); - ext = matcher.group(EXTENSION_INDEX); - } - - final String extension = ext == null ? VersionNumber.EMPTY_EXTENSION : trimRight(ext); - - return split == null ? VersionNumber.UNKNOWN : new VersionNumber(Arrays.asList(split), extension); - } - - /** - * Try to determine the version number of the operating system by parsing the user agent string. - * - * - * @param family - * family of the operating system - * @param userAgent - * user agent string - * @return extracted version number - */ - public static VersionNumber parseOperatingSystemVersion(@Nonnull final OperatingSystemFamily family, @Nonnull final String userAgent) { - Check.notNull(family, "family"); - Check.notNull(userAgent, "userAgent"); - - final VersionNumber v; - if (OperatingSystemFamily.ANDROID == family) { - v = identifyAndroidVersion(userAgent); - } else if (OperatingSystemFamily.BADA == family) { - v = identifyBadaVersion(userAgent); - } else if (OperatingSystemFamily.BSD == family) { - v = identifyBSDVersion(userAgent); - } else if (OperatingSystemFamily.IOS == family) { - v = identifyIOSVersion(userAgent); - } else if (OperatingSystemFamily.JVM == family) { - v = identifyJavaVersion(userAgent); - } else if (OperatingSystemFamily.OS_X == family) { - v = identifyOSXVersion(userAgent); - } else if (OperatingSystemFamily.SYMBIAN == family) { - v = identifySymbianVersion(userAgent); - } else if (OperatingSystemFamily.WEBOS == family) { - v = identifyWebOSVersion(userAgent); - } else if (OperatingSystemFamily.WINDOWS == family) { - v = identifyWindowsVersion(userAgent); - } else { - v = VersionNumber.UNKNOWN; - } - return v; - } - - /** - * Interprets a string with version information. The first found group will be taken and processed. - * - * @param version - * version as string - * @return an object of {@code VersionNumber}, never {@code null} - */ - public static VersionNumber parseVersion(@Nonnull final String version) { - Check.notNull(version, "version"); - - VersionNumber result = new VersionNumber(new ArrayList(0), version); - final Matcher matcher = VERSIONSTRING.matcher(version); - if (matcher.find()) { - final List groups = Arrays.asList(matcher.group(MAJOR_INDEX).split("\\.")); - final String extension = matcher.group(EXTENSION_INDEX) == null ? VersionNumber.EMPTY_EXTENSION : trimRight(matcher - .group(EXTENSION_INDEX)); - result = new VersionNumber(groups, extension); - } - - return result; - } - - /** - * Trims the whitespace at the end of the given string. - * - * @param text - * string to trim - * @return trimmed string - */ - private static String trimRight(@Nonnull final String text) { - return text.replaceAll("\\s+$", ""); - } - - /** - * Attention: This class is not intended to create objects from it. - */ - private VersionParser() { - // This class is not intended to create objects from it. - } - -} diff --git a/app/src/main/java/net/sf/uadetector/datareader/DataReader.java b/app/src/main/java/net/sf/uadetector/datareader/DataReader.java deleted file mode 100644 index 2205a1d..0000000 --- a/app/src/main/java/net/sf/uadetector/datareader/DataReader.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datareader; - -import java.net.URL; -import java.nio.charset.Charset; - -import net.sf.uadetector.internal.data.Data; - -/** - * Interface for data readers that reads User-Agent informations for implementations of {@code UserAgentStringParser}. - * - * @author André Rouél - */ -public interface DataReader { - - /** - * Reads UAS data from the given {@code URL}. - * - *

- * The implementing class must ensure that no error leads to a termination of this operation. In case of an error - * this operation must return {@link Data#EMPTY} and all error information must be stored in a log. - * - * @param url - * the URL where the UAS data can be retrieved - * @param charset - * the character set in which the data should be read - * @return read in UAS data as new {@code Data} instance or {@link Data#EMPTY} - */ - Data read(final URL url, final Charset charset); - - /** - * Reads UAS data from the given {@code String}. - * - *

- * The implementing class must ensure that no error leads to a termination of this operation. In case of an error - * this operation must return {@link Data#EMPTY} and all error information must be stored in a log. - * - * @param data - * a string of UAS data - * @return read in UAS data as new {@code Data} instance or {@link Data#EMPTY} - */ - Data read(final String data); - -} diff --git a/app/src/main/java/net/sf/uadetector/datareader/XmlDataReader.java b/app/src/main/java/net/sf/uadetector/datareader/XmlDataReader.java deleted file mode 100644 index 058ecdd..0000000 --- a/app/src/main/java/net/sf/uadetector/datareader/XmlDataReader.java +++ /dev/null @@ -1,178 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datareader; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.nio.charset.Charset; - -import javax.annotation.Nonnull; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.exception.CanNotOpenStreamException; -import net.sf.uadetector.internal.data.Data; -import net.sf.uadetector.internal.data.DataBuilder; -import net.sf.uadetector.internal.data.XmlDataHandler; -import net.sf.uadetector.internal.util.Closeables; -import net.sf.uadetector.internal.util.UrlUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xml.sax.SAXException; - -/** - * Reader for the XML data for UASparser from http://user-agent-string.info.
- *
- * This reader is safe when used concurrently by multiple threads. - * - * @author André Rouél - */ -public final class XmlDataReader implements DataReader { - - protected static final class XmlParser { - - private static final String MSG_NOT_PARSED_AS_EXPECTED = "The UAS data has not been parsed as expected."; - - public static void parse(@Nonnull final InputStream stream, @Nonnull final DataBuilder builder) - throws ParserConfigurationException, SAXException, IOException { - final SAXParserFactory factory = SAXParserFactory.newInstance(); -// factory.setValidating(true); - final SAXParser parser = factory.newSAXParser(); - final XmlDataHandler handler = new XmlDataHandler(builder); - parser.parse(stream, handler); - validate(handler); - } - - protected static void validate(@Nonnull final XmlDataHandler handler) { - if (handler.hasError()) { - throw new IllegalStateException(MSG_NOT_PARSED_AS_EXPECTED); - } - } - - private XmlParser() { - // This class is not intended to create objects from it. - } - - } - - /** - * Default character set to read UAS data - */ - private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); - - /** - * Corresponding default logger for this class - */ - private static final Logger LOG = LoggerFactory.getLogger(XmlDataReader.class); - - /** - * Reads the UAS data in XML format based on the given URL.
- *
- * When during the reading errors occur which lead to a termination of the read operation, the information will be - * written to a log. The termination of the read operation will not lead to a program termination and in this case - * this method returns {@link Data#EMPTY}. - * - * @param inputStream - * an input stream for reading UAS data - * @param charset - * the character set in which the data should be read - * @return read in UAS data as {@code Data} instance - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if any of the given arguments is {@code null} - * @throws net.sf.uadetector.exception.CanNotOpenStreamException - * if no stream to the given {@code URL} can be established - */ - protected static Data readXml(@Nonnull final InputStream inputStream, @Nonnull final Charset charset) { - Check.notNull(inputStream, "inputStream"); - Check.notNull(charset, "charset"); - - final DataBuilder builder = new DataBuilder(); - boolean hasErrors = false; - try { - XmlParser.parse(inputStream, builder); - } catch (final ParserConfigurationException e) { - hasErrors = true; - LOG.warn(e.getLocalizedMessage()); - } catch (final SAXException e) { - hasErrors = true; - LOG.warn(e.getLocalizedMessage()); - } catch (final IOException e) { - hasErrors = true; - LOG.warn(e.getLocalizedMessage()); - } catch (final IllegalStateException e) { - hasErrors = true; - LOG.warn(e.getLocalizedMessage()); - } catch (final Exception e) { - hasErrors = true; - LOG.warn(e.getLocalizedMessage(), e); - } finally { - Closeables.closeAndConvert(inputStream, true); - } - - return hasErrors ? Data.EMPTY : builder.build(); - } - - /** - * Reads the UAS data in XML format from the given string. - * - * @param data - * UAS data as string - * @return read in User-Agent data as {@code Data} instance otherwise {@link Data#EMPTY} - * - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if any of the given argument is {@code null} - */ - @Override - public Data read(@Nonnull final String data) { - Check.notNull(data, "data"); - - return readXml(new ByteArrayInputStream(data.getBytes(DEFAULT_CHARSET)), DEFAULT_CHARSET); - } - - /** - * Reads the UAS data in XML format based on the given URL. - * - * @param url - * {@code URL} to User-Agent informations - * @param charset - * the character set in which the data should be read - * @return read in User-Agent data as {@code Data} instance otherwise {@link Data#EMPTY} - * - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if any of the given arguments is {@code null} - */ - @Override - public Data read(@Nonnull final URL url, @Nonnull final Charset charset) { - Check.notNull(url, "url"); - Check.notNull(charset, "charset"); - - Data data = Data.EMPTY; - try { - data = readXml(UrlUtil.open(url), charset); - } catch (final CanNotOpenStreamException e) { - LOG.warn(e.getLocalizedMessage()); - } - - return data; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/datareader/package-info.java b/app/src/main/java/net/sf/uadetector/datareader/package-info.java deleted file mode 100644 index 47a7b71..0000000 --- a/app/src/main/java/net/sf/uadetector/datareader/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -@javax.annotation.ParametersAreNonnullByDefault -package net.sf.uadetector.datareader; diff --git a/app/src/main/java/net/sf/uadetector/datastore/AbstractDataStore.java b/app/src/main/java/net/sf/uadetector/datastore/AbstractDataStore.java deleted file mode 100644 index b5075bc..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/AbstractDataStore.java +++ /dev/null @@ -1,196 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datastore; - -import java.net.URL; -import java.nio.charset.Charset; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.datareader.DataReader; -import net.sf.uadetector.internal.data.Data; -import net.sf.uadetector.internal.util.UrlUtil; - -/** - * The abstract implementation to store UAS data only in the heap space.
- *
- * A store must always have an usable instance of {@link Data}. It is recommended to initialize it with the supplied UAS - * file in the uadetector-resources module. - * - * @author André Rouél - */ -public abstract class AbstractDataStore implements DataStore { - - /** - * Runtime check that the passed instance of {@link Data} is not empty (respectively {@link Data#EMPTY}). - * - * @param data - * instance of {@code Data} - * @throws IllegalStateException - * if the passed instance is empty - */ - private static Data checkData(final Data data) { - if (Data.EMPTY.equals(data)) { - throw new IllegalStateException("Argument 'data' must not be empty."); - } - return data; - } - - /** - * This method reads the given {@link URL} by using an {@link DataReader}. The new created instance of {@link Data} - * will be returned. - * - * @param reader - * data reader to read the given {@code dataUrl} - * @param url - * URL to UAS data - * @param charset - * the character set in which the data should be read - * @return an instance of {@code Data} or {@link Data#EMPTY} if an error occurred, but never {@code null} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - protected static final Data readData(@Nonnull final DataReader reader, @Nonnull final URL url, @Nonnull final Charset charset) { - Check.notNull(reader, "reader"); - Check.notNull(url, "url"); - Check.notNull(charset, "charset"); - - return reader.read(url, charset); - } - - /** - * Current the character set in which the UAS data will be read - */ - private final Charset charset; - - /** - * Current UAS data - */ - private final Data data; - - /** - * The {@code URL} to get UAS data - */ - private final URL dataUrl; - - /** - * The data reader to read in UAS data - */ - private final DataReader reader; - - /** - * The {@code URL} to get the latest version information of UAS data - */ - private final URL versionUrl; - - /** - * Constructs an new instance of {@link AbstractDataStore}. - * - * @param data - * first UAS data which will be available in the store - * @param reader - * data reader to read the given {@code dataUrl} - * @param dataUrl - * URL to UAS data - * @param versionUrl - * URL to version information about the given UAS data - * @param charset - * the character set in which the data should be read - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - */ - protected AbstractDataStore(@Nonnull final Data data, @Nonnull final DataReader reader, @Nonnull final URL dataUrl, - @Nonnull final URL versionUrl, @Nonnull final Charset charset) { - Check.notNull(data, "data"); - Check.notNull(reader, "reader"); - Check.notNull(charset, "charset"); - Check.notNull(dataUrl, "dataUrl"); - Check.notNull(versionUrl, "versionUrl"); - - this.data = checkData(data); - this.reader = reader; - this.dataUrl = dataUrl; - this.versionUrl = versionUrl; - this.charset = charset; - } - - /** - * Constructs an {@code AbstractDataStore} by reading the given {@code dataUrl} as UAS data. - * - * @param reader - * data reader to read the given {@code dataUrl} - * @param dataUrl - * URL to UAS data - * @param versionUrl - * URL to version information about the given UAS data - * @param charset - * the character set in which the data should be read - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given strings are not valid URLs - */ - protected AbstractDataStore(final DataReader reader, final String dataUrl, final String versionUrl, final Charset charset) { - this(reader, UrlUtil.build(dataUrl), UrlUtil.build(versionUrl), charset); - } - - /** - * Constructs an {@code AbstractDataStore} by reading the given {@code dataUrl} as UAS data. - * - * @param reader - * data reader to read the given {@code dataUrl} - * @param dataUrl - * URL to UAS data - * @param versionUrl - * URL to version information about the given UAS data - * @param charset - * the character set in which the data should be read - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the created instance of {@link Data} is empty - */ - protected AbstractDataStore(final DataReader reader, final URL dataUrl, final URL versionUrl, final Charset charset) { - this(checkData(readData(reader, dataUrl, charset)), reader, dataUrl, versionUrl, charset); - } - - @Override - public Charset getCharset() { - return charset; - } - - @Override - public Data getData() { - return data; - } - - @Override - public DataReader getDataReader() { - return reader; - } - - @Override - public URL getDataUrl() { - return dataUrl; - } - - @Override - public URL getVersionUrl() { - return versionUrl; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/datastore/AbstractRefreshableDataStore.java b/app/src/main/java/net/sf/uadetector/datastore/AbstractRefreshableDataStore.java deleted file mode 100644 index 7330f8f..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/AbstractRefreshableDataStore.java +++ /dev/null @@ -1,250 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datastore; - -import java.net.URL; -import java.nio.charset.Charset; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; -import net.sf.qualitycheck.exception.IllegalStateOfArgumentException; -import net.sf.uadetector.datareader.DataReader; -import net.sf.uadetector.exception.CanNotOpenStreamException; -import net.sf.uadetector.internal.data.Data; -import net.sf.uadetector.internal.util.UrlUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This abstract implementation provides basic update functionality to be able to update the UAS data in your - * data store. During initialization the passed in fallback data store will be used and an update process will be - * triggered. The update operation itself works as background task to avoid blocking (for example when reading data by a - * network connection and the remote host is not available or really slow). - *

- * Such a store must always have an usable instance of {@link Data} and should be initialized with the supplied UAS file - * in the uadetector-resources module. - * - * @author André Rouél - */ -public abstract class AbstractRefreshableDataStore implements RefreshableDataStore { - - /** - * Corresponding default logger for this class - */ - private static final Logger LOG = LoggerFactory.getLogger(AbstractRefreshableDataStore.class); - - /** - * Runtime check that the passed instance of {@link Data} is not empty (respectively {@link Data#EMPTY}). - * - * @param data - * instance of {@code Data} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the passed instance is empty - */ - private static Data checkData(final Data data) { - if (Data.EMPTY.equals(data)) { - throw new IllegalStateOfArgumentException("Argument 'data' must not be empty."); - } - return data; - } - - /** - * Current the character set in which the UAS data will be read - */ - private final Charset charset; - - /** - * Current UAS data - */ - private Data data; - - /** - * The {@code URL} to get UAS data - */ - private final URL dataUrl; - - /** - * Default data store which will be used during initialization of a {@code DataStore} and can be used in emergency - * cases. - */ - private final DataStore fallback; - - /** - * The data reader to read in UAS data - */ - private final DataReader reader; - - /** - * Update operation which runs itself in background (non-blocking) - */ - private UpdateOperation updateOperation = new UpdateOperationTask(this); - - /** - * The {@code URL} to get the latest version information of UAS data - */ - private final URL versionUrl; - - /** - * Constructs an {@code AbstractDataStore} by reading the given {@code dataUrl} as UAS data. - * - * @param reader - * data reader to read the given {@code dataUrl} - * @param dataUrl - * URL to UAS data - * @param versionUrl - * URL to version information about the given UAS data - * @param charset - * the character set in which the data should be read - * @param fallback - * UAS data as fallback in case the data on the specified resource can not be read correctly - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the given strings are not valid URLs - */ - protected AbstractRefreshableDataStore(@Nonnull final DataReader reader, @Nonnull final String dataUrl, - @Nonnull final String versionUrl, @Nonnull final Charset charset, @Nonnull final DataStore fallback) { - this(reader, UrlUtil.build(dataUrl), UrlUtil.build(versionUrl), charset, fallback); - } - - /** - * Constructs an {@code AbstractDataStore} by reading the given {@code dataUrl} as UAS data. - * - * @param reader - * data reader to read the given {@code dataUrl} - * @param dataUrl - * URL to UAS data - * @param versionUrl - * URL to version information about the given UAS data - * @param charset - * the character set in which the data should be read - * @param fallback - * UAS data as fallback in case the data on the specified resource can not be read correctly - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the created instance of {@link Data} is empty - */ - protected AbstractRefreshableDataStore(@Nonnull final DataReader reader, @Nonnull final URL dataUrl, @Nonnull final URL versionUrl, - @Nonnull final Charset charset, final DataStore fallback) { - Check.notNull(reader, "reader"); - Check.notNull(charset, "charset"); - Check.notNull(dataUrl, "dataUrl"); - Check.notNull(versionUrl, "versionUrl"); - Check.notNull(fallback, "fallback"); - - this.reader = reader; - this.dataUrl = dataUrl; - this.versionUrl = versionUrl; - this.charset = charset; - this.fallback = fallback; - - data = checkData(fallback.getData()); - } - - @Override - public Charset getCharset() { - return charset; - } - - @Override - public Data getData() { - return data; - } - - @Override - public DataReader getDataReader() { - return reader; - } - - @Override - public URL getDataUrl() { - return dataUrl; - } - - @Override - public DataStore getFallback() { - return fallback; - } - - @Override - public UpdateOperation getUpdateOperation() { - return updateOperation; - } - - @Override - public URL getVersionUrl() { - return versionUrl; - } - - /** - * Triggers the update of the DataStore. When this action is executed, the current data URL will be - * read in and the DataReader parses and builds a new Data instance. Finally, the currently set - * Data reference will be replaced by the new one. - *

- * Attention: This method is implemented as background task. You can not assume that you immediately get an - * updated data store. - */ - @Override - public void refresh() { - try { - updateOperation.run(); - } catch (final CanNotOpenStreamException e) { - LOG.warn(String.format(MSG_URL_NOT_READABLE, e.getLocalizedMessage())); - } catch (final IllegalArgumentException e) { - LOG.warn(MSG_FAULTY_CONTENT + " " + e.getLocalizedMessage()); - } catch (final RuntimeException e) { - LOG.warn(MSG_FAULTY_CONTENT, e); - } - } - - /** - * Sets new UAS data in the store. - * - * @param data - * UAS data to override the current ({@code null} is not allowed) - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the given instance of {@code Data} is empty - */ - protected void setData(@Nonnull final Data data) { - Check.notNull(data, "data"); - - this.data = checkData(data); - - // add some useful UAS data informations to the log - if (LOG.isDebugEnabled()) { - LOG.debug(data.toStats()); - } - } - - /** - * Sets a new update operation. - * - * @param updateOperation - * operation to override the current ({@code null} is not allowed) - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - protected void setUpdateOperation(@Nonnull final UpdateOperation updateOperation) { - Check.notNull(updateOperation, "updateOperation"); - this.updateOperation = updateOperation; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/datastore/AbstractUpdateOperation.java b/app/src/main/java/net/sf/uadetector/datastore/AbstractUpdateOperation.java deleted file mode 100644 index 26d3b52..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/AbstractUpdateOperation.java +++ /dev/null @@ -1,217 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datastore; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.LineNumberReader; -import java.net.URL; -import java.nio.charset.Charset; -import java.util.concurrent.ExecutorService; -import java.util.regex.Pattern; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.internal.util.ExecutorServices; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Provides a basic implementation to update UAS data in the background when calling {@link #run()}. - * - * @author André Rouél - */ -public abstract class AbstractUpdateOperation implements UpdateOperation { - - /** - * Defines an empty version string - */ - private static final String EMPTY_VERSION = ""; - - /** - * Corresponding default logger for this class - */ - private static final Logger LOG = LoggerFactory.getLogger(AbstractUpdateOperation.class); - - /** - * Message for the log when no update is available.
- *
- * Message sample: No update available. Current version is '20120301-01'.
- * First placeholder: current version - */ - private static final String MSG_NO_UPDATE_AVAILABLE = "No update available. Current version is '%s'."; - - /** - * Message for the log when an online update check is not possible.
- *
- * Message sample: Can not check for an updated version. Are you sure you have an established internet - * connection? - */ - private static final String MSG_NO_UPDATE_CHECK_POSSIBLE = "Can not check for an updated version. Are you sure you have an established internet connection?"; - - /** - * Message for the log when an exception occur during the update check.
- *
- * Message sample: Can not check for an updated version: java.net.ConnectException: - * Connection refused
- * First placeholder: class name of exception
- * Second placeholder: exception message - */ - private static final String MSG_NO_UPDATE_CHECK_POSSIBLE__DEBUG = "Can not check for an updated version: %s: %s"; - - /** - * Message for the log when an update is available.
- *
- * Message sample: An update is available. Current version is '20120301-01' and remote version is ' - * 20120401-01'.
- * First placeholder: current version
- * Second placeholder: new remote version - */ - private static final String MSG_UPDATE_AVAILABLE = "An update is available. Current version is '%s' and remote version is '%s'."; - - /** - * Pattern of a typical version of UAS data - */ - private static final Pattern VERSION_PATTERN = Pattern.compile("\\d{8}\\-\\d{2}"); - - /** - * Checks a given newer version against an older one. - * - * @param newer - * possible newer version - * @param older - * possible older version - * @return {@code true} if the first argument is newer than the second argument, otherwise {@code false} - */ - static boolean hasUpdate(final String newer, final String older) { - return VERSION_PATTERN.matcher(newer).matches() && VERSION_PATTERN.matcher(older).matches() ? newer.compareTo(older) > 0 : false; - } - - /** - * Reads the current User-Agent data version from http://data.udger.com. - * - * @param url - * a URL which the version information can be loaded - * @return a version string or {@code null} - * @throws IOException - * if an I/O exception occurs - */ - @Nullable - private static String retrieveRemoteVersion(@Nonnull final URL url, @Nonnull final Charset charset) throws IOException { - final InputStream stream = url.openStream(); - final InputStreamReader reader = new InputStreamReader(stream, charset); - final LineNumberReader lnr = new LineNumberReader(reader); - final String line = lnr.readLine(); - lnr.close(); - reader.close(); - stream.close(); - return line; - } - - /** - * {@link ExecutorService} to run the update operation of the UAS data in background - */ - private final ExecutorService executorService = ExecutorServices.createBackgroundExecutor(); - - /** - * Time of last update check in milliseconds - */ - private long lastUpdateCheck = 0; - - /** - * The data store for instances that implements {@link net.sf.uadetector.internal.data.Data} - */ - private final RefreshableDataStore store; - - public AbstractUpdateOperation(@Nonnull final RefreshableDataStore dataStore) { - Check.notNull(dataStore, "dataStore"); - store = dataStore; - } - - /** - * Shortcut to get the current version of the UAS data in the {@link net.sf.uadetector.datastore.DataStore}. - * - * @return current version of UAS data - */ - @Nonnull - private String getCurrentVersion() { - return store.getData().getVersion(); - } - - /** - * Gets the time of the last update check in milliseconds. - * - * @return time of the last update check in milliseconds - */ - @Override - public long getLastUpdateCheck() { - return lastUpdateCheck; - } - - /** - * Fetches the current version information over HTTP and compares it with the last version of the most recently - * imported data. - * - * @return {@code true} if an update exists, otherwise {@code false} - */ - protected boolean isUpdateAvailable() { - boolean result = false; - String version = EMPTY_VERSION; - try { - version = retrieveRemoteVersion(store.getVersionUrl(), store.getCharset()); - } catch (final IOException e) { - LOG.info(MSG_NO_UPDATE_CHECK_POSSIBLE); - LOG.debug(String.format(MSG_NO_UPDATE_CHECK_POSSIBLE__DEBUG, e.getClass().getName(), e.getLocalizedMessage())); - } - if (hasUpdate(version, getCurrentVersion())) { - LOG.debug(String.format(MSG_UPDATE_AVAILABLE, getCurrentVersion(), version)); - result = true; - } else { - LOG.debug(String.format(MSG_NO_UPDATE_AVAILABLE, getCurrentVersion())); - } - lastUpdateCheck = System.currentTimeMillis(); - return result; - } - - /** - * Executes the update at some time in the future (as soon as possible) within a new thread. - */ - @Override - public void run() { - executorService.execute(new Runnable() { - @Override - public void run() { - call(); - } - }); - } - - /** - * Shuts down the corresponding background executor as soon as possible, but at the latest specified default time. - * - * @see ExecutorServices#shutdown(ExecutorService) - */ - @Override - public void shutdown() { - ExecutorServices.shutdown(executorService); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/datastore/CachingXmlDataStore.java b/app/src/main/java/net/sf/uadetector/datastore/CachingXmlDataStore.java deleted file mode 100644 index 1bb1f40..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/CachingXmlDataStore.java +++ /dev/null @@ -1,329 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datastore; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.Charset; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; -import net.sf.qualitycheck.exception.IllegalStateOfArgumentException; -import net.sf.uadetector.datareader.DataReader; -import net.sf.uadetector.datareader.XmlDataReader; -import net.sf.uadetector.internal.data.Data; -import net.sf.uadetector.internal.util.FileUtil; -import net.sf.uadetector.internal.util.UrlUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Implementation of a {@link DataStore} which is able to recover UAS data in XML format from a cache file. If - * the cache file is empty, the data will be read from the given data URL.
- *
- * You can also update the data of the store at any time if you trigger {@link CachingXmlDataStore#refresh()}. - * - * @author André Rouél - */ -public final class CachingXmlDataStore extends AbstractRefreshableDataStore { - - /** - * Internal data store which will be used to load previously saved UAS data from a cache file. - */ - private static class CacheFileDataStore extends AbstractDataStore { - protected CacheFileDataStore(final Data data, final DataReader reader, final URL dataUrl, final Charset charset) { - super(data, reader, dataUrl, dataUrl, charset); - } - } - - /** - * The default temporary-file directory - */ - private static final String CACHE_DIR = System.getProperty("java.io.tmpdir"); - - /** - * Corresponding default logger of this class - */ - private static final Logger LOG = LoggerFactory.getLogger(CachingXmlDataStore.class); - - /** - * Message for the log if the cache file is filled - */ - private static final String MSG_CACHE_FILE_IS_EMPTY = "The cache file is empty. The given UAS data source will be imported."; - - /** - * Message for the log if the cache file is empty - */ - private static final String MSG_CACHE_FILE_IS_FILLED = "The cache file is filled and will be imported."; - - /** - * Message if the cache file contains unexpected data and must be deleted manually - */ - private static final String MSG_CACHE_FILE_IS_DAMAGED = "The cache file '%s' is damaged and must be removed manually."; - - /** - * Message if the cache file contains unexpected data and has been removed - */ - private static final String MSG_CACHE_FILE_IS_DAMAGED_AND_DELETED = "The cache file '%s' is damaged and has been deleted."; - - /** - * The prefix string to be used in generating the cache file's name; must be at least three characters long - */ - private static final String PREFIX = "uas"; - - /** - * The suffix string to be used in generating the cache file's name; may be {@code null}, in which case the suffix " - * {@code .tmp}" will be used - */ - private static final String SUFFIX = ".xml"; - - /** - * Constructs a new instance of {@code CachingXmlDataStore} with the given arguments. The given {@code cacheFile} - * can be empty or filled with previously cached data in XML format. The file must be writable otherwise an - * exception will be thrown. - * - * @param dataUrl - * URL for online version of UAS data - * @param versionURL - * URL for version information of online UAS data - * @param fallback - * UAS data as fallback in case the data on the specified resource can not be read correctly - * @return new instance of {@link CachingXmlDataStore} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the given cache file can not be read - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if no URL can be resolved to the given given file - */ - @Nonnull - public static CachingXmlDataStore createCachingXmlDataStore(@Nonnull final URL dataUrl, @Nonnull final URL versionURL, @Nonnull final DataStore fallback) { - return createCachingXmlDataStore(findOrCreateCacheFile(), dataUrl, versionURL, DEFAULT_CHARSET, - fallback); - } - - @Deprecated - public static CachingXmlDataStore createCachingXmlDataStore(@Nonnull final DataStore fallback) { - return createCachingXmlDataStore(findOrCreateCacheFile(), fallback); - } - - /** - * Constructs a new instance of {@code CachingXmlDataStore} with the given arguments. The given {@code cacheFile} - * can be empty or filled with previously cached data in XML format. The file must be writable otherwise an - * exception will be thrown. - * - * @param cacheFile - * file with cached UAS data in XML format or empty file - * @param fallback - * UAS data as fallback in case the data on the specified resource can not be read correctly - * @return new instance of {@link CachingXmlDataStore} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the given cache file can not be read - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if no URL can be resolved to the given given file - */ - @Nonnull - @Deprecated - public static CachingXmlDataStore createCachingXmlDataStore(@Nonnull final File cacheFile, @Nonnull final DataStore fallback) { - return createCachingXmlDataStore(cacheFile, UrlUtil.build(DEFAULT_DATA_URL), UrlUtil.build(DEFAULT_VERSION_URL), DEFAULT_CHARSET, - fallback); - } - - /** - * Constructs a new instance of {@code CachingXmlDataStore} with the given arguments. The given {@code cacheFile} - * can be empty or filled with previously cached data in XML format. The file must be writable otherwise an - * exception will be thrown. - * - * @param cacheFile - * file with cached UAS data in XML format or empty file - * @param dataUrl - * URL to UAS data - * @param versionUrl - * URL to version information about the given UAS data - * @param charset - * the character set in which the data should be read - * @param fallback - * UAS data as fallback in case the data on the specified resource can not be read correctly - * @return new instance of {@link CachingXmlDataStore} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given cache file can not be read - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if no URL can be resolved to the given given file - */ - @Nonnull - public static CachingXmlDataStore createCachingXmlDataStore(@Nonnull final File cacheFile, @Nonnull final URL dataUrl, - @Nonnull final URL versionUrl, @Nonnull final Charset charset, @Nonnull final DataStore fallback) { - Check.notNull(cacheFile, "cacheFile"); - Check.notNull(charset, "charset"); - Check.notNull(dataUrl, "dataUrl"); - Check.notNull(fallback, "fallback"); - Check.notNull(versionUrl, "versionUrl"); - - final DataReader reader = new XmlDataReader(); - final DataStore fallbackDataStore = readCacheFileAsFallback(reader, cacheFile, charset, fallback); - return new CachingXmlDataStore(reader, dataUrl, versionUrl, charset, cacheFile, fallbackDataStore); - } - - /** - * Constructs a new instance of {@code CachingXmlDataStore} with the given arguments. The file used to cache the - * read in UAS data will be called from {@link CachingXmlDataStore#findOrCreateCacheFile()}. This file may - * be empty or filled with previously cached data in XML format. The file must be writable otherwise an exception - * will be thrown. - * - * @param dataUrl - * URL to UAS data - * @param versionUrl - * URL to version information about the given UAS data - * @param charset - * the character set in which the data should be read - * @param fallback - * UAS data as fallback in case the data on the specified resource can not be read correctly - * @return new instance of {@link CachingXmlDataStore} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the given cache file can not be read - */ - @Nonnull - public static CachingXmlDataStore createCachingXmlDataStore(@Nonnull final URL dataUrl, @Nonnull final URL versionUrl, - @Nonnull final Charset charset, @Nonnull final DataStore fallback) { - return createCachingXmlDataStore(findOrCreateCacheFile(), dataUrl, versionUrl, charset, fallback); - } - - /** - * Removes the given cache file because it contains damaged content. - * - * @param cacheFile - * cache file to delete - */ - private static void deleteCacheFile(final File cacheFile) { - try { - if (cacheFile.delete()) { - LOG.warn(String.format(MSG_CACHE_FILE_IS_DAMAGED_AND_DELETED, cacheFile.getPath())); - } else { - LOG.warn(String.format(MSG_CACHE_FILE_IS_DAMAGED, cacheFile.getPath())); - } - } catch (final Exception e) { - LOG.warn(String.format(MSG_CACHE_FILE_IS_DAMAGED, cacheFile.getPath())); - } - } - - /** - * Gets the cache file for UAS data in the default temporary-file directory. If no cache file exists, a new - * empty file in the default temporary-file directory will be created, using the default prefix and suffix to - * generate its name. - * - * @return file to cache read in UAS data - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the cache file can not be created - */ - @Nonnull - public static File findOrCreateCacheFile() { - final File file = new File(CACHE_DIR, PREFIX + SUFFIX); - if (!file.exists()) { - try { - file.createNewFile(); - } catch (final IOException e) { - throw new IllegalStateOfArgumentException("Can not create a cache file.", e); - } - } - return file; - } - - /** - * Checks if the given file is empty. - * - * @param file - * the file that could be empty - * @return {@code true} when the file is accessible and empty otherwise {@code false} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if an I/O error occurs - */ - private static boolean isEmpty(@Nonnull final File file, @Nonnull final Charset charset) { - try { - return FileUtil.isEmpty(file, charset); - } catch (final IOException e) { - throw new IllegalStateOfArgumentException("The given file could not be read.", e); - } - } - - /** - * Tries to read the content of specified cache file and returns them as fallback data store. If the cache file - * contains unexpected data the given fallback data store will be returned instead. - * - * @param reader - * data reader to read the given {@code dataUrl} - * @param cacheFile - * file with cached UAS data in XML format or empty file - * @param versionUrl - * URL to version information about the given UAS data - * @param charset - * the character set in which the data should be read - * @param fallback - * UAS data as fallback in case the data on the specified resource can not be read correctly - * @return a fallback data store - */ - private static DataStore readCacheFileAsFallback(@Nonnull final DataReader reader, @Nonnull final File cacheFile, - @Nonnull final Charset charset, @Nonnull final DataStore fallback) { - DataStore fallbackDataStore; - if (!isEmpty(cacheFile, charset)) { - final URL cacheFileUrl = UrlUtil.toUrl(cacheFile); - try { - fallbackDataStore = new CacheFileDataStore(reader.read(cacheFileUrl, charset), reader, cacheFileUrl, charset); - LOG.debug(MSG_CACHE_FILE_IS_FILLED); - } catch (final RuntimeException e) { - fallbackDataStore = fallback; - deleteCacheFile(cacheFile); - } - } else { - fallbackDataStore = fallback; - LOG.debug(MSG_CACHE_FILE_IS_EMPTY); - } - return fallbackDataStore; - } - - /** - * Constructs an {@code CachingXmlDataStore} with the given arguments. - * - * @param data - * first UAS data which will be available in the store - * @param reader - * data reader to read the given {@code dataUrl} - * @param dataUrl - * URL to UAS data - * @param versionUrl - * URL to version information about the given UAS data - * @param charset - * the character set in which the data should be read - * @param cacheFile - * file with cached UAS data in XML format or an empty file - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - */ - private CachingXmlDataStore(@Nonnull final DataReader reader, @Nonnull final URL dataUrl, @Nonnull final URL versionUrl, - @Nonnull final Charset charset, @Nonnull final File cacheFile, @Nonnull final DataStore fallback) { - super(reader, dataUrl, versionUrl, charset, fallback); - setUpdateOperation(new UpdateOperationWithCacheFileTask(this, cacheFile)); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/datastore/DataStore.java b/app/src/main/java/net/sf/uadetector/datastore/DataStore.java deleted file mode 100644 index 169a36d..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/DataStore.java +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datastore; - -import java.net.URL; -import java.nio.charset.Charset; - -import javax.annotation.Nonnull; - -import net.sf.uadetector.datareader.DataReader; -import net.sf.uadetector.internal.data.Data; - -/** - * Defines an interface to store UAS data where ever you want. - * - * @author André Rouél - */ -public interface DataStore { - - /** - * Default character set to read UAS data - */ - Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); - - /** - * URL to retrieve the current UAS data as XML - */ - @Deprecated - String DEFAULT_DATA_URL = "http://user-agent-string.info/rpc/get_data.php?key=free&format=xml"; - - /** - * URL to retrieve the current version of the UAS data - */ - @Deprecated - String DEFAULT_VERSION_URL = "http://user-agent-string.info/rpc/get_data.php?key=free&format=ini&ver=y"; - - /** - * Gets the character set in which the UAS data will be read. - * - * @return current UAS data - */ - @Nonnull - Charset getCharset(); - - /** - * Gets the UAS data which are currently set. - * - * @return current UAS data - */ - @Nonnull - Data getData(); - - /** - * Gets the data reader to read in UAS data. - * - * @return the data reader to read in UAS data - */ - @Nonnull - DataReader getDataReader(); - - /** - * Gets the URL from which the UAS data can be read. - * - * @return URL to UAS data - */ - @Nonnull - URL getDataUrl(); - - /** - * Gets the URL from which version information about the UAS data can be read. - * - * @return URL to version information of UAS data - */ - @Nonnull - URL getVersionUrl(); - -} diff --git a/app/src/main/java/net/sf/uadetector/datastore/OnlineXmlDataStore.java b/app/src/main/java/net/sf/uadetector/datastore/OnlineXmlDataStore.java deleted file mode 100644 index 181996d..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/OnlineXmlDataStore.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datastore; - -import java.net.URL; - -import net.sf.uadetector.datareader.DataReader; -import net.sf.uadetector.datareader.XmlDataReader; - -/** - * This is the simplest implementation of a {@link RefreshableDataStore}. It initialize the store by reading the - * UAS data online via {@link DataStore#DEFAULT_DATA_URL} and store it only in the Java heap space. - *

- * Attentation: During initialization the fallback data store will be used and the remote data will be read in - * background (non-blocking). - * - * @author André Rouél - */ -public final class OnlineXmlDataStore extends AbstractRefreshableDataStore { - - /** - * The default data reader to read in UAS data in XML format - */ - private static final DataReader DEFAULT_DATA_READER = new XmlDataReader(); - - /** - * Constructs an {@code OnlineXmlDataStore} by reading UAS data by the specified default URL - * {@link DataStore#DEFAULT_DATA_URL} (in XML format). - * - * @param fallback - * UAS data as fallback in case the data on the specified resource can not be read correctly - */ - @Deprecated - public OnlineXmlDataStore(final DataStore fallback) { - super(DEFAULT_DATA_READER, DEFAULT_DATA_URL, DEFAULT_VERSION_URL, DEFAULT_CHARSET, fallback); - } - - /** - * Constructs an {@code OnlineXmlDataStore} by reading UAS data by the specified URL (in XML format). - * @param dataurl - * @param versionUrl - * @param fallback - */ - public OnlineXmlDataStore(final URL dataurl, final URL versionUrl, final DataStore fallback) { - super(DEFAULT_DATA_READER, dataurl, versionUrl, DEFAULT_CHARSET, fallback); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/datastore/RefreshableDataStore.java b/app/src/main/java/net/sf/uadetector/datastore/RefreshableDataStore.java deleted file mode 100644 index 68bf001..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/RefreshableDataStore.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datastore; - -import javax.annotation.Nonnull; - -/** - * Extends the interface with an update functionality for UAS data in the store. - * - * @author André Rouél - */ -public interface RefreshableDataStore extends DataStore { - - /** - * Message for the log if the read content can not be processed correctly - */ - String MSG_FAULTY_CONTENT = "The read content is faulty and can not be processed correctly."; - - /** - * Message for the log if the UAS data can not be read from the given URL - */ - String MSG_URL_NOT_READABLE = "The data can not be read from the specified URL: %s"; - - /** - * This method returns a data store which will be used during start up and can be used in emergency cases. - *

- * This data store will be used instantly during initialization to avoid long initializations times of an - * {@link net.sf.uadetector.UserAgentStringParser}, especially when reading data by a network connection. - */ - @Nonnull - DataStore getFallback(); - - /** - * Returns the update operation of this data store which can be triggered within an executor service. - * - * @return an update operation - */ - @Nonnull - UpdateOperation getUpdateOperation(); - - /** - * Triggers the update of the {@code DataStore}. When this action is executed, the current data URL will be read in - * and the {@code DataReader} parses and builds a new {@code Data} instance. Finally, the currently set {@code Data} - * reference will be replaced by the new one. - */ - void refresh(); - -} diff --git a/app/src/main/java/net/sf/uadetector/datastore/SimpleXmlDataStore.java b/app/src/main/java/net/sf/uadetector/datastore/SimpleXmlDataStore.java deleted file mode 100644 index 8b2deb2..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/SimpleXmlDataStore.java +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datastore; - -import java.net.URL; - -import net.sf.uadetector.datareader.DataReader; -import net.sf.uadetector.datareader.XmlDataReader; - -/** - * This is the simplest implementation of a {@link DataStore}. It initialize the store by reading the UAS data - * via the passed URL and store it only in the Java heap space. - * - *

- * The given resource must have a valid XML format with UTF-8 charset that validates against specified schema under http://data.udger.com/uasxmldata_old.dtd. - * - * @author André Rouél - */ -public final class SimpleXmlDataStore extends AbstractDataStore implements DataStore { - - /** - * The default data reader to read in UAS data in XML format - */ - private static final DataReader DEFAULT_DATA_READER = new XmlDataReader(); - - /** - * Constructs an {@code SimpleXmlDataStore} by reading UAS data by the specified default URL - * {@link DataStore#DEFAULT_DATA_URL} (in XML format). - * - * @param dataUrl - * URL to UAS data - * @param versionUrl - * URL to version information about the given UAS data - */ - public SimpleXmlDataStore(final URL dataUrl, final URL versionUrl) { - super(DEFAULT_DATA_READER, dataUrl, versionUrl, DEFAULT_CHARSET); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/datastore/UpdateOperation.java b/app/src/main/java/net/sf/uadetector/datastore/UpdateOperation.java deleted file mode 100644 index ee65cf5..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/UpdateOperation.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datastore; - -import javax.annotation.Nonnegative; - -/** - * Defines an update operation which will be executed within a executor service in background. - * - * @author André Rouél - */ -public interface UpdateOperation extends Runnable { - - /** - * The default interval to check for updates is once per day - */ - @Nonnegative - long DEFAULT_UPDATE_INTERVAL = 1000 * 60 * 60 * 24; - - /** - * This function checks whether updated UAS data are available and updates silently the data in a - * {@link net.sf.uadetector.datastore.DataStore}. - */ - void call(); - - /** - * Gets the time of the last update check in milliseconds. - * - * @return time of the last update check in milliseconds - */ - @Nonnegative - long getLastUpdateCheck(); - - /** - * Shuts down the corresponding background executor. - */ - void shutdown(); - -} diff --git a/app/src/main/java/net/sf/uadetector/datastore/UpdateOperationTask.java b/app/src/main/java/net/sf/uadetector/datastore/UpdateOperationTask.java deleted file mode 100644 index 1cd6d7d..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/UpdateOperationTask.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datastore; - -import net.sf.uadetector.internal.data.Data; - -final class UpdateOperationTask extends AbstractUpdateOperation { - - /** - * The data store for instances that implements {@link net.sf.uadetector.internal.data.Data} - */ - private final AbstractRefreshableDataStore store; - - public UpdateOperationTask(final AbstractRefreshableDataStore dataStore) { - super(dataStore); - store = dataStore; - } - - @Override - public void call() { - if (isUpdateAvailable()) { - final Data data = store.getDataReader().read(store.getDataUrl(), store.getCharset()); - store.setData(data); - } - } - -} diff --git a/app/src/main/java/net/sf/uadetector/datastore/UpdateOperationWithCacheFileTask.java b/app/src/main/java/net/sf/uadetector/datastore/UpdateOperationWithCacheFileTask.java deleted file mode 100644 index 07b0f84..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/UpdateOperationWithCacheFileTask.java +++ /dev/null @@ -1,255 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.datastore; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.URL; -import java.nio.charset.Charset; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.exception.CanNotOpenStreamException; -import net.sf.uadetector.internal.data.Data; -import net.sf.uadetector.internal.util.Closeables; -import net.sf.uadetector.internal.util.FileUtil; -import net.sf.uadetector.internal.util.UrlUtil; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -final class UpdateOperationWithCacheFileTask extends AbstractUpdateOperation { - - /** - * Corresponding default logger of this class - */ - private static final Logger LOG = LoggerFactory.getLogger(UpdateOperationWithCacheFileTask.class); - - /** - * Message for the log when issues occur during reading of or writing to the cache file. - */ - private static final String MSG_CACHE_FILE_ISSUES = "Issues occured during reading of or writing to the cache file: %s"; - - /** - * Message for the log if the passed resources are the same and an update makes no sense - */ - private static final String MSG_SAME_RESOURCES = "The passed URL and file resources are the same. An update was not performed."; - - /** - * Creates a temporary file near the passed file. The name of the given one will be used and the suffix ".temp" will - * be added. - * - * @param file - * file in which the entire contents from the given URL can be saved - * @throws IllegalStateException - * if the file can not be deleted - */ - protected static File createTemporaryFile(@Nonnull final File file) { - Check.notNull(file, "file"); - - final File tempFile = new File(file.getParent(), file.getName() + ".temp"); - - // remove orphaned temporary file - deleteFile(tempFile); - - return tempFile; - } - - /** - * Removes the given file. - * - * @param file - * a file which should be deleted - * - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the file can not be deleted - */ - protected static void deleteFile(@Nonnull final File file) { - Check.notNull(file, "file"); - Check.stateIsTrue(!file.exists() || file.delete(), "Cannot delete file '%s'.", file.getPath()); - } - - /** - * Checks if the given file is empty. - * - * @param file - * the file that could be empty - * @return {@code true} when the file is accessible and empty otherwise {@code false} - * @throws IllegalStateException - * if an I/O error occurs - */ - private static boolean isEmpty(@Nonnull final File file, @Nonnull final Charset charset) { - try { - return FileUtil.isEmpty(file, charset); - } catch (final IOException e) { - throw new IllegalStateException("The given file could not be read."); - } - } - - /** - * Checks that {@code older} {@link Data} has a lower version number than the {@code newer} one. - * - * @param older - * possibly older {@code Data} - * @param newer - * possibly newer {@code Data} - * @return {@code true} if the {@code newer} Data is really newer, otherwise {@code false} - */ - protected static boolean isNewerData(@Nonnull final Data older, @Nonnull final Data newer) { - return newer.getVersion().compareTo(older.getVersion()) > 0; - } - - /** - * Reads the content from the given {@link URL} and saves it to the passed file. - * - * @param file - * file in which the entire contents from the given URL can be saved - * @param store - * a data store for UAS data - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if any of the passed arguments is {@code null} - * @throws IOException - * if an I/O error occurs - */ - protected static void readAndSave(@Nonnull final File file, @Nonnull final DataStore store) throws IOException { - Check.notNull(file, "file"); - Check.notNull(store, "store"); - - final URL url = store.getDataUrl(); - final Charset charset = store.getCharset(); - - final boolean isEqual = url.toExternalForm().equals(UrlUtil.toUrl(file).toExternalForm()); - if (!isEqual) { - - // check if the data can be read in successfully - final String data = UrlUtil.read(url, charset); - if (Data.EMPTY.equals(store.getDataReader().read(data))) { - throw new IllegalStateException("The read in content can not be transformed to an instance of 'Data'."); - } - - final File tempFile = createTemporaryFile(file); - - FileOutputStream outputStream = null; - boolean threw = true; - try { - // write data to temporary file - outputStream = new FileOutputStream(tempFile); - outputStream.write(data.getBytes(charset)); - - // delete the original file - deleteFile(file); - - threw = false; - } finally { - Closeables.close(outputStream, threw); - } - - // rename the new file to the original one - renameFile(tempFile, file); - } else { - LOG.debug(MSG_SAME_RESOURCES); - } - } - - /** - * Renames the given file {@code from} to the new file {@code to}. - * - * @param from - * an existing file - * @param to - * a new file - * - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the file can not be renamed - */ - protected static void renameFile(@Nonnull final File from, @Nonnull final File to) { - Check.notNull(from, "from"); - Check.stateIsTrue(from.exists(), "Argument 'from' must not be an existing file."); - Check.notNull(to, "to"); - Check.stateIsTrue(from.renameTo(to), "Renaming file from '%s' to '%s' failed.", from.getAbsolutePath(), to.getAbsolutePath()); - } - - /** - * File to cache read in UAS data - */ - private final File cacheFile; - - /** - * The data store for instances that implements {@link net.sf.uadetector.internal.data.Data} - */ - private final AbstractRefreshableDataStore store; - - public UpdateOperationWithCacheFileTask(@Nonnull final AbstractRefreshableDataStore dataStore, @Nonnull final File cacheFile) { - super(dataStore); - Check.notNull(dataStore, "dataStore"); - Check.notNull(cacheFile, "cacheFile"); - store = dataStore; - this.cacheFile = cacheFile; - } - - @Override - public void call() { - readDataIfNewerAvailable(); - } - - private boolean isCacheFileEmpty() { - return isEmpty(cacheFile, store.getCharset()); - } - - private void readDataIfNewerAvailable() { - try { - if (isUpdateAvailable() || isCacheFileEmpty()) { - readAndSave(cacheFile, store); - store.setData(store.getDataReader().read(cacheFile.toURI().toURL(), store.getCharset())); - } - } catch (final CanNotOpenStreamException e) { - LOG.warn(String.format(RefreshableDataStore.MSG_URL_NOT_READABLE, e.getLocalizedMessage())); - readFallbackData(); - } catch (final RuntimeException e) { - LOG.warn(RefreshableDataStore.MSG_FAULTY_CONTENT, e); - readFallbackData(); - } catch (final IOException e) { - LOG.warn(String.format(MSG_CACHE_FILE_ISSUES, e.getLocalizedMessage()), e); - readFallbackData(); - } - } - - private void readFallbackData() { - LOG.info("Reading fallback data..."); - try { - if (isCacheFileEmpty()) { - readAndSave(cacheFile, store.getFallback()); - final Data data = store.getDataReader().read(cacheFile.toURI().toURL(), store.getCharset()); - if (isNewerData(store.getData(), data)) { - store.setData(data); - } - } - } catch (final CanNotOpenStreamException e) { - LOG.warn(String.format(RefreshableDataStore.MSG_URL_NOT_READABLE, e.getLocalizedMessage())); - } catch (final RuntimeException e) { - LOG.warn(RefreshableDataStore.MSG_FAULTY_CONTENT, e); - } catch (final IOException e) { - LOG.warn(String.format(MSG_CACHE_FILE_ISSUES, e.getLocalizedMessage()), e); - } - } - -} diff --git a/app/src/main/java/net/sf/uadetector/datastore/package-info.java b/app/src/main/java/net/sf/uadetector/datastore/package-info.java deleted file mode 100644 index 62e253d..0000000 --- a/app/src/main/java/net/sf/uadetector/datastore/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -@javax.annotation.ParametersAreNonnullByDefault -package net.sf.uadetector.datastore; diff --git a/app/src/main/java/net/sf/uadetector/exception/CanNotOpenStreamException.java b/app/src/main/java/net/sf/uadetector/exception/CanNotOpenStreamException.java deleted file mode 100644 index d8d8def..0000000 --- a/app/src/main/java/net/sf/uadetector/exception/CanNotOpenStreamException.java +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.exception; - -/** - * Thrown to indicate that no stream to an {@link java.net.URL} can be established.
- *
- * This exception is intended to tunnel the checked exception {@link java.io.IOException} during the call - * {@link java.net.URL#openStream()}. - * - * @author André Rouél - */ -public class CanNotOpenStreamException extends RuntimeException { - - private static final long serialVersionUID = 8381680536297450770L; - - protected static final String DEFAULT_MESSAGE = "Can not open stream to the given URL."; - - protected static final String MESSAGE_WITH_URL = "Can not open stream to the given URL: %s"; - - private static String format(final String url) { - return String.format(MESSAGE_WITH_URL, url); - } - - /** - * Constructs an {@code CanNotOpenStreamException} with the default message - * {@link CanNotOpenStreamException#DEFAULT_MESSAGE}. - */ - public CanNotOpenStreamException() { - super(DEFAULT_MESSAGE); - } - - /** - * Constructs an {@code CanNotOpenStreamException} with the message - * {@link CanNotOpenStreamException#MESSAGE_WITH_URL} including the given URL as string representation. - * - * @param url - * the URL to which no stream can be established - */ - public CanNotOpenStreamException(final String url) { - super(format(url)); - } - - /** - * Constructs a new exception with the message {@link CanNotOpenStreamException#MESSAGE_WITH_URL} including the - * given URL as string representation and cause. - * - * @param url - * the URL to which no stream can be established - * @param cause - * the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A - * {@code null} value is permitted, and indicates that the cause is nonexistent or unknown.) - */ - public CanNotOpenStreamException(final String url, final Throwable cause) { - super(format(url), cause); - } - - /** - * Constructs a new exception with the default message {@link CanNotOpenStreamException#DEFAULT_MESSAGE}. - * - * @param cause - * the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A - * {@code null} value is permitted, and indicates that the cause is nonexistent or unknown.) - */ - public CanNotOpenStreamException(final Throwable cause) { - super(DEFAULT_MESSAGE, cause); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/exception/CannotCloseException.java b/app/src/main/java/net/sf/uadetector/exception/CannotCloseException.java deleted file mode 100644 index d5abc95..0000000 --- a/app/src/main/java/net/sf/uadetector/exception/CannotCloseException.java +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.exception; - -/** - * Thrown to indicate that a {@link java.io.Closeable} cannot be closed.
- *
- * This exception is intended to tunnel the checked exception {@link java.io.IOException} during the call - * {@link java.io.Closeable#close()}. - * - * @author André Rouél - */ -public class CannotCloseException extends RuntimeException { - - private static final long serialVersionUID = -8641033043995976022L; - - protected static final String DEFAULT_MESSAGE = "Cannot close the given Closeable."; - - protected static final String MESSAGE_WITH_INFO = "Cannot close the given Closeable: %s"; - - private static String format(final String url) { - return String.format(MESSAGE_WITH_INFO, url); - } - - /** - * Constructs an {@code CannotCloseException} with the default message {@link CannotCloseException#DEFAULT_MESSAGE}. - */ - public CannotCloseException() { - super(DEFAULT_MESSAGE); - } - - /** - * Constructs an {@code CannotCloseException} with the message {@link CannotCloseException#MESSAGE_WITH_INFO} - * including additional information. - * - * @param info - * additional information why a {@link java.io.Closeable} cannot be closed - */ - public CannotCloseException(final String info) { - super(format(info)); - } - - /** - * Constructs a new exception with the message {@link CannotCloseException#MESSAGE_WITH_INFO} including additional - * information. - * - * @param info - * additional information why a {@link java.io.Closeable} cannot be closed - * @param cause - * the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A - * {@code null} value is permitted, and indicates that the cause is nonexistent or unknown.) - */ - public CannotCloseException(final String info, final Throwable cause) { - super(format(info), cause); - } - - /** - * Constructs a new exception with the default message {@link CannotCloseException#DEFAULT_MESSAGE}. - * - * @param cause - * the cause (which is saved for later retrieval by the {@link Throwable#getCause()} method). (A - * {@code null} value is permitted, and indicates that the cause is nonexistent or unknown.) - */ - public CannotCloseException(final Throwable cause) { - super(DEFAULT_MESSAGE, cause); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/exception/package-info.java b/app/src/main/java/net/sf/uadetector/exception/package-info.java deleted file mode 100644 index 3a9659a..0000000 --- a/app/src/main/java/net/sf/uadetector/exception/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -@javax.annotation.ParametersAreNonnullByDefault -package net.sf.uadetector.exception; diff --git a/app/src/main/java/net/sf/uadetector/internal/data/BrowserOperatingSystemMappingComparator.java b/app/src/main/java/net/sf/uadetector/internal/data/BrowserOperatingSystemMappingComparator.java deleted file mode 100644 index 3f9b536..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/BrowserOperatingSystemMappingComparator.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.sf.uadetector.internal.data; - -import javax.annotation.concurrent.ThreadSafe; - -import net.sf.uadetector.internal.data.domain.BrowserOperatingSystemMapping; -import net.sf.uadetector.internal.util.CompareNullSafe; - -@ThreadSafe -public final class BrowserOperatingSystemMappingComparator extends CompareNullSafe { - - private static final long serialVersionUID = 3227329029706985170L; - - public static final BrowserOperatingSystemMappingComparator INSTANCE = new BrowserOperatingSystemMappingComparator(); - - /** - * Attention: This class is a stateless singleton and not intended to create more than one object - * from it. - */ - private BrowserOperatingSystemMappingComparator() { - // This class is not intended to create own objects from it. - } - - @Override - public int compareType(final BrowserOperatingSystemMapping o1, final BrowserOperatingSystemMapping o2) { - return o1.getBrowserId() < o2.getBrowserId() ? -1 : o1.getBrowserId() == o2.getBrowserId() ? 0 : 1; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/Data.java b/app/src/main/java/net/sf/uadetector/internal/data/Data.java deleted file mode 100644 index af7758f..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/Data.java +++ /dev/null @@ -1,379 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.internal.data.domain.Browser; -import net.sf.uadetector.internal.data.domain.BrowserOperatingSystemMapping; -import net.sf.uadetector.internal.data.domain.BrowserPattern; -import net.sf.uadetector.internal.data.domain.BrowserType; -import net.sf.uadetector.internal.data.domain.Device; -import net.sf.uadetector.internal.data.domain.DevicePattern; -import net.sf.uadetector.internal.data.domain.OperatingSystem; -import net.sf.uadetector.internal.data.domain.OperatingSystemPattern; -import net.sf.uadetector.internal.data.domain.Robot; - -/** - * This class represents the detection information of UADetector. - *

- * An instance of {@code Data} is immutable, their values cannot be changed after creation. - * - * @author André Rouél - */ -@Immutable -public class Data implements Serializable { - - /** - * An immutable empty {@code Data} object. - */ - public static final Data EMPTY = new Data(new HashSet(0), new HashMap>(0), - new HashMap(0), new TreeMap(), new HashSet(0), - new HashSet(0), new HashMap>(0), - new TreeMap(), new ArrayList(0), new HashSet(0), - new HashMap>(0), new TreeMap(), ""); - - private static final long serialVersionUID = 8522012551928801089L; - - @Nonnull - private final Map> browserPatterns; - - @Nonnull - private final Set browsers; - - @Nonnull - private final Map> devicePatterns; - - @Nonnull - private final Set devices; - - @Nonnull - private final SortedMap patternToDeviceMap; - - @Nonnull - private final Set browserToOperatingSystemMappings; - - @Nonnull - private final Map browserTypes; - - @Nonnull - private final Map> operatingSystemPatterns; - - @Nonnull - private final Set operatingSystems; - - @Nonnull - private final SortedMap patternToBrowserMap; - - @Nonnull - private final SortedMap patternToOperatingSystemMap; - - @Nonnull - private final List robots; - - /** - * Version information of the UAS data - */ - @Nonnull - private final String version; - - public Data(@Nonnull final Set browsers, @Nonnull final Map> browserPatterns, - @Nonnull final Map browserTypes, @Nonnull final SortedMap patternToBrowserMap, - @Nonnull final Set browserToOperatingSystemMappings, - @Nonnull final Set operatingSystems, - @Nonnull final Map> operatingSystemPatterns, - @Nonnull final SortedMap patternToOperatingSystemMap, - @Nonnull final List robots, @Nonnull final Set devices, - @Nonnull final Map> devicePatterns, - @Nonnull final SortedMap patternToDeviceMap, @Nonnull final String version) { - Check.notNull(browsers, "browsers"); - Check.notNull(browserPatterns, "browserPatterns"); - Check.notNull(browserTypes, "browserTypes"); - Check.notNull(patternToBrowserMap, "patternToBrowserMap"); - Check.notNull(browserToOperatingSystemMappings, "browserToOperatingSystemMap"); - Check.notNull(operatingSystems, "operatingSystems"); - Check.notNull(operatingSystemPatterns, "operatingSystemPatterns"); - Check.notNull(patternToOperatingSystemMap, "patternToOperatingSystemMap"); - Check.notNull(robots, "robots"); - Check.notNull(devices, "devices"); - Check.notNull(devicePatterns, "devicePatterns"); - Check.notNull(patternToDeviceMap, "patternToDeviceMap"); - Check.notNull(version, "version"); - - this.browsers = Collections.unmodifiableSet(new HashSet(browsers)); - this.browserPatterns = Collections.unmodifiableMap(new HashMap>(browserPatterns)); - this.browserTypes = Collections.unmodifiableMap(new HashMap(Check.notNull(browserTypes, "browserTypes"))); - this.patternToBrowserMap = Collections.unmodifiableSortedMap(new TreeMap(patternToBrowserMap)); - this.browserToOperatingSystemMappings = Collections.unmodifiableSet(new HashSet( - browserToOperatingSystemMappings)); - this.operatingSystems = Collections.unmodifiableSet(new HashSet(operatingSystems)); - this.operatingSystemPatterns = Collections.unmodifiableMap(new HashMap>( - operatingSystemPatterns)); - this.patternToOperatingSystemMap = Collections.unmodifiableSortedMap(new TreeMap( - patternToOperatingSystemMap)); - this.robots = Collections.unmodifiableList(new ArrayList(robots)); - this.devices = Collections.unmodifiableSet(new HashSet(devices)); - this.devicePatterns = Collections.unmodifiableMap(new HashMap>(devicePatterns)); - this.patternToDeviceMap = Collections.unmodifiableSortedMap(new TreeMap(patternToDeviceMap)); - this.version = Check.notNull(version, "version"); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final Data other = (Data) obj; - if (!browsers.equals(other.browsers)) { - return false; - } - if (!browserPatterns.equals(other.browserPatterns)) { - return false; - } - if (!browserTypes.equals(other.browserTypes)) { - return false; - } - if (!patternToBrowserMap.equals(other.patternToBrowserMap)) { - return false; - } - if (!browserToOperatingSystemMappings.equals(other.browserToOperatingSystemMappings)) { - return false; - } - if (!operatingSystems.equals(other.operatingSystems)) { - return false; - } - if (!operatingSystemPatterns.equals(other.operatingSystemPatterns)) { - return false; - } - if (!patternToOperatingSystemMap.equals(other.patternToOperatingSystemMap)) { - return false; - } - if (!robots.equals(other.robots)) { - return false; - } - if (!devices.equals(other.devices)) { - return false; - } - if (!devicePatterns.equals(other.devicePatterns)) { - return false; - } - if (!patternToDeviceMap.equals(other.patternToDeviceMap)) { - return false; - } - if (!version.equals(other.version)) { - return false; - } - return true; - } - - @Nonnull - public Map> getBrowserPatterns() { - return browserPatterns; - } - - @Nonnull - public Set getBrowsers() { - return browsers; - } - - @Nonnull - public Set getBrowserToOperatingSystemMappings() { - return browserToOperatingSystemMappings; - } - - @Nonnull - public Map getBrowserTypes() { - return browserTypes; - } - - @Nonnull - public Map> getDevicePatterns() { - return devicePatterns; - } - - @Nonnull - public Set getDevices() { - return devices; - } - - @Nonnull - public Map> getOperatingSystemPatterns() { - return operatingSystemPatterns; - } - - @Nonnull - public Set getOperatingSystems() { - return operatingSystems; - } - - @Nonnull - public SortedMap getPatternToBrowserMap() { - return patternToBrowserMap; - } - - @Nonnull - public SortedMap getPatternToDeviceMap() { - return patternToDeviceMap; - } - - @Nonnull - public SortedMap getPatternToOperatingSystemMap() { - return patternToOperatingSystemMap; - } - - @Nonnull - public List getRobots() { - return robots; - } - - /** - * Gets the version of the UAS data which are available within this instance. - * - * @return version of UAS data - */ - @Nonnull - public String getVersion() { - return version; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + browsers.hashCode(); - result = prime * result + browserPatterns.hashCode(); - result = prime * result + browserTypes.hashCode(); - result = prime * result + patternToBrowserMap.hashCode(); - result = prime * result + browserToOperatingSystemMappings.hashCode(); - result = prime * result + operatingSystems.hashCode(); - result = prime * result + operatingSystemPatterns.hashCode(); - result = prime * result + patternToOperatingSystemMap.hashCode(); - result = prime * result + robots.hashCode(); - result = prime * result + devices.hashCode(); - result = prime * result + devicePatterns.hashCode(); - result = prime * result + patternToDeviceMap.hashCode(); - result = prime * result + version.hashCode(); - return result; - } - - @Nonnull - public String toStats() { - final StringBuilder builder = new StringBuilder(); - builder.append("UAS data stats\n"); - builder.append("----------------------------------------------------------------"); - builder.append('\n'); - builder.append("version:\t\t"); - builder.append(version); - builder.append('\n'); - builder.append("browser:\t\t"); - builder.append(browsers.size()); - builder.append('\n'); - final Map browserByType = new HashMap(); - for (final Browser browser : browsers) { - final AtomicInteger counter = browserByType.get(browser.getType().getName()); - if (counter == null) { - browserByType.put(browser.getType().getName(), new AtomicInteger(1)); - } else { - counter.incrementAndGet(); - } - } - for (final Entry entry : browserByType.entrySet()) { - builder.append('\t'); - builder.append('\t'); - builder.append('\t'); - builder.append(entry.getKey()); - builder.append(":\t"); - builder.append(entry.getValue().get()); - builder.append('\n'); - } - builder.append("browser patterns:\t"); - builder.append(patternToBrowserMap.size()); - builder.append('\n'); - builder.append("operating systems:\t"); - builder.append(operatingSystems.size()); - builder.append('\n'); - builder.append("os patterns:\t\t"); - builder.append(patternToOperatingSystemMap.size()); - builder.append('\n'); - builder.append("robots:\t\t\t"); - builder.append(robots.size()); - builder.append('\n'); - builder.append("devices:\t"); - builder.append(devices.size()); - builder.append('\n'); - builder.append("device patterns:\t"); - builder.append(patternToDeviceMap.size()); - builder.append('\n'); - builder.append("----------------------------------------------------------------"); - return builder.toString(); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("Data [browsers="); - builder.append(browsers); - builder.append(", browserPatterns="); - builder.append(browserPatterns); - builder.append(", browserTypes="); - builder.append(browserTypes); - builder.append(", patternToBrowserMap="); - builder.append(patternToBrowserMap); - builder.append(", browserToOperatingSystemMap="); - builder.append(browserToOperatingSystemMappings); - builder.append(", operatingSystems="); - builder.append(operatingSystems); - builder.append(", operatingSystemPatterns="); - builder.append(operatingSystemPatterns); - builder.append(", patternToOperatingSystemMap="); - builder.append(patternToOperatingSystemMap); - builder.append(", robots="); - builder.append(robots); - builder.append(", devices="); - builder.append(devices); - builder.append(", devicePatterns="); - builder.append(devicePatterns); - builder.append(", patternToDeviceMap="); - builder.append(patternToDeviceMap); - builder.append(", version="); - builder.append(version); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/DataBuilder.java b/app/src/main/java/net/sf/uadetector/internal/data/DataBuilder.java deleted file mode 100644 index 8653bcb..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/DataBuilder.java +++ /dev/null @@ -1,472 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.SortedMap; -import java.util.SortedSet; -import java.util.TreeMap; -import java.util.TreeSet; - -import javax.annotation.Nonnull; -import javax.annotation.concurrent.NotThreadSafe; - -import net.sf.qualitycheck.Check; -import net.sf.qualitycheck.exception.IllegalStateOfArgumentException; -import net.sf.uadetector.internal.data.domain.Browser; -import net.sf.uadetector.internal.data.domain.BrowserOperatingSystemMapping; -import net.sf.uadetector.internal.data.domain.BrowserPattern; -import net.sf.uadetector.internal.data.domain.BrowserType; -import net.sf.uadetector.internal.data.domain.Device; -import net.sf.uadetector.internal.data.domain.DevicePattern; -import net.sf.uadetector.internal.data.domain.OperatingSystem; -import net.sf.uadetector.internal.data.domain.OperatingSystemPattern; -import net.sf.uadetector.internal.data.domain.Robot; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class is intended to create instances of {@code Data}. - * - * @author André Rouél - */ -@NotThreadSafe -public class DataBuilder { - - private static final Logger LOG = LoggerFactory.getLogger(DataBuilder.class); - - private static void addOperatingSystemToBrowser(final Map browserBuilders, - final Map operatingSystems, final Map browserOsMap) { - Browser.Builder browserBuilder; - for (final Map.Entry entry : browserOsMap.entrySet()) { - if (browserBuilders.containsKey(entry.getKey())) { - browserBuilder = browserBuilders.get(entry.getKey()); - if (operatingSystems.containsKey(entry.getValue())) { - browserBuilder.setOperatingSystem(operatingSystems.get(entry.getValue())); - } else { - LOG.warn("Can not find an operating system with ID '" + entry.getValue() + "' for browser '" - + browserBuilder.getProducer() + " " + browserBuilder.getFamily() + "'."); - } - } else { - LOG.warn("Can not find a browser with ID '" + entry.getKey() + "'."); - } - } - } - - private static void addPatternToBrowser(final Map builders, - final Map> patterns) { - for (final Map.Entry entry : builders.entrySet()) { - if (patterns.containsKey(entry.getKey())) { - entry.getValue().setPatterns(patterns.get(entry.getKey())); - } else { - LOG.warn("No pattern available for '" + entry.getValue().getProducer() + " " + entry.getValue().getFamily() + "'."); - } - } - } - - private static void addPatternToDevice(final Map builders, - final Map> patterns) { - for (final Map.Entry entry : builders.entrySet()) { - if (patterns.containsKey(entry.getKey())) { - entry.getValue().setPatterns(patterns.get(entry.getKey())); - } else { - LOG.debug("No pattern available for '" + entry.getValue().getName() + "'."); - } - } - } - - private static void addPatternToOperatingSystem(final Map builders, - final Map> patterns) { - for (final Map.Entry entry : builders.entrySet()) { - final SortedSet patternSet = patterns.get(entry.getKey()); - if (patternSet != null) { - entry.getValue().addPatterns(patternSet); - } else { - LOG.debug("No patterns for operating system entry (with id '" + entry.getKey() + "') available."); - } - } - } - - private static void addTypeToBrowser(final Map builders, final Map types) { - int typeId; - for (final Map.Entry entry : builders.entrySet()) { - typeId = entry.getValue().getTypeId(); - if (types.containsKey(typeId)) { - entry.getValue().setType(types.get(typeId)); - } else { - LOG.warn("No type available for '" + entry.getValue().getProducer() + " " + entry.getValue().getFamily() + "'."); - } - } - } - - private static Set buildBrowsers(final Map browserBuilders) { - final Set browsers = new HashSet(); - for (final Map.Entry entry : browserBuilders.entrySet()) { - try { - browsers.add(entry.getValue().build()); - } catch (final Exception e) { - LOG.warn("Can not build browser: " + e.getLocalizedMessage()); - } - } - return browsers; - } - - private static Set buildDevices(final Map deviceBuilders) { - final Set devices = new HashSet(); - for (final Map.Entry entry : deviceBuilders.entrySet()) { - try { - devices.add(entry.getValue().build()); - } catch (final Exception e) { - LOG.warn("Can not build device '" + entry.getValue().getName() + "': " + e.getLocalizedMessage()); - } - } - return devices; - } - - private static Map buildOperatingSystems(final Map osBuilders) { - final Map operatingSystems = new HashMap(); - for (final Map.Entry entry : osBuilders.entrySet()) { - try { - operatingSystems.put(entry.getKey(), entry.getValue().build()); - } catch (final Exception e) { - LOG.warn("Can not build operating system: " + e.getLocalizedMessage()); - } - } - return operatingSystems; - } - - private static SortedMap buildPatternToBrowserMap(final Set browserSet) { - final SortedMap patternBrowser = new TreeMap(BROWSER_PATTERN_COMPARATOR); - for (final Browser browser : browserSet) { - for (final BrowserPattern pattern : browser.getPatterns()) { - patternBrowser.put(pattern, browser); - } - } - return patternBrowser; - } - - private static SortedMap buildPatternToDeviceMap(final Set devices) { - final SortedMap patternDevice = new TreeMap(DEVICE_PATTERN_COMPARATOR); - for (final Device device : devices) { - for (final DevicePattern pattern : device.getPatterns()) { - patternDevice.put(pattern, device); - } - } - return patternDevice; - } - - private static SortedMap buildPatternToOperatingSystemMap(final Set osSet) { - final SortedMap map = new TreeMap( - OS_PATTERN_COMPARATOR); - for (final OperatingSystem os : osSet) { - for (final OperatingSystemPattern pattern : os.getPatterns()) { - map.put(pattern, os); - } - } - return map; - } - - private static Map convertBrowserOsMapping(final Set browserOperatingSystemMappings) { - final Map result = new HashMap(); - for (final BrowserOperatingSystemMapping mapping : browserOperatingSystemMappings) { - result.put(mapping.getBrowserId(), mapping.getOperatingSystemId()); - } - return result; - } - - private static Set convertOperatingSystems(final Map operatingSystems) { - final Set result = new HashSet(); - for (final Entry entry : operatingSystems.entrySet()) { - result.add(entry.getValue()); - } - return result; - } - - @Nonnull - private final Map browserTypes = new HashMap(); - - @Nonnull - private final Map> browserPatterns = new HashMap>(); - - @Nonnull - private final Map> operatingSystemPatterns = new HashMap>(); - - @Nonnull - private final Map browserBuilders = new HashMap(); - - @Nonnull - private final Set browsers = new HashSet(); - - @Nonnull - private final Set devices = new HashSet(); - - @Nonnull - private final Map deviceBuilders = new HashMap(); - - @Nonnull - private final Map> devicePatterns = new HashMap>(); - - @Nonnull - private final Map operatingSystemBuilders = new HashMap(); - - @Nonnull - private final Set operatingSystems = new HashSet(); - - @Nonnull - private final List robots = new ArrayList(); - - private String version; - - @Nonnull - private final Set browserToOperatingSystemMap = new HashSet(); - - private static final OrderedPatternComparator BROWSER_PATTERN_COMPARATOR = new OrderedPatternComparator(); - - private static final OrderedPatternComparator DEVICE_PATTERN_COMPARATOR = new OrderedPatternComparator(); - - private static final OrderedPatternComparator OS_PATTERN_COMPARATOR = new OrderedPatternComparator(); - - public DataBuilder appendBrowser(@Nonnull final Browser browser) { - Check.notNull(browser, "browser"); - - browsers.add(browser); - return this; - } - - /** - * Appends a copy of the given {@code Browser.Builder} to the internal data structure. - * - * @param browserBuilder - * {@code Browser.Builder} to be copied and appended - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the ID of the given builder is invalid - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if a builder with the same ID already exists - */ - @Nonnull - public DataBuilder appendBrowserBuilder(@Nonnull final Browser.Builder browserBuilder) { - Check.notNull(browserBuilder, "browserBuilder"); - Check.notNegative(browserBuilder.getId(), "browserBuilder.getId()"); - if (browserBuilder.getType() == null && browserBuilder.getTypeId() < 0) { - throw new IllegalStateOfArgumentException("A Type or Type-ID of argument 'browserBuilder' must be set."); - } - if (browserBuilders.containsKey(browserBuilder.getId())) { - throw new IllegalStateOfArgumentException("The browser builder '" + browserBuilder.getProducer() + " " - + browserBuilder.getFamily() + "' is already in the map."); - } - - final Browser.Builder builder = browserBuilder.copy(); - browserBuilders.put(builder.getId(), builder); - return this; - } - - @Nonnull - public DataBuilder appendBrowserOperatingSystemMapping(@Nonnull final BrowserOperatingSystemMapping browserOsMapping) { - Check.notNull(browserOsMapping, "browserOsMapping"); - - browserToOperatingSystemMap.add(browserOsMapping); - return this; - } - - /** - * Appends a browser pattern to the map of pattern sorted by ID. - * - * @param pattern - * a pattern for a browser - * @return itself - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - @Nonnull - public DataBuilder appendBrowserPattern(@Nonnull final BrowserPattern pattern) { - Check.notNull(pattern, "pattern"); - if (!browserPatterns.containsKey(pattern.getId())) { - browserPatterns.put(pattern.getId(), new TreeSet(BROWSER_PATTERN_COMPARATOR)); - } - - browserPatterns.get(pattern.getId()).add(pattern); - return this; - } - - @Nonnull - public DataBuilder appendBrowserType(@Nonnull final BrowserType type) { - Check.notNull(type, "type"); - - browserTypes.put(type.getId(), type); - return this; - } - - public DataBuilder appendDevice(@Nonnull final Device device) { - Check.notNull(device, "device"); - - devices.add(device); - return this; - } - - /** - * Appends a copy of the given {@code Device.Builder} to the internal data structure. - * - * @param deviceBuilder - * {@code Device.Builder} to be copied and appended - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if the ID of the given builder is invalid - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if a builder with the same ID already exists - */ - @Nonnull - public DataBuilder appendDeviceBuilder(@Nonnull final Device.Builder deviceBuilder) { - Check.notNull(deviceBuilder, "deviceBuilder"); - Check.notNegative(deviceBuilder.getId(), "deviceBuilder.getId()"); - if (deviceBuilders.containsKey(deviceBuilder.getId())) { - throw new IllegalStateOfArgumentException("The device builder '" + deviceBuilder.getName() + "' is already in the map."); - } - - final Device.Builder builder = deviceBuilder.copy(); - deviceBuilders.put(builder.getId(), builder); - return this; - } - - /** - * Appends a device pattern to the map of pattern sorted by ID. - * - * @param pattern - * a pattern for a device - * @return itself - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - @Nonnull - public DataBuilder appendDevicePattern(@Nonnull final DevicePattern pattern) { - Check.notNull(pattern, "pattern"); - if (!devicePatterns.containsKey(pattern.getId())) { - devicePatterns.put(pattern.getId(), new TreeSet(DEVICE_PATTERN_COMPARATOR)); - } - - devicePatterns.get(pattern.getId()).add(pattern); - return this; - } - - @Nonnull - public DataBuilder appendOperatingSystem(@Nonnull final OperatingSystem operatingSystem) { - Check.notNull(operatingSystem, "operatingSystem"); - - operatingSystems.add(operatingSystem); - return this; - } - - /** - * Appends a copy of the given {@code OperatingSystem.Builder} to the internal data structure. - * - * @param operatingSystemBuilder - * {@code OperatingSystem.Builder} to be copied and appended - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the ID of the given builder is negative - * @throws net.sf.qualitycheck.exception.IllegalStateOfArgumentException - * if a builder with the same ID already exists - */ - @Nonnull - public DataBuilder appendOperatingSystemBuilder(@Nonnull final OperatingSystem.Builder operatingSystemBuilder) { - Check.notNull(operatingSystemBuilder, "operatingSystemBuilder"); - Check.notNegative(operatingSystemBuilder.getId(), "operatingSystemBuilder.getId()"); - Check.stateIsTrue(!operatingSystemBuilders.containsKey(operatingSystemBuilder.getId()), - "Operating system builder with ID '%s' already exists.", operatingSystemBuilder.getId()); - - final OperatingSystem.Builder builder = operatingSystemBuilder.copy(); - operatingSystemBuilders.put(builder.getId(), builder); - return this; - } - - /** - * Appends an operating system pattern to the map of pattern sorted by ID. - * - * @param pattern - * a pattern for a browser - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the pattern is {@code null} - * @return itself - */ - @Nonnull - public DataBuilder appendOperatingSystemPattern(@Nonnull final OperatingSystemPattern pattern) { - Check.notNull(pattern, "pattern"); - - if (!operatingSystemPatterns.containsKey(pattern.getId())) { - operatingSystemPatterns.put(pattern.getId(), new TreeSet(OS_PATTERN_COMPARATOR)); - } - - operatingSystemPatterns.get(pattern.getId()).add(pattern); - return this; - } - - @Nonnull - public DataBuilder appendRobot(@Nonnull final Robot robot) { - Check.notNull(robot, "robot"); - - robots.add(robot); - return this; - } - - @Nonnull - public Data build() { - addTypeToBrowser(browserBuilders, browserTypes); - addPatternToBrowser(browserBuilders, browserPatterns); - addPatternToOperatingSystem(operatingSystemBuilders, operatingSystemPatterns); - addPatternToDevice(deviceBuilders, devicePatterns); - - final Map systems = buildOperatingSystems(operatingSystemBuilders); - addOperatingSystemToBrowser(browserBuilders, systems, convertBrowserOsMapping(browserToOperatingSystemMap)); - - final Set osSet = convertOperatingSystems(systems); - osSet.addAll(operatingSystems); - - final Set browserSet = buildBrowsers(browserBuilders); - browserSet.addAll(browsers); - - final Set deviceSet = buildDevices(deviceBuilders); - deviceSet.addAll(devices); - - final SortedMap patternToBrowserMap = buildPatternToBrowserMap(browserSet); - final SortedMap patternToOperatingSystemMap = buildPatternToOperatingSystemMap(osSet); - final SortedMap patternToDeviceMap = buildPatternToDeviceMap(deviceSet); - - return new Data(browserSet, browserPatterns, browserTypes, patternToBrowserMap, browserToOperatingSystemMap, osSet, - operatingSystemPatterns, patternToOperatingSystemMap, robots, deviceSet, devicePatterns, patternToDeviceMap, version); - } - - @Nonnull - public DataBuilder setVersion(@Nonnull final String version) { - Check.notNull(version, "version"); - - this.version = version; - return this; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/IdentifiableComparator.java b/app/src/main/java/net/sf/uadetector/internal/data/IdentifiableComparator.java deleted file mode 100644 index 87937ac..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/IdentifiableComparator.java +++ /dev/null @@ -1,30 +0,0 @@ -package net.sf.uadetector.internal.data; - -import java.util.Comparator; - -import javax.annotation.concurrent.ThreadSafe; - -import net.sf.uadetector.internal.data.domain.Identifiable; -import net.sf.uadetector.internal.util.CompareNullSafe; - -@ThreadSafe -public final class IdentifiableComparator extends CompareNullSafe implements Comparator { - - public static final IdentifiableComparator INSTANCE = new IdentifiableComparator(); - - private static final long serialVersionUID = -4279820324904203666L; - - /** - * Attention: This class is a stateless singleton and not intended to create more than one object - * from it. - */ - private IdentifiableComparator() { - // This class is not intended to create own objects from it. - } - - @Override - public int compareType(final Identifiable o1, final Identifiable o2) { - return compareInt(o1.getId(), o2.getId()); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/OrderedPatternComparator.java b/app/src/main/java/net/sf/uadetector/internal/data/OrderedPatternComparator.java deleted file mode 100644 index 7d1a129..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/OrderedPatternComparator.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data; - -import net.sf.uadetector.internal.util.CompareNullSafe; - -public final class OrderedPatternComparator> extends CompareNullSafe { - - private static final long serialVersionUID = -3561941361756671092L; - - @Override - public int compareType(final T o1, final T o2) { - return o1.compareTo(o2); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/XmlDataHandler.java b/app/src/main/java/net/sf/uadetector/internal/data/XmlDataHandler.java deleted file mode 100644 index d2ac983..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/XmlDataHandler.java +++ /dev/null @@ -1,709 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.internal.data.domain.Browser; -import net.sf.uadetector.internal.data.domain.BrowserOperatingSystemMapping; -import net.sf.uadetector.internal.data.domain.BrowserPattern; -import net.sf.uadetector.internal.data.domain.BrowserType; -import net.sf.uadetector.internal.data.domain.Device; -import net.sf.uadetector.internal.data.domain.DevicePattern; -import net.sf.uadetector.internal.data.domain.OperatingSystem; -import net.sf.uadetector.internal.data.domain.OperatingSystemPattern; -import net.sf.uadetector.internal.data.domain.Robot; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xml.sax.Attributes; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; -import org.xml.sax.helpers.DefaultHandler; - -public final class XmlDataHandler extends DefaultHandler { - - public enum Tag { - - /** - * Tag name of a browser entry - */ - BROWSER("browser"), - - /** - * Tag name of the ID of an browser pattern - */ - BROWSER_ID("browser_id"), - - /** - * Tag name of the informational URL of a browser entry - */ - BROWSER_INFO_URL("browser_info_url"), - - /** - * Tag name of a mapping entry between a browser and an operating system - */ - BROWSER_OS_MAPPING("browser_os"), - - /** - * Tag name of a browser pattern - */ - BROWSER_PATTERN("browser_reg"), - - /** - * Tag name of a browser type entry - */ - BROWSER_TYPE("browser_type"), - - /** - * Tag name of the type ID of a browser - */ - BROWSER_TYPE_ID("type"), - - /** - * Tag name of a producer company of an user agent - */ - COMPANY("company"), - - /** - * Tag name of the URL of a producer company from an user agent - */ - COMPANY_URL("url_company"), - - /** - * Tag name of a device - */ - DEVICE("device"), - - /** - * Tag name of a device pattern - */ - DEVICE_ID("device_id"), - - /** - * Tag name of the informational URL of an device entry - */ - DEVICE_INFO_URL("device_info_url"), - - /** - * Tag name of a device pattern - */ - DEVICE_PATTERN("device_reg"), - - /** - * Tag name of all devices - */ - DEVICES("devices"), - - /** - * Tag name of all device patterns - */ - DEVICES_PATTERN("devices_reg"), - - /** - * Tag name of an family of an user agent - */ - FAMILY("family"), - - /** - * Tag name of the icon of an entry - */ - ICON("icon"), - - /** - * Tag name of an ID of an user agent - */ - ID("id"), - - /** - * Tag name of the product name of an user agent - */ - NAME("name"), - - /** - * Tag name of an operating system entry - */ - OPERATING_SYSTEM("os"), - - /** - * Tag name of the ID of an operating system pattern - */ - OPERATING_SYSTEM_ID("os_id"), - - /** - * Tag name of the informational URL of an operating system entry - */ - OPERATING_SYSTEM_INFO_URL("os_info_url"), - - /** - * Tag name of an operating system pattern - */ - OPERATING_SYSTEM_PATTERN("operating_system_reg"), - - /** - * Tag name of the order of an user agent pattern - */ - PATTERN_ORDER("order"), - - /** - * Tag name of the regular expression of an user agent pattern - */ - PATTERN_REGEX("regstring"), - - /** - * Tag name of a robot entry - */ - ROBOT("robot"), - - /** - * Tag name of the informational URL of a robot entry - */ - ROBOT_INFO_URL("bot_info_url"), - - /** - * Tag name of the product URL of an user agent - */ - URL("url"), - - /** - * Tag name of an user agent string of a robot entry - */ - USERAGENT("useragent"), - - /** - * Tag name of the data version - */ - VERSION("version"); - - public static Tag evaluate(@Nonnull final String tagName) { - Check.notNull(tagName, "tagName"); - - Tag result = null; - for (final Tag tag : values()) { - if (tag.getTagName().equalsIgnoreCase(tagName)) { - result = tag; - break; - } - } - return result; - } - - public static boolean isBrowserOsMappingTag(final String tagName) { - return BROWSER_OS_MAPPING.getTagName().equalsIgnoreCase(tagName); - } - - public static boolean isBrowserPatternTag(final String tagName) { - return BROWSER_PATTERN.getTagName().equalsIgnoreCase(tagName); - } - - public static boolean isBrowserTag(final String tagName) { - return BROWSER.getTagName().equalsIgnoreCase(tagName); - } - - public static boolean isBrowserTypeTag(final String tagName) { - return BROWSER_TYPE.getTagName().equalsIgnoreCase(tagName); - } - - public static boolean isDevicePatternTag(final String tagName) { - return DEVICE_PATTERN.getTagName().equalsIgnoreCase(tagName); - } - - public static boolean isDeviceTag(final String tagName) { - return DEVICE.getTagName().equalsIgnoreCase(tagName); - } - - public static boolean isIdTag(final String tagName) { - return ID.getTagName().equalsIgnoreCase(tagName); - } - - public static boolean isOperatingSystemPatternTag(final String tagName) { - return OPERATING_SYSTEM_PATTERN.getTagName().equalsIgnoreCase(tagName); - } - - public static boolean isOperatingSystemTag(final String tagName) { - return OPERATING_SYSTEM.getTagName().equalsIgnoreCase(tagName); - } - - public static boolean isRobotTag(final String tagName) { - return ROBOT.getTagName().equalsIgnoreCase(tagName); - } - - @Nonnull - private String tagName; - - private Tag(@Nonnull final String tagName) { - this.tagName = tagName; - } - - @Nonnull - public String getTagName() { - return tagName; - } - - } - - /** - * Character set to read the internal Document Type Definition (DTD) of UAS data - */ - private static final String CHARSET = "UTF-8"; - - /** - * Corresponding logger for this class - */ - private static final Logger LOG = LoggerFactory.getLogger(XmlDataHandler.class); - - /** - * Path to the internal Document Type Definition (DTD) of UAS data files to be able to work completely offline - */ - protected static final String UASDATA_DEF = "uadetector/uasxmldata.dtd"; - - /** - * URL to the Document Type Definition (DTD) of UAS data files - */ - protected static final String UASDATA_DEF_URL = "http://data.udger.com/uasxmldata_old.dtd"; - - /** - * Logs an issue while parsing XML. - * - * @param prefix - * log level as string to add at the beginning of the message - * @param e - * exception to log - */ - protected static void logParsingIssue(final String prefix, final SAXParseException e) { - final StringBuilder buffer = new StringBuilder(); - buffer.append(prefix); - buffer.append(" while reading UAS data: "); - buffer.append(e.getMessage()); - buffer.append(" (line: "); - buffer.append(e.getLineNumber()); - if (e.getSystemId() != null) { - buffer.append(" uri: "); - buffer.append(e.getSystemId()); - } - buffer.append(")"); - LOG.warn(buffer.toString()); - } - - private Browser.Builder browserBuilder = new Browser.Builder(); - - private Device.Builder deviceBuilder = new Device.Builder(); - - private BrowserOperatingSystemMapping.Builder browserOsMappingBuilder = new BrowserOperatingSystemMapping.Builder(); - - private BrowserPattern.Builder browserPatternBuilder = new BrowserPattern.Builder(); - - private DevicePattern.Builder devicePatternBuilder = new DevicePattern.Builder(); - - private BrowserType.Builder browserTypeBuilder = new BrowserType.Builder(); - - private StringBuilder buffer = new StringBuilder(); - - private Tag currentTag = null; - - @Nonnull - private final DataBuilder dataBuilder; - - /** - * Flag to note that a fatal error occurred while parsing the document - */ - private boolean error = false; - - private boolean isBrowser = false; - - private boolean isBrowserOsMapping = false; - - private boolean isBrowserPattern = false; - - private boolean isBrowserType = false; - - private boolean isDevice = false; - - private boolean isDevicePattern = false; - - private boolean isOperatingSystem = false; - - private boolean isOperatingSystemPattern = false; - - private boolean isRobot = false; - - private OperatingSystem.Builder operatingSystemBuilder = new OperatingSystem.Builder(); - - private OperatingSystemPattern.Builder operatingSystemPatternBuilder = new OperatingSystemPattern.Builder(); - - private Robot.Builder robotBuilder = new Robot.Builder(); - - /** - * Flag to note that a warning occurred while parsing the document - */ - private boolean warning = false; - - public XmlDataHandler(@Nonnull final DataBuilder builder) { - Check.notNull(builder, "builder"); - - dataBuilder = builder; - } - - private void addToBrowserBuilder() { - if (isBrowser) { - if (currentTag == Tag.ID) { - browserBuilder.setId(buffer.toString()); - } else if (currentTag == Tag.BROWSER_TYPE_ID) { - browserBuilder.setTypeId(buffer.toString()); - } else if (currentTag == Tag.NAME) { - browserBuilder.setFamilyName(buffer.toString()); - } else if (currentTag == Tag.URL) { - browserBuilder.setUrl(buffer.toString()); - } else if (currentTag == Tag.COMPANY) { - browserBuilder.setProducer(buffer.toString()); - } else if (currentTag == Tag.COMPANY_URL) { - browserBuilder.setProducerUrl(buffer.toString()); - } else if (currentTag == Tag.ICON) { - browserBuilder.setIcon(buffer.toString()); - } else if (currentTag == Tag.BROWSER_INFO_URL) { - browserBuilder.setInfoUrl(buffer.toString()); - } - } - } - - private void addToBrowserOsMappingBuilder() { - if (isBrowserOsMapping && currentTag == Tag.BROWSER_ID) { - browserOsMappingBuilder.setBrowserId(buffer.toString()); - } else if (isBrowserOsMapping && currentTag == Tag.OPERATING_SYSTEM_ID) { - browserOsMappingBuilder.setOperatingSystemId(buffer.toString()); - } - } - - private void addToBrowserPatternBuilder() { - if (isBrowserPattern && currentTag == Tag.PATTERN_ORDER) { - browserPatternBuilder.setPosition(buffer.toString()); - } else if (isBrowserPattern && currentTag == Tag.BROWSER_ID) { - browserPatternBuilder.setId(buffer.toString()); - } else if (isBrowserPattern && currentTag == Tag.PATTERN_REGEX) { - browserPatternBuilder.setPerlRegularExpression(buffer.toString()); - } - } - - private void addToBrowserTypeBuilder() { - if (isBrowserType && currentTag == Tag.ID) { - browserTypeBuilder.setId(buffer.toString()); - } else if (isBrowserType && currentTag == Tag.BROWSER_TYPE_ID) { - browserTypeBuilder.setName(buffer.toString()); - } - } - - private void addToDeviceBuilder() { - if (isDevice) { - if (currentTag == Tag.ID) { - deviceBuilder.setId(buffer.toString()); - } else if (currentTag == Tag.NAME) { - deviceBuilder.setName(buffer.toString()); - } else if (currentTag == Tag.ICON) { - deviceBuilder.setIcon(buffer.toString()); - } else if (currentTag == Tag.DEVICE_INFO_URL) { - deviceBuilder.setInfoUrl(buffer.toString()); - } - } - } - - private void addToDevicePatternBuilder() { - if (isDevicePattern && currentTag == Tag.PATTERN_ORDER) { - devicePatternBuilder.setPosition(buffer.toString()); - } else if (isDevicePattern && currentTag == Tag.DEVICE_ID) { - devicePatternBuilder.setId(buffer.toString()); - } else if (isDevicePattern && currentTag == Tag.PATTERN_REGEX) { - devicePatternBuilder.setPerlRegularExpression(buffer.toString()); - } - } - - private void addToOperatingSystemBuilder() { - if (isOperatingSystem) { - if (currentTag == Tag.ID) { - operatingSystemBuilder.setId(buffer.toString()); - } else if (currentTag == Tag.FAMILY) { - operatingSystemBuilder.setFamily(buffer.toString()); - } else if (currentTag == Tag.NAME) { - operatingSystemBuilder.setName(buffer.toString()); - } else if (currentTag == Tag.URL) { - operatingSystemBuilder.setUrl(buffer.toString()); - } else if (currentTag == Tag.COMPANY) { - operatingSystemBuilder.setProducer(buffer.toString()); - } else if (currentTag == Tag.COMPANY_URL) { - operatingSystemBuilder.setProducerUrl(buffer.toString()); - } else if (currentTag == Tag.ICON) { - operatingSystemBuilder.setIcon(buffer.toString()); - } else if (currentTag == Tag.OPERATING_SYSTEM_INFO_URL) { - operatingSystemBuilder.setInfoUrl(buffer.toString()); - } - } - } - - private void addToOperatingSystemPatternBuilder() { - if (isOperatingSystemPattern) { - if (currentTag == Tag.PATTERN_ORDER) { - operatingSystemPatternBuilder.setPosition(buffer.toString()); - } else if (currentTag == Tag.OPERATING_SYSTEM_ID) { - operatingSystemPatternBuilder.setId(buffer.toString()); - } else if (currentTag == Tag.PATTERN_REGEX) { - operatingSystemPatternBuilder.setPerlRegularExpression(buffer.toString()); - } - } - } - - private void addToRobotBuilder() { - if (isRobot) { - if (currentTag == Tag.ID) { - robotBuilder.setId(buffer.toString()); - } else if (currentTag == Tag.USERAGENT) { - robotBuilder.setUserAgentString(buffer.toString()); - } else if (currentTag == Tag.FAMILY) { - robotBuilder.setFamilyName(buffer.toString()); - } else if (currentTag == Tag.NAME) { - robotBuilder.setName(buffer.toString()); - } else if (currentTag == Tag.COMPANY) { - robotBuilder.setProducer(buffer.toString()); - } else if (currentTag == Tag.COMPANY_URL) { - robotBuilder.setProducerUrl(buffer.toString()); - } else if (currentTag == Tag.ICON) { - robotBuilder.setIcon(buffer.toString()); - } else if (currentTag == Tag.ROBOT_INFO_URL) { - robotBuilder.setInfoUrl(buffer.toString()); - } - } - } - - @Override - public void characters(final char ch[], final int start, final int length) throws SAXException { - buffer.append(new String(ch, start, length)); - } - - @Override - public void endElement(final String uri, final String localName, final String tagName) throws SAXException { - - transferToSpecificBuilderAndReset(); - - if (Tag.isRobotTag(tagName)) { - saveAndResetRobotBuilder(); - isRobot = false; - } else if (Tag.isBrowserTag(tagName)) { - saveAndResetBrowserBuilder(); - isBrowser = false; - } else if (Tag.isOperatingSystemTag(tagName)) { - saveAndResetOperatingSystemBuilder(); - isOperatingSystem = false; - } else if (Tag.isBrowserTypeTag(tagName)) { - saveAndResetBrowserTypeBuilder(); - isBrowserType = false; - } else if (Tag.isBrowserPatternTag(tagName)) { - saveAndResetBrowserPatternBuilder(); - isBrowserPattern = false; - } else if (Tag.isBrowserOsMappingTag(tagName)) { - saveAndResetBrowserOperatingSystemMapping(); - isBrowserOsMapping = false; - } else if (Tag.isOperatingSystemPatternTag(tagName)) { - saveAndResetOperatingSystemPatternBuilder(); - isOperatingSystemPattern = false; - } else if (Tag.isDeviceTag(tagName)) { - saveAndResetDeviceBuilder(); - isDevice = false; - } else if (Tag.isDevicePatternTag(tagName)) { - saveAndResetDevicePatternBuilder(); - isDevicePattern = false; - } - - currentTag = null; - } - - @Override - public void error(final SAXParseException e) throws SAXException { - error = true; - logParsingIssue("Error", e); - super.fatalError(e); - } - - @Override - public void fatalError(final SAXParseException e) throws SAXException { - logParsingIssue("Fatal error", e); - - // this call throws a SAXException - super.fatalError(e); - } - - /** - * Gets the flag whether an error occurred while parsing the document. - * - * @return {@code true} if an error occurred otherwise {@code false} - */ - public boolean hasError() { - return error; - } - - /** - * Gets the flag whether an warning occurred while parsing the document. - * - * @return {@code true} if an warning occurred otherwise {@code false} - */ - public boolean hasWarning() { - return warning; - } - - @Override - public InputSource resolveEntity(final String publicId, final String systemId) throws IOException, SAXException { - if (UASDATA_DEF_URL.equals(systemId)) { - final InputStream stream = this.getClass().getClassLoader().getResourceAsStream(UASDATA_DEF); - return new InputSource(new InputStreamReader(stream, CHARSET)); - } - throw new SAXException("unable to resolve remote entity, systemId = " + systemId); - } - - private void saveAndResetBrowserBuilder() { - dataBuilder.appendBrowserBuilder(browserBuilder); - browserBuilder = new Browser.Builder(); - } - - private void saveAndResetBrowserOperatingSystemMapping() { - dataBuilder.appendBrowserOperatingSystemMapping(browserOsMappingBuilder.build()); - browserOsMappingBuilder = new BrowserOperatingSystemMapping.Builder(); - } - - private void saveAndResetBrowserPatternBuilder() { - try { - dataBuilder.appendBrowserPattern(browserPatternBuilder.build()); - } catch (final IllegalArgumentException e) { - LOG.warn("Can not append browser pattern: " + e.getLocalizedMessage()); - } - browserPatternBuilder = new BrowserPattern.Builder(); - } - - private void saveAndResetBrowserTypeBuilder() { - dataBuilder.appendBrowserType(browserTypeBuilder.build()); - browserTypeBuilder = new BrowserType.Builder(); - } - - private void saveAndResetDeviceBuilder() { - dataBuilder.appendDeviceBuilder(deviceBuilder); - deviceBuilder = new Device.Builder(); - } - - private void saveAndResetDevicePatternBuilder() { - try { - dataBuilder.appendDevicePattern(devicePatternBuilder.build()); - } catch (final IllegalArgumentException e) { - LOG.warn("Can not append device pattern: " + e.getLocalizedMessage()); - } - devicePatternBuilder = new DevicePattern.Builder(); - } - - private void saveAndResetOperatingSystemBuilder() { - dataBuilder.appendOperatingSystemBuilder(operatingSystemBuilder); - operatingSystemBuilder = new OperatingSystem.Builder(); - } - - private void saveAndResetOperatingSystemPatternBuilder() { - try { - dataBuilder.appendOperatingSystemPattern(operatingSystemPatternBuilder.build()); - } catch (final IllegalArgumentException e) { - LOG.warn("Can not append OS pattern: " + e.getLocalizedMessage()); - } - operatingSystemPatternBuilder = new OperatingSystemPattern.Builder(); - } - - private void saveAndResetRobotBuilder() { - dataBuilder.appendRobot(robotBuilder.build()); - robotBuilder = new Robot.Builder(); - } - - @Override - public void startElement(final String uri, final String localName, final String tagName, final Attributes attributes) - throws SAXException { - - if (Tag.isRobotTag(tagName)) { - isRobot = true; - } else if (Tag.isBrowserTag(tagName)) { - isBrowser = true; - } else if (Tag.isOperatingSystemTag(tagName)) { - isOperatingSystem = true; - } else if (Tag.isBrowserTypeTag(tagName)) { - isBrowserType = true; - } else if (Tag.isBrowserPatternTag(tagName)) { - isBrowserPattern = true; - } else if (Tag.isBrowserOsMappingTag(tagName)) { - isBrowserOsMapping = true; - } else if (Tag.isOperatingSystemPatternTag(tagName)) { - isOperatingSystemPattern = true; - } else if (Tag.isDeviceTag(tagName)) { - isDevice = true; - } else if (Tag.isDevicePatternTag(tagName)) { - isDevicePattern = true; - } - - currentTag = Tag.evaluate(tagName); - } - - /** - * Transfers all characters of a specific tag to the corresponding builder and resets the string buffer. - */ - private void transferToSpecificBuilderAndReset() { - - // version - if (currentTag == Tag.VERSION) { - dataBuilder.setVersion(buffer.toString()); - } - - // robot browser - addToRobotBuilder(); - - // build browser - addToBrowserBuilder(); - - // build operating system - addToOperatingSystemBuilder(); - - // build browser pattern - addToBrowserPatternBuilder(); - - // build browser type - addToBrowserTypeBuilder(); - - // build browser to operating system mapping - addToBrowserOsMappingBuilder(); - - // build operating system pattern - addToOperatingSystemPatternBuilder(); - - // build browser - addToDeviceBuilder(); - - // build browser pattern - addToDevicePatternBuilder(); - - buffer = new StringBuilder(); - } - - @Override - public void warning(final SAXParseException e) throws SAXException { - warning = true; - logParsingIssue("Warning", e); - super.warning(e); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/Browser.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/Browser.java deleted file mode 100644 index 80154db..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/Browser.java +++ /dev/null @@ -1,512 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data.domain; - -import java.io.Serializable; -import java.util.Collections; -import java.util.SortedSet; -import java.util.TreeSet; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.NotThreadSafe; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.UserAgent; -import net.sf.uadetector.UserAgentFamily; - -@Immutable -public final class Browser implements Identifiable, Serializable { - - @NotThreadSafe - public static final class Builder { - - private static final String EMPTY = ""; - - @Nonnull - private UserAgentFamily family = UserAgentFamily.UNKNOWN; - - @Nonnull - private String familyName = EMPTY; - - @Nonnull - private String icon = EMPTY; - - private int id = Integer.MIN_VALUE; - - @Nonnull - private String infoUrl = EMPTY; - - @Nullable - private OperatingSystem operatingSystem; - - @Nonnull - private SortedSet patterns = new TreeSet(); - - @Nonnull - private String producer = EMPTY; - - @Nonnull - private String producerUrl = EMPTY; - - @Nullable - private BrowserType type; - - private transient int typeId = Integer.MIN_VALUE; - - @Nonnull - private String url = EMPTY; - - public Builder() { - // default constructor - } - - public Builder(@Nonnull final Browser browser) { - Check.notNull(browser, "browser"); - id = Check.notNegative(browser.getId(), "browser.getId()"); - family = Check.notNull(browser.getFamily(), "browser.getFamily()"); - familyName = Check.notNull(browser.getFamilyName(), "browser.getFamilyName()"); - patterns = new TreeSet(Check.notNull(browser.getPatterns(), "browser.getPatterns()")); - type = Check.notNull(browser.getType(), "browser.getType()"); - operatingSystem = Check.notNull(browser.getOperatingSystem(), "browser.getOperatingSystem()"); - icon = Check.notNull(browser.getIcon(), "browser.getIcon()"); - infoUrl = Check.notNull(browser.getInfoUrl(), "browser.getInfoUrl()"); - producer = Check.notNull(browser.getProducer(), "browser.getProducer()"); - producerUrl = Check.notNull(browser.getProducerUrl(), "browser.getProducerUrl()"); - url = Check.notNull(browser.getUrl(), "browser.getUrl()"); - } - - /** - * Creates a new instance of a builder with the data of the passed builder. - * - * @param builder - * builder containing the data to be copied - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - protected Builder(@Nonnull final Builder builder) { - Check.notNull(builder, "builder"); - - family = builder.family; - familyName = builder.familyName; - icon = builder.icon; - id = builder.id; - infoUrl = builder.infoUrl; - operatingSystem = builder.operatingSystem; - patterns = builder.patterns; - producer = builder.producer; - producerUrl = builder.producerUrl; - type = builder.type; - typeId = builder.typeId; - url = builder.url; - } - - @Nonnull - public Browser build() { - return new Browser(id, family, familyName, patterns, type, operatingSystem, icon, infoUrl, producer, producerUrl, url); - } - - /** - * Creates a copy (with all its data) of the current builder. - * - * @return a new instance of the current builder, never {@code null} - */ - @Nonnull - public Builder copy() { - return new Builder(this); - } - - @Nonnull - public UserAgentFamily getFamily() { - return family; - } - - @Nonnull - public String getFamilyName() { - return familyName; - } - - @Nonnull - public String getIcon() { - return icon; - } - - public int getId() { - return id; - } - - @Nonnull - public String getInfoUrl() { - return infoUrl; - } - - @Nullable - public OperatingSystem getOperatingSystem() { - return operatingSystem; - } - - @Nonnull - public SortedSet getPatterns() { - return patterns; - } - - @Nonnull - public String getProducer() { - return producer; - } - - @Nonnull - public String getProducerUrl() { - return producerUrl; - } - - @Nullable - public BrowserType getType() { - return type; - } - - public int getTypeId() { - return typeId; - } - - @Nonnull - public String getUrl() { - return url; - } - - @Nonnull - private Builder setFamily(@Nonnull final UserAgentFamily family) { - this.family = Check.notNull(family, "family"); - return this; - } - - @Nonnull - public Builder setFamilyName(@Nonnull final String familyName) { - this.familyName = Check.notNull(familyName, "familyName"); - return setFamily(UserAgentFamily.evaluate(familyName)); - } - - @Nonnull - public Builder setIcon(@Nonnull final String icon) { - this.icon = Check.notNull(icon, "icon"); - return this; - } - - @Nonnull - public Builder setId(@Nonnegative final int id) { - this.id = Check.notNegative(id, "id"); - return this; - } - - @Nonnull - public Builder setId(@Nonnull final String id) { - setId(Integer.parseInt(Check.notEmpty(id.replace("\n","").replace("\t",""), "id"))); - return this; - } - - @Nonnull - public Builder setInfoUrl(@Nonnull final String infoUrl) { - this.infoUrl = Check.notNull(infoUrl, "infoUrl"); - return this; - } - - @Nonnull - public Builder setOperatingSystem(@Nonnull final OperatingSystem operatingSystem) { - this.operatingSystem = Check.notNull(operatingSystem, "operatingSystem"); - return this; - } - - @Nonnull - public Builder setPatterns(@Nonnull final SortedSet patterns) { - this.patterns = new TreeSet(Check.notNull(patterns, "patterns")); - return this; - } - - @Nonnull - public Builder setProducer(@Nonnull final String producer) { - this.producer = Check.notNull(producer, "producer"); - return this; - } - - @Nonnull - public Builder setProducerUrl(@Nonnull final String producerUrl) { - this.producerUrl = Check.notNull(producerUrl, "producerUrl"); - return this; - } - - @Nonnull - public Builder setType(@Nonnull final BrowserType type) { - this.type = Check.notNull(type, "type"); - setTypeId(type.getId()); - return this; - } - - @Nonnull - public Builder setTypeId(@Nonnegative final int typeId) { - this.typeId = Check.notNegative(typeId, "typeId"); - return this; - } - - @Nonnull - public Builder setTypeId(@Nonnull final String typeId) { - setTypeId(Integer.parseInt(Check.notEmpty(typeId.replace("\n","").replace("\t",""), "typeId"))); - return this; - } - - @Nonnull - public Builder setUrl(@Nonnull final String url) { - this.url = Check.notNull(url, "url"); - return this; - } - - } - - private static final long serialVersionUID = 6741143419664475577L; - - private static int buildHashCode(@Nonnegative final int id, @Nonnull final UserAgentFamily family, @Nonnull final String familyName, - @Nonnull final SortedSet patterns, @Nonnull final BrowserType type, - @Nullable final OperatingSystem operatingSystem, @Nonnull final String icon, @Nonnull final String infoUrl, - @Nonnull final String producer, @Nonnull final String producerUrl, @Nonnull final String url) { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + family.hashCode(); - result = prime * result + familyName.hashCode(); - result = prime * result + patterns.hashCode(); - result = prime * result + type.hashCode(); - result = prime * result + (operatingSystem == null ? 0 : operatingSystem.hashCode()); - result = prime * result + icon.hashCode(); - result = prime * result + infoUrl.hashCode(); - result = prime * result + producer.hashCode(); - result = prime * result + producerUrl.hashCode(); - result = prime * result + url.hashCode(); - return result; - } - - @Nonnull - private final UserAgentFamily family; - - @Nonnull - private final String familyName; - - private final int hash; - - @Nonnull - private final String icon; - - @Nonnegative - private final int id; - - @Nonnull - private final String infoUrl; - - @Nullable - private final OperatingSystem operatingSystem; - - @Nonnull - private final SortedSet patterns; - - @Nonnull - private final String producer; - - @Nonnull - private final String producerUrl; - - @Nonnull - private final BrowserType type; - - @Nonnull - private final String url; - - public Browser(@Nonnegative final int id, @Nonnull final UserAgentFamily family, @Nonnull final String familyName, - @Nonnull final SortedSet patterns, @Nonnull final BrowserType type, - @Nonnull final OperatingSystem operatingSystem, @Nonnull final String icon, @Nonnull final String infoUrl, - @Nonnull final String producer, @Nonnull final String producerUrl, @Nonnull final String url) { - this.id = Check.notNegative(id, "id"); - this.family = Check.notNull(family, "family"); - this.familyName = Check.notNull(familyName, "familyName"); - this.patterns = Collections.unmodifiableSortedSet(new TreeSet(Check.notNull(patterns, "patterns"))); - this.type = Check.notNull(type, "type"); - this.operatingSystem = operatingSystem; - this.icon = Check.notNull(icon, "icon"); - this.infoUrl = Check.notNull(infoUrl, "infoUrl"); - this.producer = Check.notNull(producer, "producer"); - this.producerUrl = Check.notNull(producerUrl, "producerUrl"); - this.url = Check.notNull(url, "url"); - hash = buildHashCode(id, family, familyName, patterns, type, operatingSystem, icon, infoUrl, producer, producerUrl, url); - } - - /** - * Copy values from itself to a UserAgentInfo.Builder. - */ - public void copyTo(@Nonnull final UserAgent.Builder builder) { - builder.setFamily(family); - builder.setIcon(icon); - builder.setName(familyName); - builder.setProducer(producer); - builder.setProducerUrl(producerUrl); - builder.setTypeName(type.getName()); - builder.setUrl(url); - if (operatingSystem != null) { - operatingSystem.copyTo(builder); - } - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final Browser other = (Browser) obj; - if (id != other.id) { - return false; - } - if (!family.equals(other.family)) { - return false; - } - if (!familyName.equals(other.familyName)) { - return false; - } - if (!patterns.equals(other.patterns)) { - return false; - } - if (!type.equals(other.type)) { - return false; - } - if (operatingSystem == null) { - if (other.operatingSystem != null) { - return false; - } - } else if (!operatingSystem.equals(other.operatingSystem)) { - return false; - } - if (!icon.equals(other.icon)) { - return false; - } - if (!infoUrl.equals(other.infoUrl)) { - return false; - } - if (!producer.equals(other.producer)) { - return false; - } - if (!producerUrl.equals(other.producerUrl)) { - return false; - } - if (!url.equals(other.url)) { - return false; - } - return true; - } - - @Nonnull - public UserAgentFamily getFamily() { - return family; - } - - @Nonnull - public String getFamilyName() { - return familyName; - } - - @Nonnull - public String getIcon() { - return icon; - } - - @Override - @Nonnegative - public int getId() { - return id; - } - - @Nonnull - public String getInfoUrl() { - return infoUrl; - } - - @Nullable - public OperatingSystem getOperatingSystem() { - return operatingSystem; - } - - @Nonnull - public SortedSet getPatterns() { - return patterns; - } - - @Nonnull - public String getProducer() { - return producer; - } - - @Nonnull - public String getProducerUrl() { - return producerUrl; - } - - @Nonnull - public BrowserType getType() { - return type; - } - - @Nonnull - public String getUrl() { - return url; - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("Browser [id="); - builder.append(id); - builder.append(", family="); - builder.append(family); - builder.append(", familyName="); - builder.append(familyName); - builder.append(", patterns="); - builder.append(patterns); - builder.append(", type="); - builder.append(type); - builder.append(", operatingSystem="); - builder.append(operatingSystem); - builder.append(", icon="); - builder.append(icon); - builder.append(", infoUrl="); - builder.append(infoUrl); - builder.append(", producer="); - builder.append(producer); - builder.append(", producerUrl="); - builder.append(producerUrl); - builder.append(", url="); - builder.append(url); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/BrowserOperatingSystemMapping.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/BrowserOperatingSystemMapping.java deleted file mode 100644 index 2ea44ae..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/BrowserOperatingSystemMapping.java +++ /dev/null @@ -1,230 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data.domain; - -import java.io.Serializable; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.NotThreadSafe; - -import net.sf.qualitycheck.Check; - -@Immutable -public final class BrowserOperatingSystemMapping implements Serializable { - - @NotThreadSafe - public static final class Builder { - - /** - * ID of a browser entry - */ - private int browserId = Integer.MIN_VALUE; - - /** - * ID of a operating system entry - */ - private int operatingSystemId = Integer.MIN_VALUE; - - /** - * Build an instance of {@code BrowserOperatingSystemMapping}. - * - * @return a new instance of {@code BrowserOperatingSystemMapping} - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if one of the needed arguments to build an instance of {@code BrowserOperatingSystemMapping} is - * invalid - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the needed arguments to build an instance of {@code BrowserOperatingSystemMapping} is - * invalid - */ - @Nonnull - public BrowserOperatingSystemMapping build() { - return new BrowserOperatingSystemMapping(browserId, operatingSystemId); - } - - /** - * Gets the identification number of a browser entry. - * - * @return identification number of a browser entry - */ - public int getBrowserId() { - return browserId; - } - - /** - * Gets the identification number of an operating system entry. - * - * @return identification number of an operating system entry - */ - public int getOperatingSystemId() { - return operatingSystemId; - } - - /** - * Sets the identification number of a browser entry. - * - * @param browserId - * identification number - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given number is smaller than {@code 0} - */ - @Nonnull - public Builder setBrowserId(@Nonnegative final int browserId) { - Check.notNegative(browserId, "browserId"); - - this.browserId = browserId; - return this; - } - - /** - * Sets the identification number of a browser entry via a string. - * - * @param browserId - * identification number (as a {@code String)} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws NumberFormatException - * if the string can not be interpreted as a number - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the interpreted number is smaller than {@code 0} - */ - @Nonnull - public Builder setBrowserId(@Nonnull final String browserId) { - Check.notNull(browserId, "browserId"); - - this.setBrowserId(Integer.parseInt(browserId.trim())); - return this; - } - - /** - * Sets the identification number of an operating system entry. - * - * @param operatingSystemId - * identification number - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given number is smaller than {@code 0} - */ - @Nonnull - public Builder setOperatingSystemId(@Nonnegative final int operatingSystemId) { - Check.notNegative(operatingSystemId, "operatingSystemId"); - - this.operatingSystemId = operatingSystemId; - return this; - } - - /** - * Sets the identification number of an operating system entry via a string. - * - * @param operatingSystemId - * identification number (as a {@code String)} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws NumberFormatException - * if the string can not be interpreted as a number - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the interpreted number is smaller than {@code 0} - */ - @Nonnull - public Builder setOperatingSystemId(@Nonnull final String operatingSystemId) { - Check.notNull(operatingSystemId, "operatingSystemId"); - - this.setOperatingSystemId(Integer.parseInt(operatingSystemId.trim())); - return this; - } - - } - - private static final long serialVersionUID = 6074931648810031757L; - - /** - * ID of a browser entry - */ - @Nonnegative - private final int browserId; - - /** - * ID of a operating system entry - */ - @Nonnegative - private final int operatingSystemId; - - /** - * Constructs an instance of {@code BrowserOperatingSystemMapping}. - * - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if one of the given arguments is smaller than {@code 0} - */ - public BrowserOperatingSystemMapping(@Nonnegative final int browserId, @Nonnegative final int operatingSystemId) { - Check.notNegative(browserId, "browserId"); - Check.notNegative(operatingSystemId, "operatingSystemId"); - - this.browserId = browserId; - this.operatingSystemId = operatingSystemId; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final BrowserOperatingSystemMapping other = (BrowserOperatingSystemMapping) obj; - if (browserId != other.browserId) { - return false; - } - if (operatingSystemId != other.operatingSystemId) { - return false; - } - return true; - } - - @Nonnegative - public int getBrowserId() { - return browserId; - } - - @Nonnegative - public int getOperatingSystemId() { - return operatingSystemId; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + browserId; - result = prime * result + operatingSystemId; - return result; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("BrowserOperatingSystemMapping [browserId="); - builder.append(browserId); - builder.append(", operatingSystemId="); - builder.append(operatingSystemId); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/BrowserPattern.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/BrowserPattern.java deleted file mode 100644 index c97a1a3..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/BrowserPattern.java +++ /dev/null @@ -1,337 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data.domain; - -import java.io.Serializable; -import java.util.regex.Pattern; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.NotThreadSafe; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.internal.util.RegularExpressionConverter; - -/** - * The {@code BrowserPattern} class represents the detection information for a browser specific item.
- *
- * A {@code BrowserPattern} object is immutable, their values cannot be changed after creation. - * - * @author André Rouél - */ -@Immutable -public final class BrowserPattern implements Identifiable, OrderedPattern, Serializable { - - /** - * Factory that creates instances of {@code BrowserPattern} via method calls. - * - * @author André Rouél - */ - @NotThreadSafe - public static final class Builder { - - /** - * Identification number (ID) of a browser pattern - */ - private int id = Integer.MIN_VALUE; - - /** - * A compiled representation of a regular expression to detect a browser - */ - private Pattern pattern; - - /** - * Position of a {@code BrowserPattern} (only relevant if there are multiple patterns for a browser in a - * {@code SortedSet}) - */ - private int position = Integer.MIN_VALUE; - - /** - * Builds a new instance of {@code BrowserPattern} and returns it. - * - * @return a new instance of {@code BrowserPattern} - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if one of the needed arguments to build an instance of {@code BrowserPattern} is invalid - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the needed arguments to build an instance of {@code BrowserPattern} is invalid - */ - @Nonnull - public BrowserPattern build() { - return new BrowserPattern(id, pattern, position); - } - - /** - * Sets the identification number of a browser pattern entry. - * - * @param id - * identification number - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given integer is smaller than {@code 0} - */ - @Nonnull - public Builder setId(@Nonnegative final int id) { - Check.notNegative(id, "id"); - - this.id = id; - return this; - } - - /** - * Sets the identification number (ID) of a browser pattern. The given {@code String} is parsed as a decimal - * number. - * - * @param id - * ID of a browser pattern as string - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws NumberFormatException - * if the given string is not parsable as integer - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the parsed integer is smaller than {@code 0} - */ - @Nonnull - public Builder setId(@Nonnull final String id) { - Check.notEmpty(id, "id"); - - this.setId(Integer.parseInt(id.trim())); - return this; - } - - /** - * Sets a regular expression for a browser pattern. - * - * @param pattern - * compiled representation of a regular expression - * @return this {@code Builder}, for chaining - */ - @Nonnull - public Builder setPattern(@Nonnull final Pattern pattern) { - Check.notNull(pattern, "pattern"); - - this.pattern = pattern; - return this; - } - - /** - * Converts a PERL regular expression in a Java regular expression and sets it in the {@code Builder}. - * - * @param regex - * PERL style regular expression to be converted - * @return this {@code Builder}, for chaining - */ - @Nonnull - public Builder setPerlRegularExpression(@Nonnull final String regex) { - Check.notEmpty(regex, "regex"); - - setPattern(RegularExpressionConverter.convertPerlRegexToPattern(regex)); - return this; - } - - /** - * Sets the position of a browser pattern in a set of patterns. - * - * @param position - * position of a browser pattern - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given integer is smaller than {@code 0} - */ - @Nonnull - public Builder setPosition(@Nonnegative final int position) { - Check.notNegative(position, "position"); - - this.position = position; - return this; - } - - /** - * Sets the position of a browser pattern in a set of patterns. The given {@code String} is parsed as a decimal - * number. - * - * @param position - * position of a browser pattern as string - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws NumberFormatException - * if the given string is not parsable as integer - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the parsed integer is smaller than {@code 0} - */ - @Nonnull - public Builder setPosition(@Nonnull final String position) { - Check.notEmpty(position, "position"); - - this.setPosition(Integer.parseInt(position.trim())); - return this; - } - - } - - private static final long serialVersionUID = 2845531314485836348L; - - /** - * Compares to integers. - * - * @param a - * first integer - * @param b - * second integer - * @return {@code -1} if {@code a} is less, {@code 0} if equal, or {@code 1} if greater than {@code b} - */ - private static int compareInt(final int a, final int b) { - int result = 0; - if (a > b) { - result = 1; - } else if (a < b) { - result = -1; - } - return result; - } - - /** - * Identification number (ID) of a browser pattern - */ - @Nonnegative - private final int id; - - /** - * A compiled representation of a regular expression to detect a browser - */ - @Nonnull - private final Pattern pattern; - - /** - * Position of a {@code BrowserPattern} (only relevant if there are multiple patterns for a browser in a - * {@code SortedSet}) - */ - @Nonnegative - private final int position; - - public BrowserPattern(@Nonnegative final int id, @Nonnull final Pattern pattern, @Nonnegative final int position) { - Check.notNegative(id, "id"); - Check.notNull(pattern, "pattern"); - Check.notNegative(position, "position"); - - this.id = id; - this.pattern = pattern; - this.position = position; - } - - /** - * Compares all attributes of this instance with the given instance of a {@code BrowserPattern}. - * - *

- * This method is consistent with equals. - * - * @param other - * another instance of {@code OperatingSystemPattern} - * @return negative value if one of the attributes of this instance is less, 0 if equal, or positive value if - * greater than the other one - */ - @Override - public int compareTo(final BrowserPattern other) { - int result = other == null ? -1 : 0; - if (result == 0) { - result = compareInt(getPosition(), other.getPosition()); - if (result == 0) { - result = compareInt(getId(), other.getId()); - } - if (result == 0) { - result = getPattern().pattern().compareTo(other.getPattern().pattern()); - } - if (result == 0) { - result = compareInt(getPattern().flags(), other.getPattern().flags()); - } - } - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final BrowserPattern other = (BrowserPattern) obj; - if (id != other.id) { - return false; - } - if (position != other.position) { - return false; - } - if (!pattern.pattern().equals(other.pattern.pattern())) { - return false; - } - if (pattern.flags() != other.pattern.flags()) { - return false; - } - return true; - } - - /** - * Gets the identification number (ID) of a browser pattern. - * - * @return identification number (ID) of a browser pattern - */ - @Override - public int getId() { - return id; - } - - @Override - public Pattern getPattern() { - return pattern; - } - - @Override - public int getPosition() { - return position; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + position; - result = prime * result + pattern.pattern().hashCode(); - result = prime * result + pattern.flags(); - return result; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("BrowserPattern [id="); - builder.append(id); - builder.append(", pattern="); - builder.append(pattern); - builder.append(", position="); - builder.append(position); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/BrowserType.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/BrowserType.java deleted file mode 100644 index ece8c7d..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/BrowserType.java +++ /dev/null @@ -1,182 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data.domain; - -import java.io.Serializable; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.NotThreadSafe; - -import net.sf.qualitycheck.Check; - -/** - * The {@code BrowserType} class represents the type of a browser.
- *
- * A {@code BrowserType} object is immutable, their values cannot be changed after creation. - * - * @author André Rouél - */ -@Immutable -public final class BrowserType implements Identifiable, Serializable { - - @NotThreadSafe - public static final class Builder { - - /** - * Identification number (ID) of a browser type entry - */ - private int id = Integer.MIN_VALUE; - - /** - * Name of a browser type entry - */ - private String name; - - /** - * Builds a new instance of {@code BrowserType} and returns it. - * - * @return a new instance of {@code BrowserType} - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if one of the needed arguments to build an instance of {@code BrowserType} is invalid - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the needed arguments to build an instance of {@code BrowserType} is invalid - */ - @Nonnull - public BrowserType build() { - return new BrowserType(id, name); - } - - /** - * Sets the identification number (ID). - * - * @param id - * ID of browser type - */ - @Nonnull - public Builder setId(@Nonnegative final int id) { - Check.notNegative(id, "id"); - - this.id = id; - return this; - } - - /** - * Sets the identification number via a string.
- *
- * An opening and closing Square brackets at the end of a string will be filtered. If the string can not be - * parsed as a long, a {@code NumberFormatException} will be thrown. - * - * @param id - * ID of browser type - */ - @Nonnull - public Builder setId(@Nonnull final String id) { - Check.notNull(id, "id"); - - this.setId(Integer.parseInt(id.trim())); - return this; - } - - /** - * Sets the name. - * - * @param name - * name of the browser type - */ - @Nonnull - public Builder setName(@Nonnull final String name) { - Check.notNull(name, "name"); - - this.name = name; - return this; - } - - } - - private static final long serialVersionUID = 2643535063309729806L; - - @Nonnegative - private final int id; - - @Nonnull - private final String name; - - public BrowserType(@Nonnegative final int id, @Nonnull final String name) { - Check.notNegative(id, "id"); - Check.notNull(name, "name"); - - this.id = id; - this.name = name; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final BrowserType other = (BrowserType) obj; - if (id != other.id) { - return false; - } - if (!name.equals(other.name)) { - return false; - } - return true; - } - - /** - * Gets the identification number (ID) of a browser type. - * - * @return identification number (ID) of a browser type - */ - @Override - public int getId() { - return id; - } - - public String getName() { - return name; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + name.hashCode(); - return result; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("BrowserType [id="); - builder.append(id); - builder.append(", name="); - builder.append(name); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/Device.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/Device.java deleted file mode 100644 index cd4cd47..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/Device.java +++ /dev/null @@ -1,275 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data.domain; - -import java.io.Serializable; -import java.util.Collections; -import java.util.SortedSet; -import java.util.TreeSet; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.NotThreadSafe; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.ReadableDeviceCategory.Category; - -@Immutable -public final class Device implements Identifiable, Serializable { - - @NotThreadSafe - public static final class Builder { - - private static final String EMPTY = ""; - - @Nonnull - private String icon = EMPTY; - - private int id = Integer.MIN_VALUE; - - @Nonnull - private String infoUrl = EMPTY; - - private String name; - - @Nonnull - private SortedSet patterns = new TreeSet(); - - public Builder() { - // default constructor - } - - /** - * Creates a new instance of a builder with the data of the passed builder. - * - * @param builder - * builder containing the data to be copied - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - private Builder(@Nonnull final Builder builder) { - Check.notNull(builder, "builder"); - - icon = builder.icon; - id = builder.id; - infoUrl = builder.infoUrl; - name = builder.name; - } - - public Builder(@Nonnull final Device device) { - Check.notNull(device, "device"); - icon = Check.notNull(device.getIcon(), "device.getIcon()"); - id = Check.notNegative(device.getId(), "device.getId()"); - infoUrl = Check.notNull(device.getInfoUrl(), "device.getInfoUrl()"); - name = Check.notNull(device.getName(), "device.getName()"); - patterns = new TreeSet(Check.notNull(device.getPatterns(), "device.getPatterns()")); - } - - @Nonnull - public Device build() { - return new Device(name, id, Category.evaluate(name), icon, infoUrl, patterns); - } - - /** - * Creates a copy (with all its data) of the current builder. - * - * @return a new instance of the current builder, never {@code null} - */ - @Nonnull - public Builder copy() { - return new Builder(this); - } - - public String getIcon() { - return icon; - } - - public int getId() { - return id; - } - - public String getInfoUrl() { - return infoUrl; - } - - public String getName() { - return name; - } - - public SortedSet getPatterns() { - return patterns; - } - - @Nonnull - public Builder setIcon(@Nonnull final String icon) { - this.icon = Check.notNull(icon, "icon"); - return this; - } - - @Nonnull - public Builder setId(@Nonnegative final int id) { - this.id = Check.notNegative(id, "id"); - return this; - } - - @Nonnull - public Builder setId(@Nonnull final String id) { - setId(Integer.parseInt(Check.notEmpty(id.replace("\n","").replace("\t",""), "id"))); - return this; - } - - @Nonnull - public Builder setInfoUrl(@Nonnull final String infoUrl) { - this.infoUrl = Check.notNull(infoUrl, "infoUrl"); - return this; - } - - @Nonnull - public Builder setName(@Nonnull final String name) { - this.name = Check.notNull(name, "name"); - return this; - } - - @Nonnull - public Builder setPatterns(@Nonnull final SortedSet patterns) { - this.patterns = new TreeSet(Check.notNull(patterns, "patterns")); - return this; - } - - } - - private static final long serialVersionUID = 1L; - - private static int buildHashCode(@Nonnull final Category category, @Nonnull final String icon, @Nonnegative final int id, - @Nonnull final String infoUrl, @Nonnull final String name, @Nonnull final SortedSet patterns) { - final int prime = 31; - int result = 1; - result = prime * result + category.hashCode(); - result = prime * result + icon.hashCode(); - result = prime * result + id; - result = prime * result + infoUrl.hashCode(); - result = prime * result + name.hashCode(); - result = prime * result + patterns.hashCode(); - return result; - } - - private final int hash; - - @Nonnull - private final String icon; - - @Nonnull - private final Category category; - - @Nonnegative - private final int id; - - @Nonnull - private final String infoUrl; - - @Nonnull - private final String name; - - @Nonnull - private final SortedSet patterns; - - public Device(@Nonnull final String name, @Nonnegative final int id, @Nonnull final Category category, @Nonnull final String icon, - @Nonnull final String infoUrl, @Nonnull final SortedSet patterns) { - this.category = category; - this.icon = Check.notNull(icon, "icon"); - this.id = Check.notNegative(id, "id"); - this.infoUrl = Check.notNull(infoUrl, "infoUrl"); - this.name = Check.notNull(name, "name"); - this.patterns = Collections.unmodifiableSortedSet(new TreeSet(Check.notNull(patterns, "patterns"))); - hash = buildHashCode(category, icon, id, infoUrl, name, patterns); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final Device other = (Device) obj; - if (category != other.category) { - return false; - } - if (!icon.equals(other.icon)) { - return false; - } - if (id != other.id) { - return false; - } - if (!infoUrl.equals(other.infoUrl)) { - return false; - } - if (!name.equals(other.name)) { - return false; - } - if (!patterns.equals(other.patterns)) { - return false; - } - return true; - } - - @Nonnull - public Category getCategory() { - return category; - } - - @Nonnull - public String getIcon() { - return icon; - } - - @Override - @Nonnegative - public int getId() { - return id; - } - - @Nonnull - public String getInfoUrl() { - return infoUrl; - } - - @Nonnull - public String getName() { - return name; - } - - @Nonnull - public SortedSet getPatterns() { - return patterns; - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public String toString() { - return "Device [icon=" + icon + ", id=" + id + ", infoUrl=" + infoUrl + ", name=" + name + ", patterns=" + patterns + "]"; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/DevicePattern.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/DevicePattern.java deleted file mode 100644 index ef4c1fe..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/DevicePattern.java +++ /dev/null @@ -1,337 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data.domain; - -import java.io.Serializable; -import java.util.regex.Pattern; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.NotThreadSafe; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.internal.util.RegularExpressionConverter; - -/** - * The {@code DevicePattern} class represents the detection information for a device specific item.
- *
- * A {@code DevicePattern} object is immutable, their values cannot be changed after creation. - * - * @author André Rouél - */ -@Immutable -public final class DevicePattern implements Identifiable, OrderedPattern, Serializable { - - /** - * Factory that creates instances of {@code DevicePattern} via method calls. - * - * @author André Rouél - */ - @NotThreadSafe - public static final class Builder { - - /** - * Identification number (ID) of a device pattern - */ - private int id = Integer.MIN_VALUE; - - /** - * A compiled representation of a regular expression to detect a device - */ - private Pattern pattern; - - /** - * Position of a {@code DevicePattern} (only relevant if there are multiple patterns for a device in a - * {@code SortedSet}) - */ - private int position = Integer.MIN_VALUE; - - /** - * Builds a new instance of {@code DevicePattern} and returns it. - * - * @return a new instance of {@code DevicePattern} - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if one of the needed arguments to build an instance of {@code DevicePattern} is invalid - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the needed arguments to build an instance of {@code DevicePattern} is invalid - */ - @Nonnull - public DevicePattern build() { - return new DevicePattern(id, pattern, position); - } - - /** - * Sets the identification number of a device pattern entry. - * - * @param id - * identification number - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given integer is smaller than {@code 0} - */ - @Nonnull - public Builder setId(@Nonnegative final int id) { - Check.notNegative(id, "id"); - - this.id = id; - return this; - } - - /** - * Sets the identification number (ID) of a device pattern. The given {@code String} is parsed as a decimal - * number. - * - * @param id - * ID of a device pattern as string - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws NumberFormatException - * if the given string is not parsable as integer - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the parsed integer is smaller than {@code 0} - */ - @Nonnull - public Builder setId(@Nonnull final String id) { - Check.notEmpty(id, "id"); - - this.setId(Integer.parseInt(id.trim())); - return this; - } - - /** - * Sets a regular expression for a device pattern. - * - * @param pattern - * compiled representation of a regular expression - * @return this {@code Builder}, for chaining - */ - @Nonnull - public Builder setPattern(@Nonnull final Pattern pattern) { - Check.notNull(pattern, "pattern"); - - this.pattern = pattern; - return this; - } - - /** - * Converts a PERL regular expression in a Java regular expression and sets it in the {@code Builder}. - * - * @param regex - * PERL style regular expression to be converted - * @return this {@code Builder}, for chaining - */ - @Nonnull - public Builder setPerlRegularExpression(@Nonnull final String regex) { - Check.notEmpty(regex, "regex"); - - setPattern(RegularExpressionConverter.convertPerlRegexToPattern(regex)); - return this; - } - - /** - * Sets the position of a device pattern in a set of patterns. - * - * @param position - * position of a device pattern - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given integer is smaller than {@code 0} - */ - @Nonnull - public Builder setPosition(@Nonnegative final int position) { - Check.notNegative(position, "position"); - - this.position = position; - return this; - } - - /** - * Sets the position of a device pattern in a set of patterns. The given {@code String} is parsed as a decimal - * number. - * - * @param position - * position of a device pattern as string - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws NumberFormatException - * if the given string is not parsable as integer - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the parsed integer is smaller than {@code 0} - */ - @Nonnull - public Builder setPosition(@Nonnull final String position) { - Check.notEmpty(position, "position"); - - this.setPosition(Integer.parseInt(position.trim())); - return this; - } - - } - - private static final long serialVersionUID = 2845531314485836348L; - - /** - * Compares to integers. - * - * @param a - * first integer - * @param b - * second integer - * @return {@code -1} if {@code a} is less, {@code 0} if equal, or {@code 1} if greater than {@code b} - */ - private static int compareInt(final int a, final int b) { - int result = 0; - if (a > b) { - result = 1; - } else if (a < b) { - result = -1; - } - return result; - } - - /** - * Identification number (ID) of a device pattern - */ - @Nonnegative - private final int id; - - /** - * A compiled representation of a regular expression to detect a device - */ - @Nonnull - private final Pattern pattern; - - /** - * Position of a {@code DevicePattern} (only relevant if there are multiple patterns for a device in a - * {@code SortedSet}) - */ - @Nonnegative - private final int position; - - public DevicePattern(@Nonnegative final int id, @Nonnull final Pattern pattern, @Nonnegative final int position) { - Check.notNegative(id, "id"); - Check.notNull(pattern, "pattern"); - Check.notNegative(position, "position"); - - this.id = id; - this.pattern = pattern; - this.position = position; - } - - /** - * Compares all attributes of this instance with the given instance of a {@code DevicePattern}. - * - *

- * This method is consistent with equals. - * - * @param other - * another instance of {@code OperatingSystemPattern} - * @return negative value if one of the attributes of this instance is less, 0 if equal, or positive value if - * greater than the other one - */ - @Override - public int compareTo(final DevicePattern other) { - int result = other == null ? -1 : 0; - if (result == 0) { - result = compareInt(getPosition(), other.getPosition()); - if (result == 0) { - result = compareInt(getId(), other.getId()); - } - if (result == 0) { - result = getPattern().pattern().compareTo(other.getPattern().pattern()); - } - if (result == 0) { - result = compareInt(getPattern().flags(), other.getPattern().flags()); - } - } - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final DevicePattern other = (DevicePattern) obj; - if (id != other.id) { - return false; - } - if (position != other.position) { - return false; - } - if (!pattern.pattern().equals(other.pattern.pattern())) { - return false; - } - if (pattern.flags() != other.pattern.flags()) { - return false; - } - return true; - } - - /** - * Gets the identification number (ID) of a device pattern. - * - * @return identification number (ID) of a device pattern - */ - @Override - public int getId() { - return id; - } - - @Override - public Pattern getPattern() { - return pattern; - } - - @Override - public int getPosition() { - return position; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + position; - result = prime * result + pattern.pattern().hashCode(); - result = prime * result + pattern.flags(); - return result; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("DevicePattern [id="); - builder.append(id); - builder.append(", pattern="); - builder.append(pattern); - builder.append(", position="); - builder.append(position); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/Identifiable.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/Identifiable.java deleted file mode 100644 index fc4bbae..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/Identifiable.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.sf.uadetector.internal.data.domain; - -/** - * Defines domain objects that have a numeric identifier (ID). - * - * @author André Rouél - */ -public interface Identifiable { - - /** - * Returns the identifier (ID) of an instance. - * - * @return numeric identifier - */ - int getId(); - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/OperatingSystem.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/OperatingSystem.java deleted file mode 100644 index 4806399..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/OperatingSystem.java +++ /dev/null @@ -1,429 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data.domain; - -import java.io.Serializable; -import java.util.Collections; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.NotThreadSafe; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.OperatingSystemFamily; -import net.sf.uadetector.UserAgent; -import net.sf.uadetector.VersionNumber; - -@Immutable -public final class OperatingSystem implements Identifiable, Serializable { - - @NotThreadSafe - public static final class Builder { - - @Nonnull - private String family = ""; - - @Nonnull - private String icon = ""; - - private int id = Integer.MIN_VALUE; - - @Nonnull - private String infoUrl = ""; - - @Nonnull - private String name = ""; - - @Nonnull - private SortedSet patterns = new TreeSet(); - - @Nonnull - private String producer = ""; - - @Nonnull - private String producerUrl = ""; - - @Nonnull - private String url = ""; - - public Builder() { - // default constructor - } - - /** - * Creates a new instance of a builder with the data of the passed builder. - * - * @param builder - * builder containing the data to be copied - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - protected Builder(@Nonnull final Builder builder) { - Check.notNull(builder, "builder"); - - family = builder.family; - icon = builder.icon; - id = builder.id; - infoUrl = builder.infoUrl; - name = builder.name; - patterns.addAll(builder.patterns); - producer = builder.producer; - producerUrl = builder.producerUrl; - url = builder.url; - } - - public Builder(@Nonnull final OperatingSystem operatingSystem) { - Check.notNull(operatingSystem, "operatingSystem"); - id = Check.notNegative(operatingSystem.getId(), "operatingSystem.getId()"); - name = Check.notNull(operatingSystem.getName(), "operatingSystem.getName()"); - family = Check.notNull(operatingSystem.getFamily(), "operatingSystem.getFamily()"); - infoUrl = Check.notNull(operatingSystem.getInfoUrl(), "operatingSystem.getInfoUrl()"); - patterns = new TreeSet(Check.notNull(operatingSystem.getPatterns(), "operatingSystem.getPatterns()")); - producer = Check.notNull(operatingSystem.getProducer(), "operatingSystem.getProducer()"); - producerUrl = Check.notNull(operatingSystem.getProducerUrl(), "operatingSystem.getProducerUrl()"); - url = Check.notNull(operatingSystem.getUrl(), "operatingSystem.getUrl()"); - icon = Check.notNull(operatingSystem.getIcon(), "operatingSystem.getIcon()"); - } - - @Nonnull - public Builder addPatterns(@Nonnull final Set patterns) { - Check.notNull(patterns, "patterns"); - - this.patterns.addAll(patterns); - return this; - } - - @Nonnull - public OperatingSystem build() { - return new OperatingSystem(id, name, family, infoUrl, patterns, producer, producerUrl, url, icon); - } - - /** - * Creates a copy (with all its data) of the current builder. - * - * @return a new instance of the current builder, never {@code null} - */ - @Nonnull - public OperatingSystem.Builder copy() { - return new Builder(this); - } - - @Nonnull - public String getFamily() { - return family; - } - - @Nonnull - public String getIcon() { - return icon; - } - - public int getId() { - return id; - } - - @Nonnull - public String getInfoUrl() { - return infoUrl; - } - - @Nonnull - public String getName() { - return name; - } - - @Nonnull - public SortedSet getPatterns() { - return patterns; - } - - @Nonnull - public String getProducer() { - return producer; - } - - @Nonnull - public String getProducerUrl() { - return producerUrl; - } - - @Nonnull - public String getUrl() { - return url; - } - - @Nonnull - public Builder setFamily(@Nonnull final String family) { - this.family = Check.notNull(family, "family"); - return this; - } - - @Nonnull - public Builder setIcon(@Nonnull final String icon) { - this.icon = Check.notNull(icon, "icon"); - return this; - } - - @Nonnull - public Builder setId(@Nonnegative final int id) { - this.id = Check.notNegative(id, "id"); - return this; - } - - @Nonnull - public Builder setId(@Nonnull final String id) { - Check.notEmpty(id, "id"); - - this.setId(Integer.parseInt(id.trim())); - return this; - } - - @Nonnull - public Builder setInfoUrl(@Nonnull final String infoUrl) { - this.infoUrl = Check.notNull(infoUrl, "infoUrl"); - return this; - } - - @Nonnull - public Builder setName(@Nonnull final String name) { - this.name = Check.notNull(name, "name"); - return this; - } - - @Nonnull - public Builder setPatterns(@Nonnull final SortedSet patterns) { - this.patterns = new TreeSet(Check.notNull(patterns, "patterns")); - return this; - } - - @Nonnull - public Builder setProducer(@Nonnull final String producer) { - this.producer = Check.notNull(producer, "producer"); - return this; - } - - @Nonnull - public Builder setProducerUrl(@Nonnull final String producerUrl) { - this.producerUrl = Check.notNull(producerUrl, "producerUrl"); - return this; - } - - @Nonnull - public Builder setUrl(@Nonnull final String url) { - this.url = Check.notNull(url, "url"); - return this; - } - - } - - private static final long serialVersionUID = -5330180544816352323L; - - private static int buildHashCode(@Nonnegative final int id, @Nonnull final String name, @Nonnull final String family, - @Nonnull final String infoUrl, @Nonnull final SortedSet patterns, @Nonnull final String producer, - @Nonnull final String producerUrl, @Nonnull final String url, @Nonnull final String icon) { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + name.hashCode(); - result = prime * result + family.hashCode(); - result = prime * result + infoUrl.hashCode(); - result = prime * result + patterns.hashCode(); - result = prime * result + producer.hashCode(); - result = prime * result + producerUrl.hashCode(); - result = prime * result + url.hashCode(); - result = prime * result + icon.hashCode(); - return result; - } - - @Nonnull - private final String family; - - private final int hash; - - @Nonnull - private final String icon; - - @Nonnegative - private final int id; - - @Nonnull - private final String infoUrl; - - @Nonnull - private final String name; - - @Nonnull - private final SortedSet patterns; - - @Nonnull - private final String producer; - - @Nonnull - private final String producerUrl; - - @Nonnull - private final String url; - - public OperatingSystem(@Nonnegative final int id, @Nonnull final String name, @Nonnull final String family, - @Nonnull final String infoUrl, @Nonnull final SortedSet patterns, @Nonnull final String producer, - @Nonnull final String producerUrl, @Nonnull final String url, @Nonnull final String icon) { - this.id = Check.notNegative(id, "id"); - this.name = Check.notNull(name, "name"); - this.family = Check.notNull(family, "family"); - this.infoUrl = Check.notNull(infoUrl, "infoUrl"); - this.patterns = Collections.unmodifiableSortedSet(new TreeSet(Check.notNull(patterns, "patterns"))); - this.producer = Check.notNull(producer, "producer"); - this.producerUrl = Check.notNull(producerUrl, "producerUrl"); - this.url = Check.notNull(url, "url"); - this.icon = Check.notNull(icon, "icon"); - hash = buildHashCode(id, name, family, infoUrl, patterns, producer, producerUrl, url, icon); - } - - /** - * Copies all information of the current operating system entry to the given user agent builder. - * - * @param builder - * user agent builder - */ - public void copyTo(@Nonnull final UserAgent.Builder builder) { - final OperatingSystemFamily f = OperatingSystemFamily.evaluate(family); - final VersionNumber version = VersionNumber.parseOperatingSystemVersion(f, builder.getUserAgentString()); - builder.setOperatingSystem(new net.sf.uadetector.OperatingSystem(f, family, icon, name, producer, producerUrl, url, version)); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final OperatingSystem other = (OperatingSystem) obj; - if (id != other.id) { - return false; - } - if (!name.equals(other.name)) { - return false; - } - if (!family.equals(other.family)) { - return false; - } - if (!infoUrl.equals(other.infoUrl)) { - return false; - } - if (!patterns.equals(other.patterns)) { - return false; - } - if (!producer.equals(other.producer)) { - return false; - } - if (!producerUrl.equals(other.producerUrl)) { - return false; - } - if (!url.equals(other.url)) { - return false; - } - if (!icon.equals(other.icon)) { - return false; - } - return true; - } - - @Nonnull - public String getFamily() { - return family; - } - - @Nonnull - public String getIcon() { - return icon; - } - - @Override - @Nonnegative - public int getId() { - return id; - } - - @Nonnull - public String getInfoUrl() { - return infoUrl; - } - - @Nonnull - public String getName() { - return name; - } - - @Nonnull - public SortedSet getPatterns() { - return patterns; - } - - @Nonnull - public String getProducer() { - return producer; - } - - @Nonnull - public String getProducerUrl() { - return producerUrl; - } - - @Nonnull - public String getUrl() { - return url; - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("OperatingSystem [id="); - builder.append(id); - builder.append(", name="); - builder.append(name); - builder.append(", family="); - builder.append(family); - builder.append(", infoUrl="); - builder.append(infoUrl); - builder.append(", patterns="); - builder.append(patterns); - builder.append(", producer="); - builder.append(producer); - builder.append(", producerUrl="); - builder.append(producerUrl); - builder.append(", url="); - builder.append(url); - builder.append(", icon="); - builder.append(icon); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/OperatingSystemPattern.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/OperatingSystemPattern.java deleted file mode 100644 index 59ef091..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/OperatingSystemPattern.java +++ /dev/null @@ -1,312 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data.domain; - -import java.io.Serializable; -import java.util.regex.Pattern; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.NotThreadSafe; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.internal.util.CompareNullSafe; -import net.sf.uadetector.internal.util.RegularExpressionConverter; - -/** - * The {@code OperatingSystemPattern} class represents the detection information for a specific operating system item.
- *
- * A {@code OperatingSystemPattern} object is immutable, their values cannot be changed after creation. - * - * @author André Rouél - */ -@Immutable -public final class OperatingSystemPattern implements Identifiable, OrderedPattern, Serializable { - - /** - * Factory that creates instances of {@code OperatingSystemPattern} via method calls. - * - * @author André Rouél - */ - @NotThreadSafe - public static final class Builder { - - /** - * Identification number (ID) of an operating system pattern - */ - private int id = Integer.MIN_VALUE; - - /** - * A compiled representation of a regular expression to detect an operating system - */ - private Pattern pattern; - - /** - * Position of a {@code OperatingSystemPattern} (only relevant if there are multiple patterns for an operating - * system in a {@code SortedSet}) - */ - private int position = Integer.MIN_VALUE; - - /** - * Builds a new instance of {@code OperatingSystemPattern} and returns it. - * - * @return a new instance of {@code OperatingSystemPattern} - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if one of the needed arguments to build an instance of {@code OperatingSystemPattern} is invalid - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the needed arguments to build an instance of {@code OperatingSystemPattern} is invalid - */ - public OperatingSystemPattern build() { - return new OperatingSystemPattern(id, pattern, position); - } - - /** - * Sets the identification number of an operating system pattern entry. - * - * @param id - * identification number - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given integer is smaller than {@code 0} - */ - public Builder setId(final int id) { - Check.notNegative(id, "id"); - - this.id = id; - return this; - } - - /** - * Sets the identification number (ID) of an operating system pattern. The given {@code String} is parsed as a - * decimal number. - * - * @param id - * ID of an operating system pattern as string - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws NumberFormatException - * if the given string is not parsable as integer - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the parsed integer is smaller than {@code 0} - */ - public Builder setId(@Nonnull final String id) { - Check.notEmpty(id, "id"); - - this.setId(Integer.parseInt(id.trim())); - return this; - } - - /** - * Sets a regular expression for an operating system pattern. - * - * @param pattern - * compiled representation of a regular expression - * @return this {@code Builder}, for chaining - */ - public Builder setPattern(@Nonnull final Pattern pattern) { - Check.notNull(pattern, "pattern"); - - this.pattern = pattern; - return this; - } - - /** - * Converts a PERL regular expression in a Java regular expression and sets it in the {@code Builder}. - * - * @param regex - * PERL style regular expression to be converted - * @return this {@code Builder}, for chaining - */ - public Builder setPerlRegularExpression(@Nonnull final String regex) { - Check.notEmpty(regex, "regex"); - - setPattern(RegularExpressionConverter.convertPerlRegexToPattern(regex)); - return this; - } - - /** - * Sets the position of an operating system pattern in a set of patterns. - * - * @param position - * position of an operating system pattern - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given integer is smaller than {@code 0} - */ - public Builder setPosition(@Nonnegative final int position) { - Check.notNegative(position, "position"); - - this.position = position; - return this; - } - - /** - * Sets the position of an operating system pattern in a set of patterns. The given {@code String} is parsed as - * a decimal number. - * - * @param position - * position of an operating system pattern as string - * @return this {@code Builder}, for chaining - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws NumberFormatException - * if the given string is not parsable as integer - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the parsed integer is smaller than {@code 0} - */ - public Builder setPosition(@Nonnull final String position) { - Check.notEmpty(position, "position"); - - this.setPosition(Integer.parseInt(position.trim())); - return this; - } - - } - - private static final long serialVersionUID = 1583732916568404647L; - - /** - * Identification number (ID) of an operating system pattern - */ - @Nonnegative - private final int id; - - /** - * A compiled representation of a regular expression to detect an operating system - */ - @Nonnull - private final Pattern pattern; - - /** - * Position of a {@code OperatingSystemPattern} (only relevant if there are multiple patterns for an operating - * system in a {@code SortedSet}) - */ - @Nonnegative - private final int position; - - public OperatingSystemPattern(@Nonnegative final int id, @Nonnull final Pattern pattern, @Nonnegative final int position) { - Check.notNegative(id, "id"); - Check.notNull(pattern, "pattern"); - Check.notNegative(position, "position"); - - this.id = id; - this.pattern = pattern; - this.position = position; - } - - /** - * Compares all attributes of this instance with the given instance of a {@code OperatingSystemPattern}. - * - *

- * This method is consistent with equals. - * - * @param other - * another instance of {@code OperatingSystemPattern} - * @return negative value if one of the attributes of this instance is less, 0 if equal, or positive value if - * greater than the other one - */ - @Override - public int compareTo(final OperatingSystemPattern other) { - int result = other == null ? -1 : 0; - if (result == 0) { - result = CompareNullSafe.compareInt(getPosition(), other.getPosition()); - if (result == 0) { - result = CompareNullSafe.compareInt(getId(), other.getId()); - } - if (result == 0) { - result = getPattern().pattern().compareTo(other.getPattern().pattern()); - } - if (result == 0) { - result = CompareNullSafe.compareInt(getPattern().flags(), other.getPattern().flags()); - } - } - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final OperatingSystemPattern other = (OperatingSystemPattern) obj; - if (id != other.id) { - return false; - } - if (position != other.position) { - return false; - } - if (!pattern.pattern().equals(other.pattern.pattern())) { - return false; - } - if (pattern.flags() != other.pattern.flags()) { - return false; - } - return true; - } - - /** - * Gets the identification number (ID) of an operating system pattern. - * - * @return identification number (ID) of an operating system pattern - */ - @Override - public int getId() { - return id; - } - - @Override - public Pattern getPattern() { - return pattern; - } - - @Override - public int getPosition() { - return position; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + position; - result = prime * result + pattern.pattern().hashCode(); - result = prime * result + pattern.flags(); - return result; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("OperatingSystemPattern [id="); - builder.append(id); - builder.append(", pattern="); - builder.append(pattern); - builder.append(", position="); - builder.append(position); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/OrderedPattern.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/OrderedPattern.java deleted file mode 100644 index ffedd13..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/OrderedPattern.java +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data.domain; - -import java.util.regex.Pattern; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; - -public interface OrderedPattern> extends Comparable { - - /** - * Gets a compiled representation of a regular expression. - * - * @return compiled representation of a regular expression - */ - @Nonnull - Pattern getPattern(); - - /** - * Gets the position of a browser pattern in a set of patterns. - * - * @return position of a browser pattern - */ - @Nonnegative - int getPosition(); - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/Robot.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/Robot.java deleted file mode 100644 index c8db5c3..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/Robot.java +++ /dev/null @@ -1,339 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.data.domain; - -import java.io.Serializable; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import javax.annotation.concurrent.Immutable; -import javax.annotation.concurrent.NotThreadSafe; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.UserAgent; -import net.sf.uadetector.UserAgentFamily; -import net.sf.uadetector.UserAgentType; - -@Immutable -public final class Robot implements Identifiable, Serializable { - - @NotThreadSafe - public static final class Builder { - - private static final String EMPTY = ""; - - @Nonnull - private UserAgentFamily family = UserAgentFamily.UNKNOWN; - - @Nonnull - private String familyName = EMPTY; - - @Nonnull - private String icon = EMPTY; - - private int id = Integer.MIN_VALUE; - - @Nonnull - private String infoUrl = EMPTY; - - @Nonnull - private String name = EMPTY; - - @Nonnull - private String producer = EMPTY; - - @Nonnull - private String producerUrl = EMPTY; - - @Nonnull - private String userAgentString = EMPTY; - - public Builder() { - // default constructor - } - - public Builder(@Nonnull final Robot robot) { - Check.notNull(robot, "robot"); - id = Check.notNegative(robot.getId(), "robot.getId()"); - name = Check.notNull(robot.getName(), "robot.getName()"); - family = Check.notNull(robot.getFamily(), "robot.getFamily()"); - familyName = Check.notNull(robot.getFamilyName(), "robot.getFamilyName()"); - infoUrl = Check.notNull(robot.getInfoUrl(), "robot.getInfoUrl()"); - producer = Check.notNull(robot.getProducer(), "robot.getProducer()"); - producerUrl = Check.notNull(robot.getProducerUrl(), "robot.getProducerUrl()"); - userAgentString = Check.notNull(robot.getUserAgentString(), "robot.getUserAgentString()"); - icon = Check.notNull(robot.getIcon(), "robot.getIcon()"); - } - - @Nonnull - public Robot build() { - return new Robot(id, name, family, familyName, infoUrl, producer, producerUrl, userAgentString, icon); - } - - @Nonnull - public Builder setFamilyName(@Nonnull final String familyName) { - this.familyName = Check.notNull(familyName, "familyName"); - family = UserAgentFamily.evaluate(familyName); - return this; - } - - @Nonnull - public Builder setIcon(@Nonnull final String icon) { - this.icon = Check.notNull(icon, "icon"); - return this; - } - - @Nonnull - public Builder setId(@Nonnegative final int id) { - this.id = Check.notNegative(id, "id"); - return this; - } - - @Nonnull - public Builder setId(@Nonnull final String id) { - this.id = Integer.parseInt(Check.notEmpty(id.replace("\n","").replace("\t",""), "id")); - return this; - } - - @Nonnull - public Builder setInfoUrl(@Nonnull final String infoUrl) { - this.infoUrl = Check.notNull(infoUrl, "infoUrl"); - return this; - } - - @Nonnull - public Builder setName(@Nonnull final String name) { - this.name = Check.notNull(name, "name"); - return this; - } - - @Nonnull - public Builder setProducer(@Nonnull final String producer) { - this.producer = Check.notNull(producer, "producer"); - return this; - } - - @Nonnull - public Builder setProducerUrl(@Nonnull final String producerUrl) { - this.producerUrl = Check.notNull(producerUrl, "producerUrl"); - return this; - } - - @Nonnull - public Builder setUserAgentString(@Nonnull final String userAgentString) { - this.userAgentString = Check.notNull(userAgentString, "userAgentString"); - return this; - } - - } - - private static final long serialVersionUID = -605392434061575985L; - - /** - * Default type name to support the classification against corresponding enum later - */ - public static final String TYPENAME = "Robot"; - - private static int buildHashCode(@Nonnegative final int id, @Nonnull final String name, @Nonnull final UserAgentFamily family, - @Nonnull final String familyName, @Nonnull final String infoUrl, @Nonnull final String producer, - @Nonnull final String producerUrl, @Nonnull final String userAgentString, @Nonnull final String icon) { - final int prime = 31; - int result = 1; - result = prime * result + id; - result = prime * result + name.hashCode(); - result = prime * result + family.hashCode(); - result = prime * result + familyName.hashCode(); - result = prime * result + infoUrl.hashCode(); - result = prime * result + producer.hashCode(); - result = prime * result + producerUrl.hashCode(); - result = prime * result + userAgentString.hashCode(); - result = prime * result + icon.hashCode(); - return result; - } - - @Nonnull - private final UserAgentFamily family; - - @Nonnull - private final String familyName; - - private final int hash; - - @Nonnull - private final String icon; - - @Nonnegative - private final int id; - - @Nonnull - private final String infoUrl; - - @Nonnull - private final String name; - - @Nonnull - private final String producer; - - @Nonnull - private final String producerUrl; - - @Nonnull - private final String userAgentString; - - public Robot(@Nonnegative final int id, @Nonnull final String name, @Nonnull final UserAgentFamily family, - @Nonnull final String familyName, @Nonnull final String infoUrl, @Nonnull final String producer, - @Nonnull final String producerUrl, @Nonnull final String userAgentString, @Nonnull final String icon) { - this.id = Check.notNegative(id, "id"); - this.name = Check.notNull(name, "name"); - this.family = Check.notNull(family, "family"); - this.familyName = Check.notNull(familyName, "familyName"); - this.infoUrl = Check.notNull(infoUrl, "infoUrl"); - this.producer = Check.notNull(producer, "producer"); - this.producerUrl = Check.notNull(producerUrl, "producerUrl"); - this.userAgentString = Check.notNull(userAgentString, "userAgentString"); - this.icon = Check.notNull(icon, "icon"); - hash = buildHashCode(id, name, family, familyName, infoUrl, producer, producerUrl, userAgentString, icon); - } - - public void copyTo(@Nonnull final UserAgent.Builder builder) { - builder.setFamily(family); - builder.setIcon(icon); - builder.setName(name); - builder.setProducer(producer); - builder.setProducerUrl(producerUrl); - builder.setUrl(infoUrl); - builder.setType(UserAgentType.ROBOT); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final Robot other = (Robot) obj; - if (id != other.id) { - return false; - } - if (!name.equals(other.name)) { - return false; - } - if (!family.equals(other.family)) { - return false; - } - if (!familyName.equals(other.familyName)) { - return false; - } - if (!infoUrl.equals(other.infoUrl)) { - return false; - } - if (!producer.equals(other.producer)) { - return false; - } - if (!producerUrl.equals(other.producerUrl)) { - return false; - } - if (!userAgentString.equals(other.userAgentString)) { - return false; - } - if (!icon.equals(other.icon)) { - return false; - } - return true; - } - - @Nonnull - public UserAgentFamily getFamily() { - return family; - } - - @Nonnull - public String getFamilyName() { - return familyName; - } - - @Nonnull - public String getIcon() { - return icon; - } - - @Override - @Nonnegative - public int getId() { - return id; - } - - @Nonnull - public String getInfoUrl() { - return infoUrl; - } - - @Nonnull - public String getName() { - return name; - } - - @Nonnull - public String getProducer() { - return producer; - } - - @Nonnull - public String getProducerUrl() { - return producerUrl; - } - - @Nonnull - public String getUserAgentString() { - return userAgentString; - } - - @Override - public int hashCode() { - return hash; - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("ReadableRobot [id="); - builder.append(id); - builder.append(", name="); - builder.append(name); - builder.append(", family="); - builder.append(family); - builder.append(", familyName="); - builder.append(familyName); - builder.append(", infoUrl="); - builder.append(infoUrl); - builder.append(", producer="); - builder.append(producer); - builder.append(", producerUrl="); - builder.append(producerUrl); - builder.append(", userAgentString="); - builder.append(userAgentString); - builder.append(", icon="); - builder.append(icon); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/data/domain/package-info.java b/app/src/main/java/net/sf/uadetector/internal/data/domain/package-info.java deleted file mode 100644 index 9bc7be3..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/domain/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -@javax.annotation.ParametersAreNonnullByDefault -package net.sf.uadetector.internal.data.domain; diff --git a/app/src/main/java/net/sf/uadetector/internal/data/package-info.java b/app/src/main/java/net/sf/uadetector/internal/data/package-info.java deleted file mode 100644 index 1195df9..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/data/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -@javax.annotation.ParametersAreNonnullByDefault -package net.sf.uadetector.internal.data; diff --git a/app/src/main/java/net/sf/uadetector/internal/util/AlphanumComparator.java b/app/src/main/java/net/sf/uadetector/internal/util/AlphanumComparator.java deleted file mode 100644 index 9923674..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/util/AlphanumComparator.java +++ /dev/null @@ -1,150 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.util; - -/* - * The Alphanum Algorithm is an improved sorting algorithm for strings - * containing numbers. Instead of sorting numbers in ASCII order like - * a standard sort, this algorithm sorts numbers in numeric order. - * - * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -import java.io.Serializable; -import java.util.Comparator; - -/** - * This is an updated version with enhancements made by Daniel Migowski, Andre Bogus, and David Koelle - * - * To convert to use Templates (Java 1.5+): - Change "implements Comparator" to "implements Comparator" - Change - * "compare(Object o1, Object o2)" to "compare(String s1, String s2)" - Remove the type checking and casting in - * compare(). - * - * To use this class: Use the static "sort" method from the java.util.Collections class: Collections.sort(your list, new - * AlphanumComparator()); - */ -public final class AlphanumComparator implements Comparator, Serializable { - - private static final long serialVersionUID = 5727692755534146935L; - - protected static int compareDigits(final String s1, final String s2) { - int thisMarker = 0; - int thatMarker = 0; - final int s1Length = s1.length(); - final int s2Length = s2.length(); - - while (thisMarker < s1Length && thatMarker < s2Length) { - final String thisChunk = getChunk(s1, s1Length, thisMarker); - thisMarker += thisChunk.length(); - - final String thatChunk = getChunk(s2, s2Length, thatMarker); - thatMarker += thatChunk.length(); - - // If both chunks contain numeric characters, sort them numerically - int result = 0; - if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) { - // Simple chunk comparison by length. - final int thisChunkLength = thisChunk.length(); - result = thisChunkLength - thatChunk.length(); - // If equal, the first different number counts - if (result == 0) { - for (int i = 0; i < thisChunkLength; i++) { - result = thisChunk.charAt(i) - thatChunk.charAt(i); - if (result != 0) { - return result; - } - } - } - } else { - result = thisChunk.compareTo(thatChunk); - } - - if (result != 0) { - return result; - } - } - - return s1Length - s2Length; - } - - /** - * Length of string is passed in for improved efficiency (only need to calculate it once) - */ - private static String getChunk(final String s, final int slength, int marker) { - final StringBuilder chunk = new StringBuilder(); - char c = s.charAt(marker); - chunk.append(c); - marker++; - if (isDigit(c)) { - while (marker < slength) { - c = s.charAt(marker); - if (!isDigit(c)) { - break; - } - chunk.append(c); - marker++; - } - } else { - while (marker < slength) { - c = s.charAt(marker); - if (isDigit(c)) { - break; - } - chunk.append(c); - marker++; - } - } - return chunk.toString(); - } - - private static boolean isDigit(final char ch) { - return ch >= 48 && ch <= 57; - } - - @Override - public int compare(final String a, final String b) { - int result = 0; - if (a == null) { - if (b != null) { - result = -1; - } - } else { - if (b == null) { - result = 1; - } else { - result = compareDigits(a, b); - } - } - return result; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/util/Closeables.java b/app/src/main/java/net/sf/uadetector/internal/util/Closeables.java deleted file mode 100644 index a6e7e12..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/util/Closeables.java +++ /dev/null @@ -1,100 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.util; - -import java.io.Closeable; -import java.io.IOException; - -import javax.annotation.Nullable; - -import net.sf.uadetector.exception.CannotCloseException; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class is intended to provide utility methods to close {@link Closeable} instances. - * - * @author André Rouél - */ -public final class Closeables { - - private static final Logger LOG = LoggerFactory.getLogger(Closeables.class); - - /** - * Closes a {@link Closeable} and swallows an occurring {@link IOException} if argument {@code swallowIOException} - * is {@code true}, otherwise the {@code IOException} will be thrown. - *

- * This method does nothing if a null reference is passed as {@code closeable}. - * - * @param closeable - * the {@code Closeable} object to be closed, or {@code null} - * @param swallowIOException - * {@code true} if an occurring {@code IOException} should be swallowed and logged or {@code false} to - * throw it - * - * @throws IOException - * if the given {@code closeable} cannot be closed - */ - public static void close(@Nullable final Closeable closeable, final boolean swallowIOException) throws IOException { - if (closeable != null) { - try { - closeable.close(); - } catch (final IOException e) { - if (!swallowIOException) { - throw e; - } - LOG.warn(e.getLocalizedMessage(), e); - } - } - } - - /** - * Closes a {@link Closeable} and swallows an occurring {@link IOException} if argument {@code swallowIOException} - * is {@code true}, otherwise the {@code IOException} will be converted into a runtime exception. - *

- * This method does nothing if a null reference is passed as {@code closeable}. - * - * @param closeable - * the {@code Closeable} object to be closed, or {@code null} - * @param swallowIOException - * {@code true} if an occurring {@code IOException} should be swallowed and logged or {@code false} to - * throw a {@code CannotCloseException} - * - * @throws CannotCloseException - * if the given {@code closeable} cannot be closed - */ - public static void closeAndConvert(@Nullable final Closeable closeable, final boolean swallowIOException) { - if (closeable != null) { - try { - closeable.close(); - } catch (final IOException e) { - if (!swallowIOException) { - throw new CannotCloseException(e.getLocalizedMessage(), e); - } - LOG.warn(e.getLocalizedMessage(), e); - } - } - } - - /** - * Attention: This class is not intended to create objects from it. - */ - private Closeables() { - // This class is not intended to create objects from it. - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/util/CompareNullSafe.java b/app/src/main/java/net/sf/uadetector/internal/util/CompareNullSafe.java deleted file mode 100644 index a6d0177..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/util/CompareNullSafe.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.util; - -import java.io.Serializable; -import java.util.Comparator; - -/** - * Compares two references to each other and {@code null} is assumed to be less than a non-{@code null} value. This - * class provides the first check for null safe comparison. - * - * @author André Rouél - */ -public abstract class CompareNullSafe implements Comparator, Serializable { - - private static final long serialVersionUID = -704997500621650775L; - - /** - * Compares to integers. - * - * @param a - * first integer - * @param b - * second integer - * @return {@code -1} if {@code a} is less, {@code 0} if equal, or {@code 1} if greater than {@code b} - */ - public static int compareInt(final int a, final int b) { - return a > b ? 1 : a == b ? 0 : -1; - } - - /** - * Compares two objects null safe to each other. - * - * @param o1 - * the first reference - * @param o2 - * the second reference - * @return a negative value if o1 < o2, zero if o1 = o2 and a positive value if o1 > o2 - */ - @Override - public int compare(final T o1, final T o2) { - int result = 0; - if (o1 == null) { - if (o2 != null) { - result = -1; - } - } else if (o2 == null) { - result = 1; - } else { - result = compareType(o1, o2); - } - return result; - } - - public abstract int compareType(final T o1, final T o2); - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/util/DaemonThreadFactory.java b/app/src/main/java/net/sf/uadetector/internal/util/DaemonThreadFactory.java deleted file mode 100644 index a42329d..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/util/DaemonThreadFactory.java +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.util; - -import java.util.concurrent.ThreadFactory; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; - -/** - * Factory to create daemon threads that runs as a background process and do not blocks an application shutdown - */ -public final class DaemonThreadFactory implements ThreadFactory { - - /** - * Name of a new thread - */ - @Nonnull - private final String threadName; - - /** - * Creates a new {@code DaemonThreadFactory} which creates itself threads with the specified name. - * - * @param threadName - * name of a thread to be created - */ - public DaemonThreadFactory(@Nonnull final String threadName) { - Check.notNull(threadName, "threadName"); - Check.notEmpty(threadName.trim(), "threadName"); - this.threadName = threadName; - } - - @Override - public Thread newThread(@Nonnull final Runnable runnable) { - final Thread thread = new Thread(runnable); - thread.setName(threadName); - thread.setDaemon(true); - return thread; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/util/ExecutorServices.java b/app/src/main/java/net/sf/uadetector/internal/util/ExecutorServices.java deleted file mode 100644 index 91cbbf8..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/util/ExecutorServices.java +++ /dev/null @@ -1,182 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.util; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This utility is intended to provide predefined {@link ExecutorService}s which runs in background and can be easily - * shut-downed within {@link #shutdownAll()} if necessary. - * - * @author André Rouél - */ -public final class ExecutorServices { - - /** - * This synchronized {@link Set} is a registry of all distributed background executors by this utility. - *

- * The containing {@link ExecutorService}s will be used to run a concrete update of UAS data instantly in - * background. - */ - private static final Set BACKGROUND_EXECUTORS = Collections.synchronizedSet(new HashSet(3)); - - /** - * Default name of a thread which will be used to run a concrete update within a background executor - */ - private static final String DEFAULT_BACKGROUND_EXECUTOR_NAME = "update-operation"; - - /** - * Default name of a thread which will be created within a scheduler - */ - private static final String DEFAULT_SCHEDULER_NAME = "update-scheduler"; - - /** - * Corresponding logger for this class - */ - private static final Logger LOG = LoggerFactory.getLogger(ExecutorServices.class); - - /** - * This synchronized {@link Set} is a registry of all distributed schedulers by this utility. - *

- * The containing {@link ScheduledExecutorService}s will be used to schedule commands which updates the UAS - * data in defined intervals. - */ - private static final Set SCHEDULERS = Collections.synchronizedSet(new HashSet(3)); - - /** - * Timeout (in seconds) to shutdown all available executors at the latest - */ - public static final long SHUTDOWN_DURATION = 5; - - /** - * Creates a single-threaded executor that is registered by this class in order to shut it down later (when it - * becomes necessary). - * - * @return a new background executor - */ - public static ExecutorService createBackgroundExecutor() { - final ExecutorService executor = Executors.newSingleThreadExecutor(new DaemonThreadFactory(DEFAULT_BACKGROUND_EXECUTOR_NAME)); - BACKGROUND_EXECUTORS.add(executor); - return executor; - } - - /** - * Creates a single-threaded scheduler that is registered by this class in order to shut it down later (when it - * becomes necessary). - * - * @return a new scheduler - */ - public static ScheduledExecutorService createScheduler() { - final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new DaemonThreadFactory(DEFAULT_SCHEDULER_NAME)); - SCHEDULERS.add(scheduler); - return scheduler; - } - - /** - * Shutdowns the given {@code ExecutorService} as soon as possible, but not later than the specified default time - * (which is {@value #SHUTDOWN_DURATION} seconds). - * - * @param executorService - * executor to stop - */ - public static void shutdown(@Nonnull final ExecutorService executorService) { - Check.notNull(executorService, "executorService"); - shutdown(executorService, SHUTDOWN_DURATION, TimeUnit.SECONDS); - } - - /** - * Shutdowns the given {@code ExecutorService} as soon as possible, but not later than the specified time. - * - * @param executorService - * executor to stop - * @param duration - * duration as a numerical value - * @param unit - * duration unit - */ - public static void shutdown(@Nonnull final ExecutorService executorService, @Nonnegative final long duration, - @Nonnull final TimeUnit unit) { - Check.notNull(executorService, "executorService"); - Check.notNull(duration, "duration"); - Check.notNull(unit, "unit"); - - executorService.shutdown(); - try { - if (!executorService.awaitTermination(duration, unit)) { - LOG.info(String.format("Executor did not terminate in %s %s.", duration, unit.name().toLowerCase())); - final List droppedTasks = executorService.shutdownNow(); - LOG.info("Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed."); - } - unregisterIfPossible(executorService); - } catch (final InterruptedException e) { - LOG.warn("Executor termination failed: " + e.getLocalizedMessage(), e); - } - } - - /** - * Shuts down all registered scheduler and background workers as soon as possible, but at the latest in specified - * {@link #SHUTDOWN_DURATION} seconds. - */ - public static void shutdownAll() { - for (final ExecutorService executor : new ArrayList(BACKGROUND_EXECUTORS)) { - shutdown(executor); - BACKGROUND_EXECUTORS.remove(executor); - } - for (final ScheduledExecutorService scheduler : new ArrayList(SCHEDULERS)) { - shutdown(scheduler); - SCHEDULERS.remove(scheduler); - } - } - - /** - * Unregisters the given {@code ExecutorService} if it is an instance of {@code ScheduledExecutorService} from the - * list of registered schedulers. - * - * @param executorService - * a possible scheduler - */ - private static void unregisterIfPossible(final ExecutorService executorService) { - if (executorService instanceof ScheduledExecutorService) { - SCHEDULERS.remove(executorService); - } else { - BACKGROUND_EXECUTORS.remove(executorService); - } - } - - /** - * Attention: This class is not intended to create objects from it. - */ - private ExecutorServices() { - // This class is not intended to create objects from it. - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/util/FileUtil.java b/app/src/main/java/net/sf/uadetector/internal/util/FileUtil.java deleted file mode 100644 index 7d51fc4..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/util/FileUtil.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.util; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.Charset; - -/** - * This class is intended to provide file utility functions. - * - * @author André Rouél - */ -public final class FileUtil { - - /** - * Checks if the given file is empty. - * - * @param file - * the file that could be empty - * @return {@code true} when the file is accessible and empty otherwise {@code false} - * @throws IOException - * if an I/O error occurs - */ - public static boolean isEmpty(final File file, final Charset charset) throws IOException { - boolean empty = false; - BufferedReader reader = null; - boolean threw = true; - try { - reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), charset)); - final String line = reader.readLine(); - empty = line == null; - threw = false; - } finally { - Closeables.close(reader, threw); - } - return empty; - } - - /** - * Attention: This class is not intended to create objects from it. - */ - private FileUtil() { - // This class is not intended to create objects from it. - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/util/RegularExpressionConverter.java b/app/src/main/java/net/sf/uadetector/internal/util/RegularExpressionConverter.java deleted file mode 100644 index a123c91..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/util/RegularExpressionConverter.java +++ /dev/null @@ -1,325 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.util; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; - -public final class RegularExpressionConverter { - - public enum Flag { - - /** - * Enables canonical equivalence. - */ - CANON_EQ(Pattern.CANON_EQ, 'c'), - - /** - * Enables case-insensitive matching. - */ - CASE_INSENSITIVE(Pattern.CASE_INSENSITIVE, 'i'), - - /** - * Permits whitespace and comments in pattern. - */ - COMMENTS(Pattern.COMMENTS, 'x'), - - /** - * Enables dotall mode. - */ - DOTALL(Pattern.DOTALL, 's'), - - /** - * Enables literal parsing of the pattern. - */ - LITERAL(Pattern.LITERAL, 'l'), - - /** - * Enables multiline mode. - */ - MULTILINE(Pattern.MULTILINE, 'm'), - - /** - * Enables Unicode-aware case folding. - */ - UNICODE_CASE(Pattern.UNICODE_CASE, 'u'), - - /** - * Enables Unix lines mode. - */ - UNIX_LINES(Pattern.UNIX_LINES, 'e'); - - private static class FlagByCharacterComparator extends CompareNullSafe { - private static final long serialVersionUID = 1L; - - @Override - public int compareType(@Nonnull final Flag f1, @Nonnull final Flag f2) { - final Character c1 = Character.valueOf(f1.getCharacter()); - final Character c2 = Character.valueOf(f2.getCharacter()); - return c1.compareTo(c2); - } - } - - private static final FlagByCharacterComparator FLAG_COMPARATOR = new FlagByCharacterComparator(); - - /** - * Converts a set of flags as to a bitmask (sum of numerical values). - * - * @param flags - * a set of flags - * @return sum of numerical values of passed flags or 0 - */ - public static int convertToBitmask(@Nonnull final Collection flags) { - Check.notNull(flags, "flags"); - - int bitmask = 0; - for (final Flag flag : flags) { - bitmask = bitmask | flag.getNumber(); - } - return bitmask; - } - - /** - * Converts a set of flags as to a string representation. The flags {@link Flag#CASE_INSENSITIVE}, - * {@link Flag#DOTALL}, {@link Flag#MULTILINE} and {@link Flag#COMMENTS} are identical to the PERL regular - * expression modifiers. - * - * @param flags - * a set of flags - * @return sum of numerical values of passed flags or 0 - */ - public static String convertToModifiers(@Nonnull final Collection flags) { - Check.notNull(flags, "flags"); - - final StringBuilder modifiers = new StringBuilder(8); - final Set sortedFlags = new TreeSet(Collections.reverseOrder(FLAG_COMPARATOR)); - sortedFlags.addAll(flags); - for (final Flag flag : sortedFlags) { - modifiers.append(flag.getCharacter()); - } - return modifiers.toString(); - } - - /** - * This method try to find a matching enum value by the given character representation. The character will be - * evaluated against the stored character of a flag. - * - * @param flag - * representation of a flag as a character - * @return the matching enum value or {@code null} - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given number is smaller than zero - */ - public static Flag evaluateByCharacter(final char flag) { - Check.notNegative(flag, "flag"); - Flag result = null; - for (final Flag value : values()) { - if (value.getCharacter() == flag) { - result = value; - break; - } - } - return result; - } - - /** - * This method try to find a matching enum value by the given numerical representation. The number will be - * evaluated against the stored number of a flag. - * - * @param flag - * representation of a flag as a character - * @return the matching enum value or {@code null} - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given number is smaller than zero - */ - public static Flag evaluateByNumber(final int flag) { - Check.notNegative(flag, "flag"); - Flag result = null; - for (final Flag value : values()) { - if (value.getNumber() == flag) { - result = value; - break; - } - } - return result; - } - - /** - * Parses a sum of flags as numerical values (bitmask) and translates it to set of enum values. - * - * @param bitmask - * Sum of numerical values of flags - * @return a set of flags - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given number is smaller than zero - */ - @Nonnull - public static Set parse(@Nonnegative final int bitmask) { - Check.notNegative(bitmask, "bitmask"); - - final Set flags = new HashSet(); - for (final Flag flag : values()) { - if ((bitmask & flag.getNumber()) != 0) { - flags.add(flag); - } - } - return flags; - } - - /** - * Translates PERL style modifiers to a set of {@code Pattern} compatible ones. - * - * @param modifiers - * modifiers as string of a PERL style regular expression - * @return a set of modifier flags that may include CASE_INSENSITIVE, MULTILINE, DOTALL and COMMENTS - */ - public static Set parse(@Nonnull final String modifiers) { - Check.notNull(modifiers, "modifiers"); - - final Set flags = new HashSet(); - for (int i = 0; i < modifiers.length(); i++) { - final Flag flag = Flag.evaluateByCharacter(modifiers.charAt(i)); - if (flag != null) { - flags.add(flag); - } - } - return flags; - } - - /** - * Representation of a flag as a character - */ - private final char character; - - /** - * Representation of a flag as a number - */ - private final int number; - - private Flag(final int value, final char character) { - number = value; - this.character = character; - } - - /** - * Returns this flag as character representation. - * - * @return representation as a character - */ - public char getCharacter() { - return character; - } - - /** - * Returns this flag as numerical representation. - * - * @return representation as a number - */ - public int getNumber() { - return number; - } - - } - - /** - * Template to support the conversion into a PERL style regular expression - */ - private static final String PATTERN_TO_REGEX_TEMPLATE = "/%s/%s"; - - /** - * Pattern for PERL style regular expression strings - */ - private static final Pattern PERL_STYLE = Pattern.compile("^/.*/((i|m|s|x)*)?$"); - - /** - * Pattern for PERL style regular expression strings with more fault-tolerance to the modifiers - */ - private static final Pattern PERL_STYLE_TOLERANT = Pattern.compile("^/.*/(([A-z])*)?$"); - - /** - * Converts a given {@code Pattern} into a PERL style regular expression. - * - * @param pattern - * regular expression pattern - * @return PERL style regular expression as string - */ - public static String convertPatternToPerlRegex(@Nonnull final Pattern pattern) { - Check.notNull(pattern, "pattern"); - final String modifiers = Flag.convertToModifiers(Flag.parse(pattern.flags())); - return String.format(PATTERN_TO_REGEX_TEMPLATE, pattern.pattern(), modifiers); - } - - /** - * Converts a PERL style regular expression into Java style.
- *
- * The leading and ending slash and the modifiers will be removed. The modifiers will be translated into equivalents - * flags of java.util.Pattern. If there are modifiers that are not valid an exception will be thrown. - * - * @param regex - * A PERL style regular expression - * @return Pattern - */ - public static Pattern convertPerlRegexToPattern(@Nonnull final String regex) { - return convertPerlRegexToPattern(regex, false); - } - - /** - * Converts a PERL style regular expression into Java style.
- *
- * The leading and ending slash and the modifiers will be removed. - * - * @param regex - * A PERL style regular expression - * @param faultTolerant - * Fault-tolerant translating the flags - * @return Pattern - */ - public static Pattern convertPerlRegexToPattern(@Nonnull final String regex, @Nonnull final boolean faultTolerant) { - Check.notNull(regex, "regex"); - - String pattern = regex.trim(); - final Matcher matcher = faultTolerant ? PERL_STYLE_TOLERANT.matcher(pattern) : PERL_STYLE.matcher(pattern); - if (!matcher.matches()) { - throw new IllegalArgumentException("The given regular expression '" + pattern - + "' seems to be not in PERL style or has unsupported modifiers."); - } - - pattern = pattern.substring(1); - final int lastIndex = pattern.lastIndexOf('/'); - pattern = pattern.substring(0, lastIndex); - - final int flags = Flag.convertToBitmask(Flag.parse(matcher.group(1))); - return Pattern.compile(pattern, flags); - } - - /** - * Attention: This class is not intended to create objects from it. - */ - private RegularExpressionConverter() { - // This class is not intended to create objects from it. - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/util/UrlUtil.java b/app/src/main/java/net/sf/uadetector/internal/util/UrlUtil.java deleted file mode 100644 index d202c8d..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/util/UrlUtil.java +++ /dev/null @@ -1,171 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.internal.util; - -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.MalformedURLException; -import java.net.URL; -import java.nio.charset.Charset; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; -import net.sf.qualitycheck.exception.IllegalStateOfArgumentException; -import net.sf.uadetector.exception.CanNotOpenStreamException; - -/** - * This class is intended to provide URL utility functions that encapsulate the checked exceptions like - * {@link MalformedURLException} during the construction of an URL or the {@link IOException} while opening a stream to - * an {@code URL}. - * - * @author André Rouél - */ -public final class UrlUtil { - - /** - * Creates an {@code URL} instance from the given {@code String} representation.
- *
- * This method tunnels a {@link MalformedURLException} by an {@link IllegalStateOfArgumentException}. - * - * @param url - * {@code String} representation of an {@code URL} - * @return new {@code URL} instance - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws IllegalStateOfArgumentException - * if the string representation of the given URL is invalid and a {@link MalformedURLException} occurs - */ - public static URL build(@Nonnull final String url) { - Check.notNull(url, "url"); - - URL ret = null; - try { - ret = new URL(url); - } catch (final MalformedURLException e) { - throw new IllegalStateOfArgumentException("The given string is not a valid URL: " + url, e); - } - return ret; - } - - /** - * Tries to open an {@link InputStream} to the given {@link URL}. - * - * @param url - * URL which should be opened - * @return opened stream - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - * @throws CanNotOpenStreamException - * if no stream to the given {@code URL} can be established - */ - public static InputStream open(@Nonnull final URL url) { - Check.notNull(url, "url"); - - final InputStream ret; - try { - ret = url.openStream(); - } catch (final IOException e) { - throw new CanNotOpenStreamException(url.toString(), e); - } - return ret; - } - - /** - * Reads the content of the passed {@link URL} as string representation. - * - * @param url - * URL to UAS data - * @param charset - * the character set in which the data should be read - * @return content as {@code String} - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if any of the given arguments is {@code null} - * @throws CanNotOpenStreamException - * if no stream to the given {@code URL} can be established - * @throws IOException - * if an I/O error occurs - */ - public static String read(@Nonnull final URL url, @Nonnull final Charset charset) throws IOException { - Check.notNull(url, "url"); - Check.notNull(charset, "charset"); - - final InputStream inputStream = open(url); - BufferedReader reader = null; - final StringBuilder buffer = new StringBuilder(); - boolean threw = true; - try { - reader = new BufferedReader(new InputStreamReader(inputStream, charset)); - buffer.append(readAll(reader)); - threw = false; - } finally { - Closeables.close(reader, threw); - Closeables.close(inputStream, false); - } - return buffer.toString(); - } - - /** - * Reads the entire contents via the given {@link Reader} as string. - * - * @param reader - * {@code Reader} to read the entire contents - * @return the read contents as string - * @throws IOException - * If an I/O error occurs - */ - private static String readAll(@Nonnull final Reader reader) throws IOException { - final StringBuilder buffer = new StringBuilder(); - int cp; - while ((cp = reader.read()) != -1) { - buffer.append((char) cp); - } - return buffer.toString(); - } - - /** - * Gets the URL to a given {@code File}. - * - * @param file - * file to be converted to a URL - * @return an URL to the passed file - * @throws IllegalStateException - * if no URL can be resolved to the given file - */ - public static URL toUrl(@Nonnull final File file) { - Check.notNull(file, "file"); - - URL url = null; - try { - url = file.toURI().toURL(); - } catch (final MalformedURLException e) { - throw new IllegalStateException("Can not construct an URL for passed file.", e); - } - return url; - } - - /** - * Attention: This class is not intended to create objects from it. - */ - private UrlUtil() { - // This class is not intended to create objects from it. - } - -} diff --git a/app/src/main/java/net/sf/uadetector/internal/util/package-info.java b/app/src/main/java/net/sf/uadetector/internal/util/package-info.java deleted file mode 100644 index 4ae3dfd..0000000 --- a/app/src/main/java/net/sf/uadetector/internal/util/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -@javax.annotation.ParametersAreNonnullByDefault -package net.sf.uadetector.internal.util; diff --git a/app/src/main/java/net/sf/uadetector/package-info.java b/app/src/main/java/net/sf/uadetector/package-info.java deleted file mode 100644 index 1ac3790..0000000 --- a/app/src/main/java/net/sf/uadetector/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -@javax.annotation.ParametersAreNonnullByDefault -package net.sf.uadetector; diff --git a/app/src/main/java/net/sf/uadetector/parser/AbstractUserAgentStringParser.java b/app/src/main/java/net/sf/uadetector/parser/AbstractUserAgentStringParser.java deleted file mode 100644 index 21f2328..0000000 --- a/app/src/main/java/net/sf/uadetector/parser/AbstractUserAgentStringParser.java +++ /dev/null @@ -1,216 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.parser; - -import java.util.Map.Entry; -import java.util.regex.Matcher; - -import javax.annotation.Nonnull; - -import net.sf.uadetector.DeviceCategory; -import net.sf.uadetector.ReadableDeviceCategory.Category; -import net.sf.uadetector.UserAgent; -import net.sf.uadetector.UserAgentStringParser; -import net.sf.uadetector.UserAgentType; -import net.sf.uadetector.VersionNumber; -import net.sf.uadetector.datastore.DataStore; -import net.sf.uadetector.internal.data.Data; -import net.sf.uadetector.internal.data.domain.Browser; -import net.sf.uadetector.internal.data.domain.BrowserPattern; -import net.sf.uadetector.internal.data.domain.Device; -import net.sf.uadetector.internal.data.domain.DevicePattern; -import net.sf.uadetector.internal.data.domain.OperatingSystem; -import net.sf.uadetector.internal.data.domain.OperatingSystemPattern; -import net.sf.uadetector.internal.data.domain.Robot; - -public abstract class AbstractUserAgentStringParser implements UserAgentStringParser { - - /** - * The number of capturing groups if nothing matches - */ - private static final int ZERO_MATCHING_GROUPS = 0; - - /** - * Examines the user agent string whether it is a browser. - * - * @param userAgent - * String of an user agent - * @param builder - * Builder for an user agent information - */ - private static void examineAsBrowser(final UserAgent.Builder builder, final Data data) { - Matcher matcher; - VersionNumber version = VersionNumber.UNKNOWN; - for (final Entry entry : data.getPatternToBrowserMap().entrySet()) { - matcher = entry.getKey().getPattern().matcher(builder.getUserAgentString()); - if (matcher.find()) { - - entry.getValue().copyTo(builder); - - // try to get the browser version from the first subgroup - if (matcher.groupCount() > ZERO_MATCHING_GROUPS) { - version = VersionNumber.parseVersion(matcher.group(1) != null ? matcher.group(1) : ""); - } - builder.setVersionNumber(version); - - break; - } - } - } - - /** - * Examines the user agent string whether it is a robot. - * - * @param userAgent - * String of an user agent - * @param builder - * Builder for an user agent information - * @return {@code true} if it is a robot, otherwise {@code false} - */ - private static boolean examineAsRobot(final UserAgent.Builder builder, final Data data) { - boolean isRobot = false; - VersionNumber version; - for (final Robot robot : data.getRobots()) { - if (robot.getUserAgentString().equals(builder.getUserAgentString())) { - isRobot = true; - robot.copyTo(builder); - - // try to get the version from the last found group - version = VersionNumber.parseLastVersionNumber(robot.getName()); - builder.setVersionNumber(version); - - break; - } - } - return isRobot; - } - - /** - * Examines the user agent string whether has a specific device category. - * - * @param userAgent - * String of an user agent - * @param builder - * Builder for an user agent information - */ - private static void examineDeviceCategory(final UserAgent.Builder builder, final Data data) { - - // a robot will be classified as 'Other' - if (UserAgentType.ROBOT == builder.getType()) { - final DeviceCategory category = findDeviceCategoryByValue(Category.OTHER, data); - builder.setDeviceCategory(category); - return; - } - - // classification depends on matching order - for (final Entry entry : data.getPatternToDeviceMap().entrySet()) { - final Matcher matcher = entry.getKey().getPattern().matcher(builder.getUserAgentString()); - if (matcher.find()) { - final Category category = Category.evaluate(entry.getValue().getName()); - final DeviceCategory deviceCategory = findDeviceCategoryByValue(category, data); - builder.setDeviceCategory(deviceCategory); - return; - } - } - - // an unknown user agent type should lead to an unknown device - if (UserAgentType.UNKNOWN == builder.getType()) { - builder.setDeviceCategory(DeviceCategory.EMPTY); - return; - } - - // if no pattern is available but the type is Other, Library, Validator or UA Anonymizer - // than classify it as 'Other' - if (UserAgentType.OTHER == builder.getType() || UserAgentType.LIBRARY == builder.getType() - || UserAgentType.VALIDATOR == builder.getType() || UserAgentType.USERAGENT_ANONYMIZER == builder.getType()) { - final DeviceCategory category = findDeviceCategoryByValue(Category.OTHER, data); - builder.setDeviceCategory(category); - return; - } - - // if no pattern is available but the type is a mobile or WAP browser than classify it as 'Smartphone' - if (UserAgentType.MOBILE_BROWSER == builder.getType() || UserAgentType.WAP_BROWSER == builder.getType()) { - final DeviceCategory category = findDeviceCategoryByValue(Category.SMARTPHONE, data); - builder.setDeviceCategory(category); - return; - } - - final DeviceCategory category = findDeviceCategoryByValue(Category.PERSONAL_COMPUTER, data); - builder.setDeviceCategory(category); - } - - /** - * Examines the operating system of the user agent string, if not available. - * - * @param userAgent - * String of an user agent - * @param builder - * Builder for an user agent information - */ - private static void examineOperatingSystem(final UserAgent.Builder builder, final Data data) { - if (net.sf.uadetector.OperatingSystem.EMPTY.equals(builder.getOperatingSystem())) { - for (final Entry entry : data.getPatternToOperatingSystemMap().entrySet()) { - final Matcher matcher = entry.getKey().getPattern().matcher(builder.getUserAgentString()); - if (matcher.find()) { - entry.getValue().copyTo(builder); - break; - } - } - } - } - - private static DeviceCategory findDeviceCategoryByValue(@Nonnull final Category category, @Nonnull final Data data) { - for (final Device device : data.getDevices()) { - if (category == device.getCategory()) { - return new DeviceCategory(category, device.getIcon(), device.getInfoUrl(), device.getName()); - } - } - return DeviceCategory.EMPTY; - } - - /** - * Gets the data store of this parser. - * - * @return data store of this parser - */ - protected abstract DataStore getDataStore(); - - @Override - public String getDataVersion() { - return getDataStore().getData().getVersion(); - } - - @Override - public UserAgent parse(final String userAgent) { - final UserAgent.Builder builder = new UserAgent.Builder(userAgent); - - // work during the analysis always with the same reference of data - final Data data = getDataStore().getData(); - - if (!examineAsRobot(builder, data)) { - examineAsBrowser(builder, data); - examineOperatingSystem(builder, data); - } - examineDeviceCategory(builder, data); - return builder.build(); - } - - @Override - public void shutdown() { - // nothing to shutdown - } - -} diff --git a/app/src/main/java/net/sf/uadetector/parser/UpdatingUserAgentStringParserImpl.java b/app/src/main/java/net/sf/uadetector/parser/UpdatingUserAgentStringParserImpl.java deleted file mode 100644 index 4150a14..0000000 --- a/app/src/main/java/net/sf/uadetector/parser/UpdatingUserAgentStringParserImpl.java +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.parser; - -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; - -import javax.annotation.Nonnegative; -import javax.annotation.PreDestroy; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.datastore.RefreshableDataStore; -import net.sf.uadetector.internal.util.ExecutorServices; - -/** - * This parser checks once per day if newer data are available. When newer data are available, they will be loaded, read - * and replaced by the current one. - * - * @author André Rouél - */ -public final class UpdatingUserAgentStringParserImpl extends UserAgentStringParserImpl { - - /** - * The default interval to check for updates is once per day - */ - public static final long DEFAULT_UPDATE_INTERVAL = 1000 * 60 * 60 * 24; - - /** - * Interval to check for updates in milliseconds - */ - private long updateInterval = DEFAULT_UPDATE_INTERVAL; - - /** - * Current update task of {@link UpdatingUserAgentStringParserImpl#scheduler} - */ - private ScheduledFuture currentUpdateTask; - - /** - * {@link ScheduledExecutorService} to schedule commands to update the UAS data in defined intervals - */ - private final ScheduledExecutorService scheduler = ExecutorServices.createScheduler(); - - /** - * Constructs an instance of {@code OnlineUserAgentStringParser}. During construction new UAS data will be queried - * online by the given {@code URL}s. - * - * @param store - * {@code DataStore} with reference UAS data used in fallback case - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if one of the given arguments is {@code null} - */ - public UpdatingUserAgentStringParserImpl(final RefreshableDataStore store) { - super(store); - - // set up update service - setUpUpdateService(); - } - - /** - * Gets the current update interval in milliseconds. - * - * @return current update interval in milliseconds - */ - public long getUpdateInterval() { - return updateInterval; - } - - /** - * Sets a new update interval in milliseconds.
- *
- * When a new update interval is set, the old update service is removed and a new one will be set. - * - * @param updateInterval - * update interval in milliseconds - * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException - * if the given update interval is less than 0 - */ - public void setUpdateInterval(@Nonnegative final long updateInterval) { - Check.notNegative(updateInterval, "updateInterval"); - this.updateInterval = updateInterval; - setUpUpdateService(); - } - - /** - * Set up a new update service to get newer UAS data - */ - private void setUpUpdateService() { - if (currentUpdateTask != null) { - currentUpdateTask.cancel(false); - } - currentUpdateTask = scheduler.scheduleWithFixedDelay(getDataStore().getUpdateOperation(), 0, updateInterval, MILLISECONDS); - } - - @Override - @PreDestroy - public void shutdown() { - currentUpdateTask.cancel(false); - ExecutorServices.shutdown(scheduler); - getDataStore().getUpdateOperation().shutdown(); - } - -} diff --git a/app/src/main/java/net/sf/uadetector/parser/UserAgentStringParserImpl.java b/app/src/main/java/net/sf/uadetector/parser/UserAgentStringParserImpl.java deleted file mode 100644 index 034b7da..0000000 --- a/app/src/main/java/net/sf/uadetector/parser/UserAgentStringParserImpl.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.parser; - -import javax.annotation.Nonnull; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.datastore.DataStore; - -/** - * This parser is an implementation of {@code UserAgentStringParser} interface and can detect user agents. The analysis - * is based on the read {@code Data} of the given data source. - * - * @author André Rouél - */ -public class UserAgentStringParserImpl extends AbstractUserAgentStringParser { - - /** - * Storage for all detection informations for UASparsers from http://user-agent-string.info. - */ - @Nonnull - private final T store; - - /** - * Constructs an {@code UserAgentStringParser} using the given UAS data as detection source. - * - * @param store - * store for UAS data - * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException - * if the given argument is {@code null} - */ - public UserAgentStringParserImpl(@Nonnull final T store) { - super(); - Check.notNull(store, "store"); - - this.store = store; - } - - @Nonnull - @Override - protected T getDataStore() { - return store; - } - -} diff --git a/app/src/main/java/net/sf/uadetector/parser/package-info.java b/app/src/main/java/net/sf/uadetector/parser/package-info.java deleted file mode 100644 index ccbe297..0000000 --- a/app/src/main/java/net/sf/uadetector/parser/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -@javax.annotation.ParametersAreNonnullByDefault -package net.sf.uadetector.parser; diff --git a/app/src/main/java/net/sf/uadetector/service/UADetectorServiceFactory.java b/app/src/main/java/net/sf/uadetector/service/UADetectorServiceFactory.java deleted file mode 100644 index 969168e..0000000 --- a/app/src/main/java/net/sf/uadetector/service/UADetectorServiceFactory.java +++ /dev/null @@ -1,326 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.service; - -import java.net.URL; - -import net.sf.uadetector.UserAgentStringParser; -import net.sf.uadetector.datareader.DataReader; -import net.sf.uadetector.datareader.XmlDataReader; -import net.sf.uadetector.datastore.AbstractDataStore; -import net.sf.uadetector.datastore.CachingXmlDataStore; -import net.sf.uadetector.datastore.DataStore; -import net.sf.uadetector.datastore.OnlineXmlDataStore; -import net.sf.uadetector.parser.UpdatingUserAgentStringParserImpl; -import net.sf.uadetector.parser.UserAgentStringParserImpl; - -/** - * Service factory to get preconfigured instances of {@code UserAgentStringParser} implementations. - * - * @author André Rouél - */ -public final class UADetectorServiceFactory { - - /** - * Holder to load the parser only when it's needed. - */ - private static final class CachingAndUpdatingParserHolder { - private static UserAgentStringParser parser = null; - - public static UserAgentStringParser getParser(final URL dataUrl, final URL verionUrl, final DataStore fallback) { - if (parser == null) - parser = new UpdatingUserAgentStringParserImpl( - CachingXmlDataStore.createCachingXmlDataStore(dataUrl, verionUrl, fallback)); - return parser; - } - } - - /** - * Holder to load the parser only once when it's needed. - */ - private static final class OnlineUpdatingParserHolder { - private static UserAgentStringParser parser = null; - - public static UserAgentStringParser getParser(final URL dataUrl, final URL versionUrl, final DataStore fallback) { - if (parser == null) - parser = new UpdatingUserAgentStringParserImpl(new OnlineXmlDataStore(dataUrl, versionUrl, fallback)); - return parser; - } - - } - - /** - * A simple implementation to store UAS data delivered in this module (called uadetector-resource) - * only in the heap space. - * - * @author André Rouél - */ - public static final class ResourceModuleXmlDataStore extends AbstractDataStore { - - /** - * The default data reader to read in UAS data in XML format - */ - private static final DataReader DEFAULT_DATA_READER = new XmlDataReader(); - - /** - * Path where the UAS data file is stored for the {@code ClassLoader} - */ - private static final String PATH = "net/sf/uadetector"; - - /** - * {@link URL} to the UAS data delivered in this module - */ - public static final URL UAS_DATA = ResourceModuleXmlDataStore.class.getClassLoader().getResource(PATH + "/uas.xml"); - - /** - * {@link URL} to the version information of the delivered UAS data in this module - */ - public static final URL UAS_VERSION = ResourceModuleXmlDataStore.class.getClassLoader().getResource(PATH + "/uas.version"); - - /** - * Constructs an {@code ResourceModuleXmlDataStore} by reading UAS data from the specified URL - * {@link #UAS_DATA} (in XML format). - */ - public ResourceModuleXmlDataStore() { - super(DEFAULT_DATA_READER, UAS_DATA, UAS_VERSION, DEFAULT_CHARSET); - } - - } - - /** - * A fallback to store UAS data. Could be used with a copy of UAS data on the file system or included as resource. - * - */ - public static final class CustomFallbackXmlDataStore extends AbstractDataStore { - - /** - * The default data reader to read in UAS data in XML format - */ - private static final DataReader DEFAULT_DATA_READER = new XmlDataReader(); - - /** - * Creates a new store with UAS data. - * @param dataUrl - * @param versionUrl - */ - protected CustomFallbackXmlDataStore(URL dataUrl, URL versionUrl) { - super(DEFAULT_DATA_READER, dataUrl, versionUrl, DEFAULT_CHARSET); - } - } - - /** - * Data store filled with the UAS data that are shipped with this module (JAR) - */ - public static final ResourceModuleXmlDataStore RESOURCE_MODULE = new ResourceModuleXmlDataStore(); - - /** - * Data store filled with the UAS data as defined by user. - */ - public static CustomFallbackXmlDataStore customFallbackXmlDataStore = null; - - /** - * {@link UserAgentStringParser} filled with the UAS data that are shipped with this module (JAR) - */ - public static final UserAgentStringParser RESOURCE_MODULE_PARSER = new UserAgentStringParserImpl( - RESOURCE_MODULE); - - /** - * Returns an implementation of {@link UserAgentStringParser} which checks at regular intervals for new versions of - * UAS data (also known as database). When newer data available, it automatically loads and updates it. - * Additionally the loaded data are stored in a cache file. - * - *

- * At initialization time the returned parser will be loaded with the UAS data of the cache file. If the - * cache file doesn't exist or is empty the data of this module will be loaded. The initialization is started only - * when this method is called the first time. - * - *

- * The update of the data store runs as background task. With this feature we try to reduce the initialization time - * of this UserAgentStringParser, because a network connection is involved and the remote system can be - * not available or slow. - * - *

- * The static class definition {@code CachingAndUpdatingParserHolder} within this factory class is not - * initialized until the JVM determines that {@code CachingAndUpdatingParserHolder} must be executed. The static - * class {@code CachingAndUpdatingParserHolder} is only executed when the static method - * {@code getOnlineUserAgentStringParser} is invoked on the class {@code UADetectorServiceFactory}, and the first - * time this happens the JVM will load and initialize the {@code CachingAndUpdatingParserHolder} class. - * - *

- * If during the operation the Internet connection gets lost, then this instance continues to work properly (and - * under correct log level settings you will get an corresponding log messages). - * - * @param dataUrl - * @param versionUrl - * @return an user agent string parser with updating service - */ - public static UserAgentStringParser getCachingAndUpdatingParser(final URL dataUrl, final URL versionUrl) { - return CachingAndUpdatingParserHolder.getParser(dataUrl, versionUrl, RESOURCE_MODULE); - } - - /** - * Returns an implementation of {@link UserAgentStringParser} which checks at regular intervals for new versions of - * UAS data (also known as database). When newer data available, it automatically loads and updates it. - * Additionally the loaded data are stored in a cache file. - * - *

- * At initialization time the returned parser will be loaded with the UAS data of the cache file. If the - * cache file doesn't exist or is empty the data of this module will be loaded. The initialization is started only - * when this method is called the first time. - * - *

- * The update of the data store runs as background task. With this feature we try to reduce the initialization time - * of this UserAgentStringParser, because a network connection is involved and the remote system can be - * not available or slow. - * - *

- * The static class definition {@code CachingAndUpdatingParserHolder} within this factory class is not - * initialized until the JVM determines that {@code CachingAndUpdatingParserHolder} must be executed. The static - * class {@code CachingAndUpdatingParserHolder} is only executed when the static method - * {@code getOnlineUserAgentStringParser} is invoked on the class {@code UADetectorServiceFactory}, and the first - * time this happens the JVM will load and initialize the {@code CachingAndUpdatingParserHolder} class. - * - *

- * If during the operation the Internet connection gets lost, then this instance continues to work properly (and - * under correct log level settings you will get an corresponding log messages). - * - * @param dataUrl - * @param versionUrl - * @param fallbackDataURL - * @param fallbackVersionURL - * @return an user agent string parser with updating service - */ - public static UserAgentStringParser getCachingAndUpdatingParser(final URL dataUrl, final URL versionUrl, final URL fallbackDataURL, final URL fallbackVersionURL) { - return CachingAndUpdatingParserHolder.getParser(dataUrl, versionUrl, getCustomFallbackXmlDataStore(fallbackDataURL, fallbackVersionURL)); - } - - /** - * Not supported any longer. Use {@link getCachingAndUpdatingParser(final URL dataUrl, final URL versionUrl)} or - * {@link getCachingAndUpdatingParser(final URL dataUrl, final URL versionUrl, final URL fallbackDataURL, final URL fallbackVersionURL)}. - * - * Returns an implementation of {@link UserAgentStringParser} with no updating functions. It will be loaded by using - * the shipped UAS data (also known as database) of this module. - * - * @return an user agent string parser using database of this module. - */ - @Deprecated - public static UserAgentStringParser getCachingAndUpdatingParser() { - return RESOURCE_MODULE_PARSER; - } - - /** - * Returns an implementation of {@link UserAgentStringParser} which checks at regular intervals for new versions of - * UAS data (also known as database). When newer data available, it automatically loads and updates it. - * - *

- * At initialization time the returned parser will be loaded with the UAS data of this module (the shipped - * one within the uadetector-resources JAR) and tries to update it. The initialization is started only when - * this method is called the first time. - * - *

- * The update of the data store runs as background task. With this feature we try to reduce the initialization time - * of this UserAgentStringParser, because a network connection is involved and the remote system can be - * not available or slow. - * - *

- * The static class definition {@code OnlineUpdatingParserHolder} within this factory class is not - * initialized until the JVM determines that {@code OnlineUpdatingParserHolder} must be executed. The static class - * {@code OnlineUpdatingParserHolder} is only executed when the static method {@code getOnlineUserAgentStringParser} - * is invoked on the class {@code UADetectorServiceFactory}, and the first time this happens the JVM will load and - * initialize the {@code OnlineUpdatingParserHolder} class. - * - *

- * If during the operation the Internet connection gets lost, then this instance continues to work properly (and - * under correct log level settings you will get an corresponding log messages). - * - * @param dataUrl - * @param versionUrl - * @return an user agent string parser with updating service - */ - public static UserAgentStringParser getOnlineUpdatingParser(final URL dataUrl, final URL versionUrl) { - return OnlineUpdatingParserHolder.getParser(dataUrl, versionUrl, RESOURCE_MODULE); - } - - /** - * Returns an implementation of {@link UserAgentStringParser} which checks at regular intervals for new versions of - * UAS data (also known as database). When newer data available, it automatically loads and updates it. - * - *

- * At initialization time the returned parser will be loaded with the UAS data of this module (the shipped - * one within the uadetector-resources JAR) and tries to update it. The initialization is started only when - * this method is called the first time. - * - *

- * The update of the data store runs as background task. With this feature we try to reduce the initialization time - * of this UserAgentStringParser, because a network connection is involved and the remote system can be - * not available or slow. - * - *

- * The static class definition {@code OnlineUpdatingParserHolder} within this factory class is not - * initialized until the JVM determines that {@code OnlineUpdatingParserHolder} must be executed. The static class - * {@code OnlineUpdatingParserHolder} is only executed when the static method {@code getOnlineUserAgentStringParser} - * is invoked on the class {@code UADetectorServiceFactory}, and the first time this happens the JVM will load and - * initialize the {@code OnlineUpdatingParserHolder} class. - * - *

- * If during the operation the Internet connection gets lost, then this instance continues to work properly (and - * under correct log level settings you will get an corresponding log messages). - * - * @param dataUrl - * @param versionUrl - * @param fallbackDataUrl - * @param fallbackVersionUrl - * @return an user agent string parser with updating service - */ - public static UserAgentStringParser getOnlineUpdatingParser(final URL dataUrl, final URL versionUrl, final URL fallbackDataUrl, final URL fallbackVersionUrl) { - return OnlineUpdatingParserHolder.getParser(dataUrl, versionUrl, getCustomFallbackXmlDataStore(fallbackDataUrl, fallbackVersionUrl)); - } - - /** - * Deprecated method as there is no public online version of the UAS data any longer that can be updated - * automatically. - * Subscribers of udger.com can use {@code getOnlineUpdatingParser(final URL dataUrl, final URL versionUrl)} instead. - * Returns an implementation of {@link UserAgentStringParser} with no updating functions. It will be loaded by using - * the shipped UAS data (also known as database) of this module. - * @return - */ - @Deprecated - public static UserAgentStringParser getOnlineUpdatingParser() { - return RESOURCE_MODULE_PARSER; - } - - /** - * Returns an implementation of {@link UserAgentStringParser} with no updating functions. It will be loaded by using - * the shipped UAS data (also known as database) of this module. The database is loaded once during - * initialization. The initialization is started at class loading of this class ({@code UADetectorServiceFactory}). - * - * @return an user agent string parser without updating service - */ - public static UserAgentStringParser getResourceModuleParser() { - return RESOURCE_MODULE_PARSER; - } - - private static CustomFallbackXmlDataStore getCustomFallbackXmlDataStore(final URL fallbackDataUrl, final URL fallbackVersionUrl) { - if (customFallbackXmlDataStore == null) - customFallbackXmlDataStore = new CustomFallbackXmlDataStore(fallbackDataUrl, fallbackVersionUrl); - return customFallbackXmlDataStore; - } - - private UADetectorServiceFactory() { - // This class is not intended to create objects from it. - } - -} diff --git a/app/src/main/java/net/sf/uadetector/service/package-info.java b/app/src/main/java/net/sf/uadetector/service/package-info.java deleted file mode 100644 index 8aa615d..0000000 --- a/app/src/main/java/net/sf/uadetector/service/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -@javax.annotation.ParametersAreNonnullByDefault -package net.sf.uadetector.service; diff --git a/app/src/main/java/net/sf/uadetector/writer/IniDataWriter.java b/app/src/main/java/net/sf/uadetector/writer/IniDataWriter.java deleted file mode 100644 index f467ebd..0000000 --- a/app/src/main/java/net/sf/uadetector/writer/IniDataWriter.java +++ /dev/null @@ -1,328 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.writer; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map.Entry; -import java.util.SortedSet; - -import javax.annotation.Nonnull; -import javax.annotation.concurrent.ThreadSafe; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.internal.data.BrowserOperatingSystemMappingComparator; -import net.sf.uadetector.internal.data.Data; -import net.sf.uadetector.internal.data.IdentifiableComparator; -import net.sf.uadetector.internal.data.OrderedPatternComparator; -import net.sf.uadetector.internal.data.domain.Browser; -import net.sf.uadetector.internal.data.domain.BrowserOperatingSystemMapping; -import net.sf.uadetector.internal.data.domain.BrowserPattern; -import net.sf.uadetector.internal.data.domain.BrowserType; -import net.sf.uadetector.internal.data.domain.Device; -import net.sf.uadetector.internal.data.domain.DevicePattern; -import net.sf.uadetector.internal.data.domain.Identifiable; -import net.sf.uadetector.internal.data.domain.OperatingSystem; -import net.sf.uadetector.internal.data.domain.OperatingSystemPattern; -import net.sf.uadetector.internal.data.domain.Robot; -import net.sf.uadetector.internal.util.RegularExpressionConverter; - -/** - * This utility is intended to transform an instance of {@code Data} into an UAS data conform XML document and - * allows us to recreate an uas.xml. - * - * @author André Rouél - */ -@ThreadSafe -public final class IniDataWriter { - - interface Char { - char EQUALS = '='; - char NEWLINE = '\n'; - char QUOTE = '"'; - char SEMICOLON = ';'; - char SQUARE_BRACKET_CLOSE = ']'; - char SQUARE_BRACKET_OPEN = '['; - char WHITESPACE = ' '; - } - - interface Tag { - String BROWSER = "browser"; - String BROWSER_OS = "browser_os"; - String BROWSER_REG = "browser_reg"; - String BROWSER_TYPE = "browser_type"; - String DEVICE = "device"; - String DEVICE_REG = "device_reg"; - String OS = "os"; - String OS_REG = "os_reg"; - String ROBOTS = "robots"; - } - - private static final String EMPTY = ""; - - private static void createBrowser(final Browser browser, final StringBuilder builder) { - createKeyValuePair(browser, String.valueOf(browser.getType().getId()), builder); - createKeyValuePair(browser, browser.getFamilyName(), builder); - createKeyValuePair(browser, browser.getUrl(), builder); - createKeyValuePair(browser, browser.getProducer(), builder); - createKeyValuePair(browser, browser.getProducerUrl(), builder); - createKeyValuePair(browser, browser.getIcon(), builder); - createKeyValuePair(browser, browser.getInfoUrl(), builder); - } - - private static void createBrowserOperatingSystemMappings(final Data data, final StringBuilder builder) { - final List mappings = new ArrayList( - data.getBrowserToOperatingSystemMappings()); - Collections.sort(mappings, BrowserOperatingSystemMappingComparator.INSTANCE); - createCategory(Tag.BROWSER_OS, builder); - createComment("browser_id[] = \"OS id\"", builder); - for (final BrowserOperatingSystemMapping mapping : mappings) { - createKeyValuePair(mapping.getBrowserId(), String.valueOf(mapping.getOperatingSystemId()), builder); - } - } - - private static void createBrowserPatterns(final Data data, final StringBuilder builder) { - final List patterns = new ArrayList(data.getBrowserPatterns().size()); - for (final Entry> entry : data.getBrowserPatterns().entrySet()) { - patterns.addAll(entry.getValue()); - } - Collections.sort(patterns, new OrderedPatternComparator()); - createCategory(Tag.BROWSER_REG, builder); - createComment("browser_reg_id[] = \"Browser regstring\"", builder); - createComment("browser_reg_id[] = \"Browser id\"", builder); - for (final BrowserPattern pattern : patterns) { - final String regex = RegularExpressionConverter.convertPatternToPerlRegex(pattern.getPattern()); - createKeyValuePair(pattern.getPosition(), regex, builder); - createKeyValuePair(pattern.getPosition(), String.valueOf(pattern.getId()), builder); - } - } - - private static void createBrowsers(final Data data, final StringBuilder builder) { - createCategory(Tag.BROWSER, builder); - createComment("browser_id[] = \"Browser type\"", builder); - createComment("browser_id[] = \"Browser Name\"", builder); - createComment("browser_id[] = \"Browser URL\"", builder); - createComment("browser_id[] = \"Browser Company\"", builder); - createComment("browser_id[] = \"Browser Company URL\"", builder); - createComment("browser_id[] = \"Browser ico\"", builder); - createComment("browser_id[] = \"Browser info URL\"", builder); - final List browsers = new ArrayList(data.getBrowsers()); - Collections.sort(browsers, IdentifiableComparator.INSTANCE); - for (final Browser browser : browsers) { - createBrowser(browser, builder); - } - } - - private static void createBrowserTypes(final Data data, final StringBuilder builder) { - createCategory(Tag.BROWSER_TYPE, builder); - createComment("browser_type_id[] = \"Browser type\"", builder); - final List browserTypes = new ArrayList(data.getBrowserTypes().values()); - Collections.sort(browserTypes, IdentifiableComparator.INSTANCE); - for (final BrowserType browserType : browserTypes) { - createKeyValuePair(browserType, browserType.getName(), builder); - } - } - - private static void createCategory(@Nonnull final String category, @Nonnull final StringBuilder builder) { - builder.append(Char.SQUARE_BRACKET_OPEN); - builder.append(category); - builder.append(Char.SQUARE_BRACKET_CLOSE); - builder.append(Char.NEWLINE); - } - - private static void createComment(@Nonnull final String comment, @Nonnull final StringBuilder builder) { - builder.append(Char.SEMICOLON); - builder.append(Char.WHITESPACE); - builder.append(comment); - builder.append(Char.NEWLINE); - } - - private static void createDescription(@Nonnull final Data data, @Nonnull final StringBuilder builder) { - createComment("Data (format ini) for UASparser - http://user-agent-string.info/download/UASparser", builder); - createComment("Version: " + data.getVersion(), builder); - createComment("Checksum:", builder); - createComment("MD5 - http://user-agent-string.info/rpc/get_data.php?format=ini&md5=y", builder); - createComment("SHA1 - http://user-agent-string.info/rpc/get_data.php?format=ini&sha1=y", builder); - builder.append(Char.SEMICOLON); - builder.append(Char.NEWLINE); - } - - private static void createDevice(final Device device, final StringBuilder builder) { - createKeyValuePair(device, device.getName(), builder); - createKeyValuePair(device, device.getIcon(), builder); - createKeyValuePair(device, device.getInfoUrl(), builder); - } - - private static void createDevicePatterns(final Data data, final StringBuilder builder) { - final List patterns = new ArrayList(data.getDevicePatterns().size()); - for (final Entry> entry : data.getDevicePatterns().entrySet()) { - patterns.addAll(entry.getValue()); - } - Collections.sort(patterns, new OrderedPatternComparator()); - createCategory(Tag.DEVICE_REG, builder); - createComment("device_reg_id[] = \"Device regstring\"", builder); - createComment("device_reg_id[] = \"Device id\"", builder); - for (final DevicePattern pattern : patterns) { - final String regex = RegularExpressionConverter.convertPatternToPerlRegex(pattern.getPattern()); - createKeyValuePair(pattern.getPosition(), regex, builder); - createKeyValuePair(pattern.getPosition(), String.valueOf(pattern.getId()), builder); - } - } - - private static void createDevices(final Data data, final StringBuilder builder) { - createCategory(Tag.DEVICE, builder); - createComment("device_id[] = \"Device type\"", builder); - createComment("device_id[] = \"Device ico\"", builder); - createComment("device_id[] = \"Device info URL\"", builder); - final List devices = new ArrayList(data.getDevices()); - Collections.sort(devices, IdentifiableComparator.INSTANCE); - for (final Device device : devices) { - createDevice(device, builder); - } - } - - private static void createKeyValuePair(@Nonnull final Identifiable identifiable, @Nonnull final String value, - @Nonnull final StringBuilder builder) { - createKeyValuePair(identifiable.getId(), value, builder); - } - - private static void createKeyValuePair(@Nonnull final int id, @Nonnull final String value, @Nonnull final StringBuilder builder) { - builder.append(id); - builder.append(Char.SQUARE_BRACKET_OPEN); - builder.append(Char.SQUARE_BRACKET_CLOSE); - builder.append(Char.WHITESPACE); - builder.append(Char.EQUALS); - builder.append(Char.WHITESPACE); - builder.append(Char.QUOTE); - builder.append(value); - builder.append(Char.QUOTE); - builder.append(Char.NEWLINE); - } - - private static void createOperatingSystem(final OperatingSystem operatingSystem, final StringBuilder builder) { - createKeyValuePair(operatingSystem, operatingSystem.getFamily(), builder); - createKeyValuePair(operatingSystem, operatingSystem.getName(), builder); - createKeyValuePair(operatingSystem, operatingSystem.getUrl(), builder); - createKeyValuePair(operatingSystem, operatingSystem.getProducer(), builder); - createKeyValuePair(operatingSystem, operatingSystem.getProducerUrl(), builder); - createKeyValuePair(operatingSystem, operatingSystem.getIcon(), builder); - } - - private static void createOperatingSystemPatterns(final Data data, final StringBuilder builder) { - createCategory(Tag.OS_REG, builder); - createComment("os_reg_id[] = \"OS regstring\"", builder); - createComment("os_reg_id[] = \"OS id\"", builder); - final List patterns = new ArrayList(data.getOperatingSystemPatterns().size()); - for (final Entry> entry : data.getOperatingSystemPatterns().entrySet()) { - patterns.addAll(entry.getValue()); - } - Collections.sort(patterns, new OrderedPatternComparator()); - - for (final OperatingSystemPattern pattern : patterns) { - final String regex = RegularExpressionConverter.convertPatternToPerlRegex(pattern.getPattern()); - createKeyValuePair(pattern.getPosition(), regex, builder); - createKeyValuePair(pattern.getPosition(), String.valueOf(pattern.getId()), builder); - } - } - - private static void createOperatingSystems(final Data data, final StringBuilder builder) { - createCategory(Tag.OS, builder); - createComment("os_id[] = \"OS Family\"", builder); - createComment("os_id[] = \"OS Name\"", builder); - createComment("os_id[] = \"OS URL\"", builder); - createComment("os_id[] = \"OS Company\"", builder); - createComment("os_id[] = \"OS Company URL\"", builder); - createComment("os_id[] = \"OS ico\"", builder); - final List operatingSystems = new ArrayList(data.getOperatingSystems()); - Collections.sort(operatingSystems, IdentifiableComparator.INSTANCE); - for (final OperatingSystem operatingSystem : operatingSystems) { - createOperatingSystem(operatingSystem, builder); - } - } - - private static void createRobot(final Robot robot, final StringBuilder builder) { - createKeyValuePair(robot, robot.getUserAgentString(), builder); - createKeyValuePair(robot, robot.getFamilyName(), builder); - createKeyValuePair(robot, robot.getName(), builder); - createKeyValuePair(robot, EMPTY, builder); - createKeyValuePair(robot, robot.getProducer(), builder); - createKeyValuePair(robot, robot.getProducerUrl(), builder); - createKeyValuePair(robot, robot.getIcon(), builder); - createKeyValuePair(robot, EMPTY, builder); - createKeyValuePair(robot, robot.getInfoUrl(), builder); - } - - private static void createRobots(final Data data, final StringBuilder builder) { - createCategory(Tag.ROBOTS, builder); - createComment("bot_id[] = \"bot useragentstring\"", builder); - createComment("bot_id[] = \"bot Family\"", builder); - createComment("bot_id[] = \"bot Name\"", builder); - createComment("bot_id[] = \"bot URL\"", builder); - createComment("bot_id[] = \"bot Company\"", builder); - createComment("bot_id[] = \"bot Company URL\"", builder); - createComment("bot_id[] = \"bot ico\"", builder); - createComment("bot_id[] = \"bot OS id\"", builder); - createComment("bot_id[] = \"bot info URL\"", builder); - for (final Robot robot : data.getRobots()) { - createRobot(robot, builder); - } - } - - /** - * Transforms a given {@code Data} instance into XML and writes it to the passed in {@code OutputStream}. - * - * @param data - * {@code Data} to transform into XML - * @param outputStream - * output stream to write - * @throws IOException - * if the given output stream can not be written - */ - public static void write(@Nonnull final Data data, @Nonnull final OutputStream outputStream) throws IOException { - Check.notNull(data, "data"); - Check.notNull(outputStream, "outputStream"); - - final StringBuilder doc = new StringBuilder(10000); - - // description element - createDescription(data, doc); - - // data - createRobots(data, doc); - createOperatingSystems(data, doc); - createBrowsers(data, doc); - createBrowserTypes(data, doc); - createBrowserPatterns(data, doc); - createBrowserOperatingSystemMappings(data, doc); - createOperatingSystemPatterns(data, doc); - createDevices(data, doc); - createDevicePatterns(data, doc); - - // write the content to output stream - outputStream.write(doc.toString().getBytes("UTF-8")); - } - - /** - * Attention: This class is not intended to create objects from it. - */ - private IniDataWriter() { - // This class is not intended to create objects from it. - } - -} diff --git a/app/src/main/java/net/sf/uadetector/writer/XmlDataWriter.java b/app/src/main/java/net/sf/uadetector/writer/XmlDataWriter.java deleted file mode 100644 index b545bf5..0000000 --- a/app/src/main/java/net/sf/uadetector/writer/XmlDataWriter.java +++ /dev/null @@ -1,455 +0,0 @@ -/******************************************************************************* - * Copyright 2013 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -package net.sf.uadetector.writer; - -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map.Entry; -import java.util.SortedSet; - -import javax.annotation.Nonnull; -import javax.annotation.concurrent.ThreadSafe; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Result; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import net.sf.qualitycheck.Check; -import net.sf.uadetector.internal.data.BrowserOperatingSystemMappingComparator; -import net.sf.uadetector.internal.data.Data; -import net.sf.uadetector.internal.data.IdentifiableComparator; -import net.sf.uadetector.internal.data.OrderedPatternComparator; -import net.sf.uadetector.internal.data.domain.Browser; -import net.sf.uadetector.internal.data.domain.BrowserOperatingSystemMapping; -import net.sf.uadetector.internal.data.domain.BrowserPattern; -import net.sf.uadetector.internal.data.domain.BrowserType; -import net.sf.uadetector.internal.data.domain.Device; -import net.sf.uadetector.internal.data.domain.DevicePattern; -import net.sf.uadetector.internal.data.domain.OperatingSystem; -import net.sf.uadetector.internal.data.domain.OperatingSystemPattern; -import net.sf.uadetector.internal.data.domain.Robot; -import net.sf.uadetector.internal.util.RegularExpressionConverter; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -/** - * This utility is intended to transform an instance of {@code Data} into an UAS data conform XML document and - * allows us to recreate an uas.xml. - * - * @author André Rouél - */ -@ThreadSafe -public final class XmlDataWriter { - - interface Tag { - String BOT_INFO_URL = "bot_info_url"; - String BROWSER = "browser"; - String BROWSER_ID = "browser_id"; - String BROWSER_INFO_URL = "browser_info_url"; - String BROWSER_OS = "browser_os"; - String BROWSER_REG = "browser_reg"; - String BROWSER_TYPE = "browser_type"; - String BROWSER_TYPES = "browser_types"; - String BROWSERS = "browsers"; - String BROWSERS_OS = "browsers_os"; - String BROWSERS_REG = "browsers_reg"; - String COMPANY = "company"; - String DATA = "data"; - String DESCRIPTION = "description"; - String DEVICE = "device"; - String DEVICE_ID = "device_id"; - String DEVICE_INFO_URL = "device_info_url"; - String DEVICE_REG = "device_reg"; - String DEVICES = "devices"; - String DEVICES_REG = "devices_reg"; - String FAMILY = "family"; - String ICON = "icon"; - String ID = "id"; - String LABEL = "label"; - String NAME = "name"; - String OPERATING_SYSTEM_REG = "operating_system_reg"; - String OPERATING_SYSTEMS = "operating_systems"; - String OPERATING_SYSTEMS_REG = "operating_systems_reg"; - String ORDER = "order"; - String OS = "os"; - String OS_ID = "os_id"; - String OS_INFO_URL = "os_info_url"; - String REGSTRING = "regstring"; - String ROBOT = "robot"; - String ROBOTS = "robots"; - String TYPE = "type"; - String UASDATA = "uasdata"; - String URL = "url"; - String URL_COMPANY = "url_company"; - String USERAGENT = "useragent"; - } - - private static final String INDENT_AMOUNT = "4"; - - private static final String INDENT_OPTION = "yes"; - - private static final String SCHEMA_URL = "http://data.udger.com/uasxmldata_old.dtd"; - - private static Element createBrowser(final Browser browser, final Document doc) { - final Element b = doc.createElement(Tag.BROWSER); - final Element id = doc.createElement(Tag.ID); - id.appendChild(doc.createTextNode(String.valueOf(browser.getId()))); - b.appendChild(id); - final Element family = doc.createElement(Tag.TYPE); - family.appendChild(doc.createTextNode(String.valueOf(browser.getType().getId()))); - b.appendChild(family); - final Element name = doc.createElement(Tag.NAME); - name.appendChild(doc.createTextNode(browser.getFamilyName())); - b.appendChild(name); - final Element url = doc.createElement(Tag.URL); - url.appendChild(doc.createCDATASection(browser.getUrl())); - b.appendChild(url); - final Element company = doc.createElement(Tag.COMPANY); - company.appendChild(doc.createCDATASection(browser.getProducer())); - b.appendChild(company); - final Element companyUrl = doc.createElement(Tag.URL_COMPANY); - companyUrl.appendChild(doc.createCDATASection(browser.getProducerUrl())); - b.appendChild(companyUrl); - final Element icon = doc.createElement(Tag.ICON); - icon.appendChild(doc.createTextNode(browser.getIcon())); - b.appendChild(icon); - final Element botInfoUrl = doc.createElement(Tag.BROWSER_INFO_URL); - botInfoUrl.appendChild(doc.createTextNode(browser.getInfoUrl())); - b.appendChild(botInfoUrl); - return b; - } - - private static Element createBrowserOperatingSystemMappings(final Data data, final Document doc) { - final List mappings = new ArrayList( - data.getBrowserToOperatingSystemMappings()); - Collections.sort(mappings, BrowserOperatingSystemMappingComparator.INSTANCE); - - final Element browserTypesElement = doc.createElement(Tag.BROWSERS_OS); - for (final BrowserOperatingSystemMapping mapping : mappings) { - final Element t = doc.createElement(Tag.BROWSER_OS); - final Element browserId = doc.createElement(Tag.BROWSER_ID); - browserId.appendChild(doc.createTextNode(String.valueOf(mapping.getBrowserId()))); - t.appendChild(browserId); - final Element osId = doc.createElement(Tag.OS_ID); - osId.appendChild(doc.createTextNode(String.valueOf(mapping.getOperatingSystemId()))); - t.appendChild(osId); - browserTypesElement.appendChild(t); - } - return browserTypesElement; - } - - private static Element createBrowserPatterns(final Data data, final Document doc) { - final List patterns = new ArrayList(data.getBrowserPatterns().size()); - for (final Entry> entry : data.getBrowserPatterns().entrySet()) { - patterns.addAll(entry.getValue()); - } - Collections.sort(patterns, new OrderedPatternComparator()); - - final Element browserTypesElement = doc.createElement(Tag.BROWSERS_REG); - for (final BrowserPattern pattern : patterns) { - final Element t = doc.createElement(Tag.BROWSER_REG); - final Element order = doc.createElement(Tag.ORDER); - order.appendChild(doc.createTextNode(String.valueOf(pattern.getPosition()))); - t.appendChild(order); - final Element id = doc.createElement(Tag.BROWSER_ID); - id.appendChild(doc.createTextNode(String.valueOf(pattern.getId()))); - t.appendChild(id); - final Element family = doc.createElement(Tag.REGSTRING); - family.appendChild(doc.createTextNode(RegularExpressionConverter.convertPatternToPerlRegex(pattern.getPattern()))); - t.appendChild(family); - browserTypesElement.appendChild(t); - } - return browserTypesElement; - } - - private static Element createBrowsers(final Data data, final Document doc) { - final Element browsersElement = doc.createElement(Tag.BROWSERS); - final List browsers = new ArrayList(data.getBrowsers()); - Collections.sort(browsers, IdentifiableComparator.INSTANCE); - for (final Browser browser : browsers) { - browsersElement.appendChild(createBrowser(browser, doc)); - } - return browsersElement; - } - - private static Element createBrowserTypes(final Data data, final Document doc) { - final Element browserTypesElement = doc.createElement(Tag.BROWSER_TYPES); - final List browserTypes = new ArrayList(data.getBrowserTypes().values()); - Collections.sort(browserTypes, IdentifiableComparator.INSTANCE); - for (final BrowserType browserType : browserTypes) { - final Element t = doc.createElement(Tag.BROWSER_TYPE); - final Element id = doc.createElement(Tag.ID); - id.appendChild(doc.createTextNode(String.valueOf(browserType.getId()))); - t.appendChild(id); - final Element family = doc.createElement(Tag.TYPE); - family.appendChild(doc.createTextNode(String.valueOf(browserType.getName()))); - t.appendChild(family); - browserTypesElement.appendChild(t); - } - return browserTypesElement; - } - - private static Element createDescription(@Nonnull final Data data, @Nonnull final Document doc) { - final Element description = doc.createElement(Tag.DESCRIPTION); - final Element label = doc.createElement(Tag.LABEL); - description.appendChild(label).appendChild( - doc.createTextNode("Data (format xml) for UASparser - http://user-agent-string.info/download/UASparser")); - final Element version = doc.createElement("version"); - description.appendChild(version).appendChild(doc.createTextNode(data.getVersion())); - final Element md5Checksum = doc.createElement("checksum"); - md5Checksum.setAttribute(Tag.TYPE, "MD5"); - description.appendChild(md5Checksum).appendChild( - doc.createTextNode("http://user-agent-string.info/rpc/get_data.php?format=xml&md5=y")); - final Element shaChecksum = doc.createElement("checksum"); - shaChecksum.setAttribute(Tag.TYPE, "SHA1"); - description.appendChild(shaChecksum).appendChild( - doc.createTextNode("http://user-agent-string.info/rpc/get_data.php?format=xml&sha1=y")); - return description; - } - - private static Element createDevice(final Device device, final Document doc) { - final Element b = doc.createElement(Tag.DEVICE); - final Element id = doc.createElement(Tag.ID); - id.appendChild(doc.createTextNode(String.valueOf(device.getId()))); - b.appendChild(id); - final Element name = doc.createElement(Tag.NAME); - name.appendChild(doc.createTextNode(device.getName())); - b.appendChild(name); - final Element icon = doc.createElement(Tag.ICON); - icon.appendChild(doc.createTextNode(device.getIcon())); - b.appendChild(icon); - final Element botInfoUrl = doc.createElement(Tag.DEVICE_INFO_URL); - botInfoUrl.appendChild(doc.createTextNode(device.getInfoUrl())); - b.appendChild(botInfoUrl); - return b; - } - - private static Element createDevicePatterns(final Data data, final Document doc) { - final List patterns = new ArrayList(data.getDevicePatterns().size()); - for (final Entry> entry : data.getDevicePatterns().entrySet()) { - patterns.addAll(entry.getValue()); - } - Collections.sort(patterns, new OrderedPatternComparator()); - - final Element deviceTypesElement = doc.createElement(Tag.DEVICES_REG); - for (final DevicePattern pattern : patterns) { - final Element t = doc.createElement(Tag.DEVICE_REG); - final Element order = doc.createElement(Tag.ORDER); - order.appendChild(doc.createTextNode(String.valueOf(pattern.getPosition()))); - t.appendChild(order); - final Element id = doc.createElement(Tag.DEVICE_ID); - id.appendChild(doc.createTextNode(String.valueOf(pattern.getId()))); - t.appendChild(id); - final Element family = doc.createElement(Tag.REGSTRING); - family.appendChild(doc.createTextNode(RegularExpressionConverter.convertPatternToPerlRegex(pattern.getPattern()))); - t.appendChild(family); - deviceTypesElement.appendChild(t); - } - return deviceTypesElement; - } - - private static Element createDevices(final Data data, final Document doc) { - final Element devicesElement = doc.createElement(Tag.DEVICES); - final List devices = new ArrayList(data.getDevices()); - Collections.sort(devices, IdentifiableComparator.INSTANCE); - for (final Device device : devices) { - devicesElement.appendChild(createDevice(device, doc)); - } - return devicesElement; - } - - private static Element createOperatingSystem(final OperatingSystem operatingSystem, final Document doc) { - final Element os = doc.createElement(Tag.OS); - final Element id = doc.createElement("id"); - id.appendChild(doc.createTextNode(String.valueOf(operatingSystem.getId()))); - os.appendChild(id); - final Element family = doc.createElement(Tag.FAMILY); - family.appendChild(doc.createTextNode(operatingSystem.getFamily())); - os.appendChild(family); - final Element name = doc.createElement(Tag.NAME); - name.appendChild(doc.createTextNode(operatingSystem.getName())); - os.appendChild(name); - final Element url = doc.createElement(Tag.URL); - url.appendChild(doc.createCDATASection(operatingSystem.getUrl())); - os.appendChild(url); - final Element company = doc.createElement(Tag.COMPANY); - company.appendChild(doc.createCDATASection(operatingSystem.getProducer())); - os.appendChild(company); - final Element companyUrl = doc.createElement(Tag.URL_COMPANY); - companyUrl.appendChild(doc.createCDATASection(operatingSystem.getProducerUrl())); - os.appendChild(companyUrl); - final Element icon = doc.createElement(Tag.ICON); - icon.appendChild(doc.createTextNode(operatingSystem.getIcon())); - os.appendChild(icon); - final Element botInfoUrl = doc.createElement(Tag.OS_INFO_URL); - botInfoUrl.appendChild(doc.createTextNode(operatingSystem.getInfoUrl())); - os.appendChild(botInfoUrl); - return os; - } - - private static Element createOperatingSystemPatterns(final Data data, final Document doc) { - final List patterns = new ArrayList(data.getOperatingSystemPatterns().size()); - for (final Entry> entry : data.getOperatingSystemPatterns().entrySet()) { - patterns.addAll(entry.getValue()); - } - Collections.sort(patterns, new OrderedPatternComparator()); - - final Element browserTypesElement = doc.createElement(Tag.OPERATING_SYSTEMS_REG); - for (final OperatingSystemPattern pattern : patterns) { - final Element t = doc.createElement(Tag.OPERATING_SYSTEM_REG); - final Element order = doc.createElement(Tag.ORDER); - order.appendChild(doc.createTextNode(String.valueOf(pattern.getPosition()))); - t.appendChild(order); - final Element id = doc.createElement(Tag.OS_ID); - id.appendChild(doc.createTextNode(String.valueOf(pattern.getId()))); - t.appendChild(id); - final Element family = doc.createElement(Tag.REGSTRING); - family.appendChild(doc.createTextNode(RegularExpressionConverter.convertPatternToPerlRegex(pattern.getPattern()))); - t.appendChild(family); - browserTypesElement.appendChild(t); - } - return browserTypesElement; - } - - private static Element createOperatingSystems(final Data data, final Document doc) { - final Element operatingSystemsElement = doc.createElement(Tag.OPERATING_SYSTEMS); - final List operatingSystems = new ArrayList(data.getOperatingSystems()); - Collections.sort(operatingSystems, IdentifiableComparator.INSTANCE); - for (final OperatingSystem operatingSystem : operatingSystems) { - operatingSystemsElement.appendChild(createOperatingSystem(operatingSystem, doc)); - } - return operatingSystemsElement; - } - - private static Element createRobots(final Data data, final Document doc) { - final Element robotsElement = doc.createElement(Tag.ROBOTS); - for (final Robot robot : data.getRobots()) { - robotsElement.appendChild(createRobots(robot, doc)); - } - return robotsElement; - } - - private static Element createRobots(final Robot robot, final Document doc) { - final Element r = doc.createElement(Tag.ROBOT); - final Element id = doc.createElement(Tag.ID); - id.appendChild(doc.createTextNode(String.valueOf(robot.getId()))); - r.appendChild(id); - final Element useragent = doc.createElement(Tag.USERAGENT); - useragent.appendChild(doc.createCDATASection(robot.getUserAgentString())); - r.appendChild(useragent); - final Element family = doc.createElement(Tag.FAMILY); - family.appendChild(doc.createTextNode(robot.getFamilyName())); - r.appendChild(family); - final Element name = doc.createElement(Tag.NAME); - name.appendChild(doc.createTextNode(robot.getName())); - r.appendChild(name); - final Element company = doc.createElement(Tag.COMPANY); - company.appendChild(doc.createCDATASection(robot.getProducer())); - r.appendChild(company); - final Element companyUrl = doc.createElement(Tag.URL_COMPANY); - companyUrl.appendChild(doc.createCDATASection(robot.getProducerUrl())); - r.appendChild(companyUrl); - final Element icon = doc.createElement(Tag.ICON); - icon.appendChild(doc.createTextNode(robot.getIcon())); - r.appendChild(icon); - final Element botInfoUrl = doc.createElement(Tag.BOT_INFO_URL); - botInfoUrl.appendChild(doc.createTextNode(robot.getInfoUrl())); - r.appendChild(botInfoUrl); - return r; - } - - @Nonnull - static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException { - final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); - return docFactory.newDocumentBuilder(); - } - - static void transform(@Nonnull final Source xmlInput, @Nonnull final Result xmlOutput) throws TransformerException { - Check.notNull(xmlInput, "xmlInput"); - Check.notNull(xmlOutput, "xmlOutput"); - - final TransformerFactory transformerFactory = TransformerFactory.newInstance(); - final Transformer transformer = transformerFactory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, INDENT_OPTION); - transformer.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, SCHEMA_URL); - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", INDENT_AMOUNT); - transformer.transform(xmlInput, xmlOutput); - } - - /** - * Transforms a given {@code Data} instance into XML and writes it to the passed in {@code OutputStream}. - * - * @param data - * {@code Data} to transform into XML - * @param outputStream - * output stream to write - * @throws ParserConfigurationException - * If a DocumentBuilder cannot be created which satisfies the configuration requested. - * @throws TransformerException - * If an unrecoverable error occurs during the course of the transformation. - */ - public static void write(@Nonnull final Data data, @Nonnull final OutputStream outputStream) throws ParserConfigurationException, - TransformerException { - Check.notNull(data, "data"); - Check.notNull(outputStream, "outputStream"); - - final Document doc = newDocumentBuilder().newDocument(); - - // root element - final Element uasdataElement = doc.createElement(Tag.UASDATA); - doc.appendChild(uasdataElement); - - // description element - uasdataElement.appendChild(createDescription(data, doc)); - - // data element - final Element dataElement = doc.createElement(Tag.DATA); - uasdataElement.appendChild(dataElement); - - dataElement.appendChild(createRobots(data, doc)); - dataElement.appendChild(createOperatingSystems(data, doc)); - dataElement.appendChild(createBrowsers(data, doc)); - dataElement.appendChild(createBrowserTypes(data, doc)); - dataElement.appendChild(createBrowserPatterns(data, doc)); - dataElement.appendChild(createBrowserOperatingSystemMappings(data, doc)); - dataElement.appendChild(createOperatingSystemPatterns(data, doc)); - dataElement.appendChild(createDevices(data, doc)); - dataElement.appendChild(createDevicePatterns(data, doc)); - - // write the content to output stream - final DOMSource source = new DOMSource(doc); - final StreamResult result = new StreamResult(outputStream); - transform(source, result); - } - - /** - * Attention: This class is not intended to create objects from it. - */ - private XmlDataWriter() { - // This class is not intended to create objects from it. - } - -} diff --git a/app/src/main/java/net/sf/uadetector/writer/package-info.java b/app/src/main/java/net/sf/uadetector/writer/package-info.java deleted file mode 100644 index 212a122..0000000 --- a/app/src/main/java/net/sf/uadetector/writer/package-info.java +++ /dev/null @@ -1,17 +0,0 @@ -/******************************************************************************* - * Copyright 2012 André Rouél - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - ******************************************************************************/ -@javax.annotation.ParametersAreNonnullByDefault -package net.sf.uadetector.writer; diff --git a/app/src/main/java/org/littleshoot/proxy/mitm/Authority.java b/app/src/main/java/org/littleshoot/proxy/mitm/Authority.java new file mode 100755 index 0000000..a7451f5 --- /dev/null +++ b/app/src/main/java/org/littleshoot/proxy/mitm/Authority.java @@ -0,0 +1,98 @@ +package org.littleshoot.proxy.mitm; + +import android.os.Environment; + +import java.io.File; + +/** + * Parameter object holding personal informations given to a SSLEngineSource. + * + * XXX consider to inline within the interface SslEngineSource, if MITM is core + */ +public class Authority { + + private final File keyStoreDir; + + private final String alias; + + private final char[] password; + + private final String commonName; + + private final String organization; + + private final String organizationalUnitName; + + private final String certOrganization; + + private final String certOrganizationalUnitName; + + /** + * Create a parameter object with example certificate and certificate + * authority informations + */ + public Authority() { + keyStoreDir = new File(Environment.getExternalStorageDirectory() + "/har/"); + alias = "littleproxy-mitm"; // proxy id + password = "Be Your Own Lantern".toCharArray(); + organization = "LittleProxy-mitm"; // proxy name + commonName = organization + ", describe proxy here"; // MITM is bad + // normally + organizationalUnitName = "Certificate Authority"; + certOrganization = organization; // proxy name + certOrganizationalUnitName = organization + + ", describe proxy purpose here, since Man-In-The-Middle is bad normally."; + } + + /** + * Create a parameter object with the given certificate and certificate + * authority informations + */ + public Authority(File keyStoreDir, String alias, char[] password, + String commonName, String organization, + String organizationalUnitName, String certOrganization, + String certOrganizationalUnitName) { + super(); + this.keyStoreDir = keyStoreDir; + this.alias = alias; + this.password = password; + this.commonName = commonName; + this.organization = organization; + this.organizationalUnitName = organizationalUnitName; + this.certOrganization = certOrganization; + this.certOrganizationalUnitName = certOrganizationalUnitName; + } + + public File aliasFile(String fileExtension) { + return new File(keyStoreDir, alias + fileExtension); + } + + public String alias() { + return alias; + } + + public char[] password() { + return password; + } + + public String commonName() { + return commonName; + } + + public String organization() { + return organization; + } + + public String organizationalUnitName() { + return organizationalUnitName; + } + + public String certOrganisation() { + return certOrganization; + } + + public String certOrganizationalUnitName() { + return certOrganizationalUnitName; + } + +} diff --git a/app/src/main/java/org/littleshoot/proxy/mitm/BouncyCastleSslEngineSource.java b/app/src/main/java/org/littleshoot/proxy/mitm/BouncyCastleSslEngineSource.java new file mode 100755 index 0000000..24973dc --- /dev/null +++ b/app/src/main/java/org/littleshoot/proxy/mitm/BouncyCastleSslEngineSource.java @@ -0,0 +1,390 @@ +package org.littleshoot.proxy.mitm; + +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.TrustManager; + +import org.apache.commons.io.IOUtils; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.bouncycastle.operator.OperatorCreationException; +import org.littleshoot.proxy.SslEngineSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; + +/** + * A {@link SslEngineSource} which creates a key store with a Root Certificate + * Authority. The certificates are generated lazily if the given key store file + * doesn't yet exist. + * + * The root certificate is exported in PEM format to be used in a browser. The + * proxy application presents for every host a dynamically created certificate + * to the browser, signed by this certificate authority. + * + * This facilitates the proxy to handle as a "Man In The Middle" to filter the + * decrypted content in clear text. + * + * The hard part was done by mawoki. It's derived from Zed Attack Proxy (ZAP). + * ZAP is an HTTP/HTTPS proxy for assessing web application security. Copyright + * 2011 mawoki@ymail.com Licensed under the Apache License, Version 2.0 + */ +public class BouncyCastleSslEngineSource implements SslEngineSource { + + private static final Logger LOG = LoggerFactory.getLogger(BouncyCastleSslEngineSource.class); + + /** + * The P12 format has to be implemented by every vendor. Oracles proprietary + * JKS type is not available in Android. + */ + private static final String KEY_STORE_TYPE = "PKCS12"; + + private static final String KEY_STORE_FILE_EXTENSION = ".p12"; + + private final Authority authority; + + private final boolean trustAllServers; + private final boolean sendCerts; + + private SSLContext sslContext; + + private Certificate caCert; + + private PrivateKey caPrivKey; + + private Cache serverSSLContexts; + + /** + * Creates a SSL engine source create a Certificate Authority if needed and + * initializes a SSL context. Exceptions will be thrown to let the manager + * decide how to react. Don't install a MITM manager in the proxy in case of + * a failure. + * + * @param authority + * a parameter object to provide personal informations of the + * Certificate Authority and the dynamic certificates. + * + * @param trustAllServers + * + * @param sendCerts + * + * @param sslContexts + * a cache to store dynamically created server certificates. + * Generation takes between 50 to 500ms, but only once per + * thread, since there is a connection cache too. It's save to + * give a null cache to prevent memory or locking issues. + */ + public BouncyCastleSslEngineSource(Authority authority, + boolean trustAllServers, boolean sendCerts, + Cache sslContexts) + throws GeneralSecurityException, OperatorCreationException, + RootCertificateException, IOException { + this.authority = authority; + this.trustAllServers = trustAllServers; + this.sendCerts = sendCerts; + this.serverSSLContexts = sslContexts; + initializeKeyStore(); + initializeSSLContext(); + } + + /** + * Creates a SSL engine source create a Certificate Authority if needed and + * initializes a SSL context. This constructor defaults a cache to store + * dynamically created server certificates. Exceptions will be thrown to let + * the manager decide how to react. Don't install a MITM manager in the + * proxy in case of a failure. + * + * @param authority + * a parameter object to provide personal informations of the + * Certificate Authority and the dynamic certificates. + * + * @param trustAllServers + * + * @param sendCerts + */ + public BouncyCastleSslEngineSource(Authority authority, + boolean trustAllServers, boolean sendCerts) + throws RootCertificateException, GeneralSecurityException, + IOException, OperatorCreationException { + this(authority, trustAllServers, sendCerts, + initDefaultCertificateCache()); + } + + private static Cache initDefaultCertificateCache() { + return CacheBuilder.newBuilder() // + .expireAfterAccess(5, TimeUnit.MINUTES) // + .concurrencyLevel(16) // + .build(); + } + + private void filterWeakCipherSuites(SSLEngine sslEngine) { + List ciphers = new LinkedList(); + for (String each : sslEngine.getEnabledCipherSuites()) { + if (each.equals("TLS_DHE_RSA_WITH_AES_128_CBC_SHA") || each.equals("TLS_DHE_RSA_WITH_AES_256_CBC_SHA")) { + LOG.debug("Removed cipher {}", each); + } else { + ciphers.add(each); + } + } + sslEngine.setEnabledCipherSuites(ciphers.toArray(new String[ciphers.size()])); + if (LOG.isDebugEnabled()) { + if (sslEngine.getUseClientMode()) { + LOG.debug("Enabled server cipher suites:"); + } else { + String host = sslEngine.getPeerHost(); + int port = sslEngine.getPeerPort(); + LOG.debug("Enabled client {}:{} cipher suites:", host, port); + } + for (String each : ciphers) { + LOG.debug(each); + } + } + } + + public SSLEngine newSslEngine() { + SSLEngine sslEngine = sslContext.createSSLEngine(); + filterWeakCipherSuites(sslEngine); + return sslEngine; + } + + @Override + public SSLEngine newSslEngine(String remoteHost, int remotePort) { + SSLEngine sslEngine = sslContext + .createSSLEngine(remoteHost, remotePort); + sslEngine.setUseClientMode(true); + if (!tryHostNameVerificationJava7(sslEngine)) { + LOG.debug("Host Name Verification is not supported, causes insecure HTTPS connection"); + } + filterWeakCipherSuites(sslEngine); + return sslEngine; + } + + private boolean tryHostNameVerificationJava7(SSLEngine sslEngine) { + for (Method method : SSLParameters.class.getMethods()) { + // method is available since Java 7 + if ("setEndpointIdentificationAlgorithm".equals(method.getName())) { + SSLParameters sslParams = new SSLParameters(); + try { + method.invoke(sslParams, "HTTPS"); + } catch (IllegalAccessException e) { + LOG.debug( + "SSLParameters#setEndpointIdentificationAlgorithm", + e); + return false; + } catch (InvocationTargetException e) { + LOG.debug( + "SSLParameters#setEndpointIdentificationAlgorithm", + e); + return false; + } + sslEngine.setSSLParameters(sslParams); + return true; + } + } + return false; + } + + private void initializeKeyStore() throws RootCertificateException, + GeneralSecurityException, OperatorCreationException, IOException { + if (authority.aliasFile(KEY_STORE_FILE_EXTENSION).exists() + && authority.aliasFile(".pem").exists()) { + return; + } + MillisecondsDuration duration = new MillisecondsDuration(); + KeyStore keystore = CertificateHelper.createRootCertificate(authority, + KEY_STORE_TYPE); + LOG.info("Created root certificate authority key store in {}ms", + duration); + + OutputStream os = null; + try { + os = new FileOutputStream( + authority.aliasFile(KEY_STORE_FILE_EXTENSION)); + keystore.store(os, authority.password()); + } finally { + IOUtils.closeQuietly(os); + } + + Certificate cert = keystore.getCertificate(authority.alias()); + exportPem(authority.aliasFile(".pem"), cert); + } + + private void initializeSSLContext() throws GeneralSecurityException, + IOException { + KeyStore ks = loadKeyStore(); + caCert = ks.getCertificate(authority.alias()); + caPrivKey = (PrivateKey) ks.getKey(authority.alias(), + authority.password()); + + TrustManager[] trustManagers; + if (trustAllServers) { + trustManagers = InsecureTrustManagerFactory.INSTANCE + .getTrustManagers(); + } else { + trustManagers = new TrustManager[] { new MergeTrustManager(ks) }; + } + + KeyManager[] keyManagers; + if (sendCerts) { + keyManagers = CertificateHelper.getKeyManagers(ks, authority); + } else { + keyManagers = new KeyManager[0]; + } + + sslContext = CertificateHelper.newClientContext(keyManagers, + trustManagers); + SSLEngine sslEngine = sslContext.createSSLEngine(); + if (!tryHostNameVerificationJava7(sslEngine)) { + LOG.warn("Host Name Verification is not supported, causes insecure HTTPS connection to upstream servers."); + } + } + + private KeyStore loadKeyStore() throws GeneralSecurityException, + IOException { + KeyStore ks = KeyStore.getInstance(KEY_STORE_TYPE); + FileInputStream is = null; + try { + is = new FileInputStream( + authority.aliasFile(KEY_STORE_FILE_EXTENSION)); + ks.load(is, authority.password()); + } finally { + IOUtils.closeQuietly(is); + } + return ks; + } + + /** + * Generates an 1024 bit RSA key pair using SHA1PRNG. Thoughts: 2048 takes + * much longer time on older CPUs. And for almost every client, 1024 is + * sufficient. + * + * Derived from Zed Attack Proxy (ZAP). ZAP is an HTTP/HTTPS proxy for + * assessing web application security. Copyright 2011 mawoki@ymail.com + * Licensed under the Apache License, Version 2.0 + * + * @param commonName + * the common name to use in the server certificate + * + * @param subjectAlternativeNames + * a List of the subject alternative names to use in the server + * certificate, could be empty, but must not be null + * + * @see org.parosproxy.paros.security.SslCertificateServiceImpl. + * createCertForHost(String) + * @see org.parosproxy.paros.network.SSLConnector.getTunnelSSLSocketFactory( + * String) + */ + public SSLEngine createCertForHost(final String commonName, + final SubjectAlternativeNameHolder subjectAlternativeNames) + throws GeneralSecurityException, OperatorCreationException, + IOException, ExecutionException { + if (commonName == null) { + throw new IllegalArgumentException( + "Error, 'commonName' is not allowed to be null!"); + } + if (subjectAlternativeNames == null) { + throw new IllegalArgumentException( + "Error, 'subjectAlternativeNames' is not allowed to be null!"); + } + + SSLContext ctx; + if (serverSSLContexts == null) { + ctx = createServerContext(commonName, subjectAlternativeNames); + } else { + ctx = serverSSLContexts.get(commonName, new Callable() { + @Override + public SSLContext call() throws Exception { + return createServerContext(commonName, + subjectAlternativeNames); + } + }); + } + return ctx.createSSLEngine(); + } + + private SSLContext createServerContext(String commonName, + SubjectAlternativeNameHolder subjectAlternativeNames) + throws GeneralSecurityException, IOException, + OperatorCreationException { + + MillisecondsDuration duration = new MillisecondsDuration(); + + KeyStore ks = CertificateHelper.createServerCertificate(commonName, + subjectAlternativeNames, authority, caCert, caPrivKey); + KeyManager[] keyManagers = CertificateHelper.getKeyManagers(ks, + authority); + + SSLContext result = CertificateHelper.newServerContext(keyManagers); + + LOG.info("Impersonated {} in {}ms", commonName, duration); + return result; + } + + public void initializeServerCertificates(String commonName, + SubjectAlternativeNameHolder subjectAlternativeNames) + throws GeneralSecurityException, OperatorCreationException, + IOException { + + KeyStore ks = CertificateHelper.createServerCertificate(commonName, + subjectAlternativeNames, authority, caCert, caPrivKey); + + PrivateKey key = (PrivateKey) ks.getKey(authority.alias(), + authority.password()); + exportPem(authority.aliasFile("-" + commonName + "-key.pem"), key); + + Object[] certs = ks.getCertificateChain(authority.alias()); + exportPem(authority.aliasFile("-" + commonName + "-cert.pem"), certs); + } + + private void exportPem(File exportFile, Object... certs) + throws IOException, CertificateEncodingException { + Writer sw = null; + JcaPEMWriter pw = null; + try { + sw = new FileWriter(exportFile); + pw = new JcaPEMWriter(sw); + for (Object cert : certs) { + pw.writeObject(cert); + pw.flush(); + } + } finally { + IOUtils.closeQuietly(pw); + IOUtils.closeQuietly(sw); + } + } + +} + +class MillisecondsDuration { + private final long mStartTime = System.currentTimeMillis(); + + @Override + public String toString() { + return String.valueOf(System.currentTimeMillis() - mStartTime); + } +} diff --git a/app/src/main/java/org/littleshoot/proxy/mitm/CertificateHelper.java b/app/src/main/java/org/littleshoot/proxy/mitm/CertificateHelper.java new file mode 100755 index 0000000..a4358e2 --- /dev/null +++ b/app/src/main/java/org/littleshoot/proxy/mitm/CertificateHelper.java @@ -0,0 +1,327 @@ +package org.littleshoot.proxy.mitm; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyManagementException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.Random; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + +import org.apache.commons.io.IOUtils; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.KeyPurposeId; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.bc.BcX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class CertificateHelper { + + private static final Logger log = LoggerFactory.getLogger(CertificateHelper.class); + + public static final String PROVIDER_NAME = BouncyCastleProvider.PROVIDER_NAME; + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + private static final String KEYGEN_ALGORITHM = "RSA"; + + private static final String SECURE_RANDOM_ALGORITHM = "SHA1PRNG"; + + /** + * The signature algorithm starting with the message digest to use when + * signing certificates. On 64-bit systems this should be set to SHA512, on + * 32-bit systems this is SHA256. On 64-bit systems, SHA512 generally + * performs better than SHA256; see this question for details: + * http://crypto.stackexchange.com/questions/26336/sha512-faster-than-sha256 + */ + private static final String SIGNATURE_ALGORITHM = (is32BitJvm() ? "SHA256" : "SHA512") + "WithRSAEncryption"; + + private static final int ROOT_KEYSIZE = 2048; + + private static final int FAKE_KEYSIZE = 1024; + + /** The milliseconds of a day */ + private static final long ONE_DAY = 86400000L; + + /** + * Current time minus 1 year, just in case software clock goes back due to + * time synchronization + */ + private static final Date NOT_BEFORE = new Date(System.currentTimeMillis() - ONE_DAY * 365); + + /** + * The maximum possible value in X.509 specification: 9999-12-31 23:59:59, + * new Date(253402300799000L), but Apple iOS 8 fails with a certificate + * expiration date grater than Mon, 24 Jan 6084 02:07:59 GMT (issue #6). + * + * Hundred years in the future from starting the proxy should be enough. + */ + private static final Date NOT_AFTER = new Date(System.currentTimeMillis() + ONE_DAY * 365 * 100); + + /** + * Enforce TLS 1.2 if available, since it's not default up to Java 8. + *

+ * Java 7 disables TLS 1.1 and 1.2 for clients. From Java Cryptography Architecture Oracle Providers Documentation: + * Although SunJSSE in the Java SE 7 release supports TLS 1.1 and TLS 1.2, + * neither version is enabled by default for client connections. Some + * servers do not implement forward compatibility correctly and refuse to + * talk to TLS 1.1 or TLS 1.2 clients. For interoperability, SunJSSE does + * not enable TLS 1.1 or TLS 1.2 by default for client connections. + */ + private static final String SSL_CONTEXT_PROTOCOL = "TLSv1.2"; + /** + * {@link SSLContext}: Every implementation of the Java platform is required + * to support the following standard SSLContext protocol: TLSv1 + */ + private static final String SSL_CONTEXT_FALLBACK_PROTOCOL = "TLSv1"; + + private CertificateHelper() {} + + public static KeyPair generateKeyPair(int keySize) + throws NoSuchAlgorithmException, NoSuchProviderException { + KeyPairGenerator generator = KeyPairGenerator + .getInstance(KEYGEN_ALGORITHM/* , PROVIDER_NAME */); + SecureRandom secureRandom = SecureRandom + .getInstance(SECURE_RANDOM_ALGORITHM/* , PROVIDER_NAME */); + generator.initialize(keySize, secureRandom); + return generator.generateKeyPair(); + } + + /** + * Uses the non-portable system property sun.arch.data.model to help + * determine if we are running on a 32-bit JVM. Since the majority of modern + * systems are 64 bits, this method "assumes" 64 bits and only returns true + * if sun.arch.data.model explicitly indicates a 32-bit JVM. + * + * @return true if we can determine definitively that this is a 32-bit JVM, + * otherwise false + */ + private static boolean is32BitJvm() { + Integer bits = Integer.getInteger("sun.arch.data.model"); + return bits != null && bits == 32; + } + + public static KeyStore createRootCertificate(Authority authority, + String keyStoreType) throws NoSuchAlgorithmException, + NoSuchProviderException, IOException, + OperatorCreationException, CertificateException, KeyStoreException { + + KeyPair keyPair = generateKeyPair(ROOT_KEYSIZE); + + X500NameBuilder nameBuilder = new X500NameBuilder(BCStyle.INSTANCE); + nameBuilder.addRDN(BCStyle.CN, authority.commonName()); + nameBuilder.addRDN(BCStyle.O, authority.organization()); + nameBuilder.addRDN(BCStyle.OU, authority.organizationalUnitName()); + + X500Name issuer = nameBuilder.build(); + BigInteger serial = BigInteger.valueOf(initRandomSerial()); + X500Name subject = issuer; + PublicKey pubKey = keyPair.getPublic(); + + X509v3CertificateBuilder generator = new JcaX509v3CertificateBuilder( + issuer, serial, NOT_BEFORE, NOT_AFTER, subject, pubKey); + + generator.addExtension(Extension.subjectKeyIdentifier, false, + createSubjectKeyIdentifier(pubKey)); + generator.addExtension(Extension.basicConstraints, true, + new BasicConstraints(true)); + + KeyUsage usage = new KeyUsage(KeyUsage.keyCertSign + | KeyUsage.digitalSignature | KeyUsage.keyEncipherment + | KeyUsage.dataEncipherment | KeyUsage.cRLSign); + generator.addExtension(Extension.keyUsage, false, usage); + + ASN1EncodableVector purposes = new ASN1EncodableVector(); + purposes.add(KeyPurposeId.id_kp_serverAuth); + purposes.add(KeyPurposeId.id_kp_clientAuth); + purposes.add(KeyPurposeId.anyExtendedKeyUsage); + generator.addExtension(Extension.extendedKeyUsage, false, + new DERSequence(purposes)); + + X509Certificate cert = signCertificate(generator, keyPair.getPrivate()); + + KeyStore result = KeyStore + .getInstance(keyStoreType/* , PROVIDER_NAME */); + result.load(null, null); + result.setKeyEntry(authority.alias(), keyPair.getPrivate(), + authority.password(), new Certificate[] { cert }); + return result; + } + + private static SubjectKeyIdentifier createSubjectKeyIdentifier(Key key) + throws IOException { + ByteArrayInputStream bIn = new ByteArrayInputStream(key.getEncoded()); + ASN1InputStream is = null; + try { + is = new ASN1InputStream(bIn); + ASN1Sequence seq = (ASN1Sequence) is.readObject(); + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo(seq); + return new BcX509ExtensionUtils().createSubjectKeyIdentifier(info); + } finally { + IOUtils.closeQuietly(is); + } + } + + public static KeyStore createServerCertificate(String commonName, + SubjectAlternativeNameHolder subjectAlternativeNames, + Authority authority, Certificate caCert, PrivateKey caPrivKey) + throws NoSuchAlgorithmException, NoSuchProviderException, + IOException, OperatorCreationException, CertificateException, + InvalidKeyException, SignatureException, KeyStoreException { + + KeyPair keyPair = generateKeyPair(FAKE_KEYSIZE); + + X500Name issuer = new X509CertificateHolder(caCert.getEncoded()) + .getSubject(); + BigInteger serial = BigInteger.valueOf(initRandomSerial()); + + X500NameBuilder name = new X500NameBuilder(BCStyle.INSTANCE); + name.addRDN(BCStyle.CN, commonName); + name.addRDN(BCStyle.O, authority.certOrganisation()); + name.addRDN(BCStyle.OU, authority.certOrganizationalUnitName()); + X500Name subject = name.build(); + + X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(issuer, serial, NOT_BEFORE, + new Date(System.currentTimeMillis() + ONE_DAY), subject, keyPair.getPublic()); + + builder.addExtension(Extension.subjectKeyIdentifier, false, + createSubjectKeyIdentifier(keyPair.getPublic())); + builder.addExtension(Extension.basicConstraints, false, + new BasicConstraints(false)); + + subjectAlternativeNames.fillInto(builder); + + X509Certificate cert = signCertificate(builder, caPrivKey); + + cert.checkValidity(new Date()); + cert.verify(caCert.getPublicKey()); + + KeyStore result = KeyStore.getInstance(KeyStore.getDefaultType() + /* , PROVIDER_NAME */); + result.load(null, null); + Certificate[] chain = { cert, caCert }; + result.setKeyEntry(authority.alias(), keyPair.getPrivate(), + authority.password(), chain); + + return result; + } + + private static X509Certificate signCertificate( + X509v3CertificateBuilder certificateBuilder, + PrivateKey signedWithPrivateKey) throws OperatorCreationException, + CertificateException { + ContentSigner signer = new JcaContentSignerBuilder(SIGNATURE_ALGORITHM) + .setProvider(PROVIDER_NAME).build(signedWithPrivateKey); + return new JcaX509CertificateConverter().setProvider( + PROVIDER_NAME).getCertificate(certificateBuilder.build(signer)); + } + + public static TrustManager[] getTrustManagers(KeyStore keyStore) + throws KeyStoreException, NoSuchAlgorithmException, + NoSuchProviderException { + String trustManAlg = TrustManagerFactory.getDefaultAlgorithm(); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(trustManAlg + /* , PROVIDER_NAME */); + tmf.init(keyStore); + return tmf.getTrustManagers(); + } + + public static KeyManager[] getKeyManagers(KeyStore keyStore, + Authority authority) throws NoSuchAlgorithmException, + NoSuchProviderException, UnrecoverableKeyException, + KeyStoreException { + String keyManAlg = KeyManagerFactory.getDefaultAlgorithm(); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManAlg + /* , PROVIDER_NAME */); + kmf.init(keyStore, authority.password()); + return kmf.getKeyManagers(); + } + + public static SSLContext newClientContext(KeyManager[] keyManagers, + TrustManager[] trustManagers) throws NoSuchAlgorithmException, + KeyManagementException, NoSuchProviderException { + SSLContext result = newSSLContext(); + result.init(keyManagers, trustManagers, null); + return result; + } + + public static SSLContext newServerContext(KeyManager[] keyManagers) + throws NoSuchAlgorithmException, NoSuchProviderException, + KeyManagementException { + SSLContext result = newSSLContext(); + SecureRandom random = new SecureRandom(); + random.setSeed(System.currentTimeMillis()); + result.init(keyManagers, null, random); + return result; + } + + private static SSLContext newSSLContext() throws NoSuchAlgorithmException { + try { + log.debug("Using protocol {}", SSL_CONTEXT_PROTOCOL); + return SSLContext.getInstance(SSL_CONTEXT_PROTOCOL + /* , PROVIDER_NAME */); + } catch (NoSuchAlgorithmException e) { + log.warn("Protocol {} not available, falling back to {}", SSL_CONTEXT_PROTOCOL, + SSL_CONTEXT_FALLBACK_PROTOCOL); + return SSLContext.getInstance(SSL_CONTEXT_FALLBACK_PROTOCOL + /* , PROVIDER_NAME */); + } + } + + public static long initRandomSerial() { + final Random rnd = new Random(); + rnd.setSeed(System.currentTimeMillis()); + // prevent browser certificate caches, cause of doubled serial numbers + // using 48bit random number + long sl = ((long) rnd.nextInt()) << 32 | (rnd.nextInt() & 0xFFFFFFFFL); + // let reserve of 16 bit for increasing, serials have to be positive + sl = sl & 0x0000FFFFFFFFFFFFL; + return sl; + } + +} diff --git a/app/src/main/java/org/littleshoot/proxy/mitm/CertificateSniffingMitmManager.java b/app/src/main/java/org/littleshoot/proxy/mitm/CertificateSniffingMitmManager.java new file mode 100755 index 0000000..0bdd0c5 --- /dev/null +++ b/app/src/main/java/org/littleshoot/proxy/mitm/CertificateSniffingMitmManager.java @@ -0,0 +1,100 @@ +package org.littleshoot.proxy.mitm; + +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; + +import org.littleshoot.proxy.MitmManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.netty.handler.codec.http.HttpRequest; + +/** + * {@link MitmManager} that uses the common name and subject alternative names + * from the upstream certificate to create a dynamic certificate with it. + */ +public class CertificateSniffingMitmManager implements MitmManager { + + private static final Logger LOG = LoggerFactory + .getLogger(CertificateSniffingMitmManager.class); + + private BouncyCastleSslEngineSource sslEngineSource; + + public CertificateSniffingMitmManager() throws RootCertificateException { + this(new Authority()); + } + + public CertificateSniffingMitmManager(Authority authority) + throws RootCertificateException { + try { + sslEngineSource = new BouncyCastleSslEngineSource(authority, true, + true); + } catch (final Exception e) { + throw new RootCertificateException( + "Errors during assembling root CA.", e); + } + } + + public SSLEngine serverSslEngine(String peerHost, int peerPort) { + return sslEngineSource.newSslEngine(peerHost, peerPort); + } + + public SSLEngine serverSslEngine() { + return sslEngineSource.newSslEngine(); + } + + public SSLEngine clientSslEngineFor(HttpRequest httpRequest, SSLSession serverSslSession) { + try { + X509Certificate upstreamCert = getCertificateFromSession(serverSslSession); + // TODO store the upstream cert by commonName to review it later + + // A reasons to not use the common name and the alternative names + // from upstream certificate from serverSslSession to create the + // dynamic certificate: + // + // It's not necessary. The host name is accepted by the browser. + // + String commonName = getCommonName(upstreamCert); + + SubjectAlternativeNameHolder san = new SubjectAlternativeNameHolder(); + + san.addAll(upstreamCert.getSubjectAlternativeNames()); + + LOG.debug("Subject Alternative Names: {}", san); + return sslEngineSource.createCertForHost(commonName, san); + + } catch (Exception e) { + throw new FakeCertificateException( + "Creation dynamic certificate failed", e); + } + } + + private X509Certificate getCertificateFromSession(SSLSession sslSession) + throws SSLPeerUnverifiedException { + Certificate[] peerCerts = sslSession.getPeerCertificates(); + Certificate peerCert = peerCerts[0]; + if (peerCert instanceof X509Certificate) { + return (X509Certificate) peerCert; + } + throw new IllegalStateException( + "Required java.security.cert.X509Certificate, found: " + + peerCert); + } + + private String getCommonName(X509Certificate c) { + LOG.debug("Subject DN principal name: {}", c.getSubjectDN().getName()); + for (String each : c.getSubjectDN().getName().split(",\\s*")) { + if (each.startsWith("CN=")) { + String result = each.substring(3); + LOG.debug("Common Name: {}", result); + return result; + } + } + throw new IllegalStateException("Missed CN in Subject DN: " + + c.getSubjectDN()); + } +} diff --git a/app/src/main/java/org/littleshoot/proxy/mitm/FakeCertificateException.java b/app/src/main/java/org/littleshoot/proxy/mitm/FakeCertificateException.java new file mode 100755 index 0000000..e46da34 --- /dev/null +++ b/app/src/main/java/org/littleshoot/proxy/mitm/FakeCertificateException.java @@ -0,0 +1,11 @@ +package org.littleshoot.proxy.mitm; + +public class FakeCertificateException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public FakeCertificateException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/app/src/main/java/org/littleshoot/proxy/mitm/Launcher.java b/app/src/main/java/org/littleshoot/proxy/mitm/Launcher.java new file mode 100755 index 0000000..2fb6826 --- /dev/null +++ b/app/src/main/java/org/littleshoot/proxy/mitm/Launcher.java @@ -0,0 +1,41 @@ +package org.littleshoot.proxy.mitm; + +import org.apache.log4j.xml.DOMConfigurator; +import org.littleshoot.proxy.HttpProxyServerBootstrap; +import org.littleshoot.proxy.impl.DefaultHttpProxyServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +public class Launcher { + + private static final Logger log = LoggerFactory.getLogger(Launcher.class); + + public static void main(final String... args) { + File log4jConfigurationFile = new File( + "src/test/resources/log4j.xml"); + if (log4jConfigurationFile.exists()) { + DOMConfigurator.configureAndWatch( + log4jConfigurationFile.getAbsolutePath(), 15); + } + try { + final int port = 9090; + + System.out.println("About to start server on port: " + port); + HttpProxyServerBootstrap bootstrap = DefaultHttpProxyServer + .bootstrapFromFile("./littleproxy.properties") + .withPort(port).withAllowLocalOnly(false); + + bootstrap.withManInTheMiddle(new CertificateSniffingMitmManager()); + + System.out.println("About to start..."); + bootstrap.start(); + + } catch (Exception e) { + log.error(e.getMessage(), e); + System.exit(1); + } + } + +} diff --git a/app/src/main/java/org/littleshoot/proxy/mitm/MergeTrustManager.java b/app/src/main/java/org/littleshoot/proxy/mitm/MergeTrustManager.java new file mode 100755 index 0000000..f5cfa3d --- /dev/null +++ b/app/src/main/java/org/littleshoot/proxy/mitm/MergeTrustManager.java @@ -0,0 +1,73 @@ +package org.littleshoot.proxy.mitm; + +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +public class MergeTrustManager implements X509TrustManager { + + private final X509TrustManager addedTm; + private final X509TrustManager javaTm; + + public MergeTrustManager(KeyStore trustStore) + throws NoSuchAlgorithmException, KeyStoreException { + if (trustStore == null) { + throw new IllegalArgumentException("Missed trust store"); + } + this.javaTm = defaultTrustManager(null); + this.addedTm = defaultTrustManager(trustStore); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + List issuers = new ArrayList(); + issuers.addAll(Arrays.asList(addedTm.getAcceptedIssuers())); + issuers.addAll(Arrays.asList(javaTm.getAcceptedIssuers())); + return issuers.toArray(new X509Certificate[issuers.size()]); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + try { + addedTm.checkServerTrusted(chain, authType); + } catch (CertificateException e) { + javaTm.checkServerTrusted(chain, authType); + } + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + try { + javaTm.checkClientTrusted(chain, authType); + } catch (CertificateException e) { + addedTm.checkClientTrusted(chain, authType); + } + } + + private X509TrustManager defaultTrustManager(KeyStore trustStore) + throws NoSuchAlgorithmException, KeyStoreException { + String tma = TrustManagerFactory.getDefaultAlgorithm(); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(tma); + tmf.init(trustStore); + TrustManager[] trustManagers = tmf.getTrustManagers(); + for (TrustManager each : trustManagers) { + if (each instanceof X509TrustManager) { + return (X509TrustManager) each; + } + } + throw new IllegalStateException("Missed X509TrustManager in " + + Arrays.toString(trustManagers)); + } + +} diff --git a/app/src/main/java/org/littleshoot/proxy/mitm/RootCertificateException.java b/app/src/main/java/org/littleshoot/proxy/mitm/RootCertificateException.java new file mode 100755 index 0000000..c3dafef --- /dev/null +++ b/app/src/main/java/org/littleshoot/proxy/mitm/RootCertificateException.java @@ -0,0 +1,11 @@ +package org.littleshoot.proxy.mitm; + +public class RootCertificateException extends Exception { + + private static final long serialVersionUID = 1L; + + public RootCertificateException(String message, Throwable t) { + super(message, t); + } + +} diff --git a/app/src/main/java/org/littleshoot/proxy/mitm/SubjectAlternativeNameHolder.java b/app/src/main/java/org/littleshoot/proxy/mitm/SubjectAlternativeNameHolder.java new file mode 100755 index 0000000..b24c750 --- /dev/null +++ b/app/src/main/java/org/littleshoot/proxy/mitm/SubjectAlternativeNameHolder.java @@ -0,0 +1,61 @@ +package org.littleshoot.proxy.mitm; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.cert.CertIOException; +import org.bouncycastle.cert.X509v3CertificateBuilder; + +public class SubjectAlternativeNameHolder { + + private static final Pattern TAGS_PATTERN = Pattern.compile("[" + + GeneralName.iPAddress + GeneralName.dNSName + "]"); + + private final List sans = new ArrayList(); + + public void addIpAddress(String ipAddress) { + sans.add(new GeneralName(GeneralName.iPAddress, ipAddress)); + } + + public void addDomainName(String subjectAlternativeName) { + sans.add(new GeneralName(GeneralName.dNSName, subjectAlternativeName)); + } + + public void fillInto(X509v3CertificateBuilder certGen) + throws CertIOException { + if (!sans.isEmpty()) { + ASN1Encodable[] encodables = sans.toArray(new ASN1Encodable[sans + .size()]); + certGen.addExtension(Extension.subjectAlternativeName, false, + new DERSequence(encodables)); + } + } + + public void addAll(Collection> subjectAlternativeNames) { + if (subjectAlternativeNames != null) { + for (List each : subjectAlternativeNames) { + sans.add(parseGeneralName(each)); + } + } + } + + private ASN1Encodable parseGeneralName(List nameEntry) { + if (nameEntry == null || nameEntry.size() != 2) { + throw new IllegalArgumentException(nameEntry != null ? String.valueOf(nameEntry) : "nameEntry is null"); + } + String tag = String.valueOf(nameEntry.get(0)); + Matcher m = TAGS_PATTERN.matcher(tag); + if (m.matches()) { + return new GeneralName(Integer.valueOf(tag), + String.valueOf(nameEntry.get(1))); + } + throw new IllegalArgumentException(String.valueOf(nameEntry)); + } +} diff --git a/app/src/main/res/drawable-hdpi/ic_mode_edit_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_mode_edit_black_24dp.png new file mode 100644 index 0000000..e531d72 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_mode_edit_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_mode_edit_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_mode_edit_black_24dp.png new file mode 100644 index 0000000..9efbaae Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_mode_edit_black_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_mode_edit_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_mode_edit_black_24dp.png new file mode 100644 index 0000000..87f8de1 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_mode_edit_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_mode_edit_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_mode_edit_black_24dp.png new file mode 100644 index 0000000..4af4ae6 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_mode_edit_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_mode_edit_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_mode_edit_black_24dp.png new file mode 100644 index 0000000..d6761ba Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_mode_edit_black_24dp.png differ diff --git a/app/src/main/res/layout/activity_change_filter.xml b/app/src/main/res/layout/activity_change_filter.xml new file mode 100644 index 0000000..7d02934 --- /dev/null +++ b/app/src/main/res/layout/activity_change_filter.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/alert_resp_filter.xml b/app/src/main/res/layout/alert_resp_filter.xml new file mode 100644 index 0000000..32176b4 --- /dev/null +++ b/app/src/main/res/layout/alert_resp_filter.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_filter.xml b/app/src/main/res/layout/item_filter.xml new file mode 100644 index 0000000..3185354 --- /dev/null +++ b/app/src/main/res/layout/item_filter.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml index b08e041..d87792f 100644 --- a/app/src/main/res/menu/activity_main_drawer.xml +++ b/app/src/main/res/menu/activity_main_drawer.xml @@ -28,6 +28,10 @@ android:checkable="true" android:title="高级功能">

+ + +