How do you go about transferring a disk-based workload to the respective memory-optimized design? How do you process memory-optimized tables? How important for performance are natively-compiled Stored Procedures? Artemakis Artemiou comes up with a step-by-step guide to implementing an in-memory OLTP solution .
کتابخانه jquery-form-validation
- Lingoes 2.8.1 – دیکشنری رایگان – برنامه ها | www.barnameha.com
- تصویر امنیتی (captcha) هایی که هکرها آنها را دور می زنند. (قسمت دوم) | tostring.blogfa.com
- Introducing OrcaMDF | improve.dk
- Using the C# Interactive Window that comes with Roslyn – Part 2 | Filip Ekberg's blog | blog.filipekberg.se
- Android Malware | www.schneier.com
- C# chess engine | www.felicepollano.com
- code contracts در سیشارپ | www.develop.com
- بالاخره امکان نوشتن، کامپایل و دیباگ مستقیم درایورها در ویژوال استودیو میسر شد | blogs.microsoft.co.il
- درایو C ویندوز 7 خود را کمی خلوت کنید! | www.hanselman.com
روش امن نگهداری پسورد کاربران
/* * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm). * Copyright (c) 2013, Taylor Hornby * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Text; using System.Security.Cryptography; namespace PasswordHash { /// <summary> /// Salted password hashing with PBKDF2-SHA1. /// Author: havoc AT defuse.ca /// www: http://crackstation.net/hashing-security.htm /// Compatibility: .NET 3.0 and later. /// </summary> public class PasswordHash { // The following constants may be changed without breaking existing hashes. public const int SALT_BYTE_SIZE = 24; public const int HASH_BYTE_SIZE = 24; public const int PBKDF2_ITERATIONS = 1000; public const int ITERATION_INDEX = 0; public const int SALT_INDEX = 1; public const int PBKDF2_INDEX = 2; /// <summary> /// Creates a salted PBKDF2 hash of the password. /// </summary> /// <param name="password">The password to hash.</param> /// <returns>The hash of the password.</returns> public static string CreateHash(string password) { // Generate a random salt RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider(); byte[] salt = new byte[SALT_BYTE_SIZE]; csprng.GetBytes(salt); // Hash the password and encode the parameters byte[] hash = PBKDF2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE); return PBKDF2_ITERATIONS + ":" + Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash); } /// <summary> /// Validates a password given a hash of the correct one. /// </summary> /// <param name="password">The password to check.</param> /// <param name="correctHash">A hash of the correct password.</param> /// <returns>True if the password is correct. False otherwise.</returns> public static bool ValidatePassword(string password, string correctHash) { // Extract the parameters from the hash char[] delimiter = { ':' }; string[] split = correctHash.Split(delimiter); int iterations = Int32.Parse(split[ITERATION_INDEX]); byte[] salt = Convert.FromBase64String(split[SALT_INDEX]); byte[] hash = Convert.FromBase64String(split[PBKDF2_INDEX]); byte[] testHash = PBKDF2(password, salt, iterations, hash.Length); return SlowEquals(hash, testHash); } /// <summary> /// Compares two byte arrays in length-constant time. This comparison /// method is used so that password hashes cannot be extracted from /// on-line systems using a timing attack and then attacked off-line. /// </summary> /// <param name="a">The first byte array.</param> /// <param name="b">The second byte array.</param> /// <returns>True if both byte arrays are equal. False otherwise.</returns> private static bool SlowEquals(byte[] a, byte[] b) { uint diff = (uint)a.Length ^ (uint)b.Length; for (int i = 0; i < a.Length && i < b.Length; i++) diff |= (uint)(a[i] ^ b[i]); return diff == 0; } /// <summary> /// Computes the PBKDF2-SHA1 hash of a password. /// </summary> /// <param name="password">The password to hash.</param> /// <param name="salt">The salt.</param> /// <param name="iterations">The PBKDF2 iteration count.</param> /// <param name="outputBytes">The length of the hash to generate, in bytes.</param> /// <returns>A hash of the password.</returns> private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes) { Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt); pbkdf2.IterationCount = iterations; return pbkdf2.GetBytes(outputBytes); } } }
banned.h
مطالبی توسط تیم Security Development Lifecycle مایکروسافت منتشر شده مبنی بر اینکه آنها هم یک سری از توابع استاندارد زبان C را در کدهای جدید خود ممنوع کردهاند. مستندات آنرا در مقاله زیر میتوانید مشاهده نمائید:
اخیرا فایل header آن نیز مطابق آخرین به روز رسانیهای مورد استفاده منتشر شده است:
استفاده از این توابع در کدهای جدید مایکروسافت ممنوع بوده و کدهای قدیمی نیز به مرور اصلاح خواهند شد.
جدیدترین تابعی که به این لیست اضافه شده ، تابع memcpy است که سر منشاء نقایص امنیتی زیر بوده است:
MS03-043 (Messenger Service)
MS03-044 (Help and Support)
MS05-039 (PnP)
MS04-011 (PCT)
MS05-030 (Outlook Express)
CVE-2007-3999 (MIT Kerberos v5)
CVE-2007-4000 (MIT Kerberos v5)
...!
#pragma deprecated (memcpy, RtlCopyMemory, CopyMemory)
warning C4995: 'memcpy': name was marked as #pragma deprecated
char dst[32];
memcpy(dst,src,len);
char dst[32];
memcpy_s(dst,sizeof(dst), src,len);
برای مطالعه بیشتر
Please Join me in welcoming memcpy() to the SDL Rogues Gallery
Unsafe at any speed: Memcpy() banished in Redmond
Good hygiene and Banned APIs
A Look Inside the Security Development Lifecycle at Microsoft
// student-model.ts export interface Student { id: number; name: string; }
// app.component.ts public model: Student; ngOnInit(): void { this.setDefaultValueForModel(); } saveForm(form: NgForm, evetn: Event) { evetn.preventDefault(); if (form.valid) { alert('everything is ok'); } } setDefaultValueForModel() { this.model = { id: 1, name: '' }; }
<form #form="ngForm" novalidate (submit)="saveForm(form,$event)"> <div> <label>Name</label> <input type="text" required name="name" autocomplete="off" [(ngModel)]="model.name" #name="ngModel"> <p [hidden]="name.valid || (name.pristine && !form.submitted)"> Name is required and should be minimum 4 characters. </p> </div> <div> <input type="submit" value="submit"> </div> </form>
<?xml version="1.0" encoding="utf-8"?> <packages> <package id="Microsoft.AspNet.OData" version="6.0.0" targetFramework="net462" /> <package id="Microsoft.AspNet.SignalR.Core" version="2.2.1" targetFramework="net462" /> <package id="Microsoft.AspNet.SignalR.Owin" version="1.2.2" targetFramework="net462" /> <package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net462" /> <package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net462" /> <package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net462" /> <package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="1.0.2" targetFramework="net462" /> <package id="Microsoft.Extensions.DependencyInjection" version="1.0.0" targetFramework="net462" /> <package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="1.0.0" targetFramework="net462" /> <package id="Microsoft.Net.Compilers" version="1.3.2" targetFramework="net462" developmentDependency="true" /> <package id="Microsoft.OData.Core" version="7.0.0" targetFramework="net462" /> <package id="Microsoft.OData.Edm" version="7.0.0" targetFramework="net462" /> <package id="Microsoft.Owin" version="3.0.1" targetFramework="net462" /> <package id="Microsoft.Owin.FileSystems" version="3.0.1" targetFramework="net462" /> <package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net462" /> <package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net462" /> <package id="Microsoft.Owin.StaticFiles" version="3.0.1" targetFramework="net462" /> <package id="Microsoft.Spatial" version="7.0.0" targetFramework="net462" /> <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net462" /> <package id="Owin" version="1.0" targetFramework="net462" /> <package id="System.Collections" version="4.0.11" targetFramework="net462" /> <package id="System.Collections.Concurrent" version="4.0.12" targetFramework="net462" /> <package id="System.ComponentModel" version="4.0.1" targetFramework="net462" /> <package id="System.Diagnostics.Debug" version="4.0.11" targetFramework="net462" /> <package id="System.Globalization" version="4.0.11" targetFramework="net462" /> <package id="System.Linq" version="4.1.0" targetFramework="net462" /> <package id="System.Linq.Expressions" version="4.1.0" targetFramework="net462" /> <package id="System.Reflection" version="4.1.0" targetFramework="net462" /> <package id="System.Resources.ResourceManager" version="4.0.1" targetFramework="net462" /> <package id="System.Runtime.Extensions" version="4.1.0" targetFramework="net462" /> <package id="System.Threading" version="4.0.11" targetFramework="net462" /> <package id="System.Threading.Tasks" version="4.0.11" targetFramework="net462" /> </packages>
PM>Update-Package -reinstall -Project YourProjectName
<?xml version="1.0" encoding="utf-8"?> <configuration> <appSettings> <add key="owin:AppStartup" value="OwinKatanaTest.OwinAppStartup, OwinKatanaTest" /> <!-- Owin App Startup Class --> <add key="webpages:Enabled" value="false" /> <!-- Disable asp.net web pages. Note that based on our current configuration, asp.net web forms, mvc and web pages won't work. This configuration is for owin stuffs only, for example asp.net web api & odata, signalr, etc. --> </appSettings> <system.web> <compilation debug="true" defaultLanguage="c#" enablePrefetchOptimization="true" optimizeCompilations="true" targetFramework="4.6.2"> <assemblies> <remove assembly="*" /> <!-- To improve app startup performance, our app will continue its work without this compilations, these are required for asp.net web forms, mvc and web pages. --> <add assembly="OwinKatanaTest" /> </assemblies> </compilation> <httpRuntime targetFramework="4.6.2" /> <httpModules> <!-- No need to these modules and handlers, owin handler itself will do everything for us --> <clear /> </httpModules> <httpHandlers> <clear /> </httpHandlers> <sessionState mode="Off" /> </system.web> <system.codedom> <compilers> <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701" /> </compilers> </system.codedom> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="Microsoft.AspNet.SignalR.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-2.2.1.0" newVersion="2.2.1.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Reflection" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Runtime.Extensions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" /> </dependentAssembly> <dependentAssembly> <assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" /> <bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" /> </dependentAssembly> </assemblyBinding> </runtime> <system.webServer> <validation validateIntegratedModeConfiguration="false" /> <modules runAllManagedModulesForAllRequests="false"> <!-- We're not going to remove all modules, some modules such as static & dynamic compression modules are really cool (-: --> <remove name="RewriteModule" /> <remove name="OutputCache" /> <remove name="Session" /> <remove name="WindowsAuthentication" /> <remove name="FormsAuthentication" /> <remove name="DefaultAuthentication" /> <remove name="RoleManager" /> <remove name="FileAuthorization" /> <remove name="UrlAuthorization" /> <remove name="AnonymousIdentification" /> <remove name="Profile" /> <remove name="UrlMappingsModule" /> <remove name="ServiceModel-4.0" /> <remove name="UrlRoutingModule-4.0" /> <remove name="ScriptModule-4.0" /> <remove name="Isapi" /> <remove name="IsapiFilter" /> <remove name="DigestAuthentication" /> <remove name="WindowsAuthentication" /> <remove name="ServerSideInclude" /> <remove name="DirectoryListing" /> <remove name="DefaultDocument" /> <remove name="CustomError" /> <remove name="Cgi" /> </modules> <defaultDocument> <!-- Default docs will be configured using owin static files middleware --> <files> <clear /> </files> </defaultDocument> <handlers> <!-- Only use this handler for all requests --> <clear /> <add name="Owin" verb="*" path="*" type="Microsoft.Owin.Host.SystemWeb.OwinHttpHandler, Microsoft.Owin.Host.SystemWeb" /> </handlers> <httpProtocol> <customHeaders> <clear /> </customHeaders> </httpProtocol> </system.webServer> </configuration>
بعد از build کردن پروژه، در صورت خطا داشتن از Referencesها، System.Reflection و System.Runtime.Extensions را حذف کنید.
using Microsoft.AspNet.SignalR; using Microsoft.OData; using Microsoft.OData.Edm; using Owin; using OwinKatanaTest.Model; using OwinKatanaTest.ODataControllers; using System.Collections.Generic; using System.Web.Http; using System.Web.OData.Builder; using System.Web.OData.Extensions; using System.Web.OData.Routing.Conventions; namespace OwinKatanaTest { public class OwinAppStartup { public void Configuration(IAppBuilder owinApp) { owinApp.Map("/odata", innerOwinAppForOData => { HttpConfiguration webApiODataConfig = new HttpConfiguration(); webApiODataConfig.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; webApiODataConfig.Formatters.Clear(); IEnumerable<IODataRoutingConvention> conventions = ODataRoutingConventions.CreateDefault(); ODataModelBuilder modelBuilder = new ODataConventionModelBuilder(webApiODataConfig); modelBuilder.Namespace = modelBuilder.ContainerName = "Test"; var categoriesSetConfig = modelBuilder.EntitySet<Category>("Categories"); var getBestCategoryFunctionConfig = categoriesSetConfig.EntityType.Collection.Function(nameof(CategoriesController.GetBestCategory)); getBestCategoryFunctionConfig.ReturnsFromEntitySet<Category>("Categories"); IEdmModel edmModel = modelBuilder.GetEdmModel(); webApiODataConfig.MapODataServiceRoute("default", "", builder => { builder.AddService(ServiceLifetime.Singleton, sp => conventions); builder.AddService(ServiceLifetime.Singleton, sp => edmModel); }); innerOwinAppForOData.UseWebApi(webApiODataConfig); }); owinApp.Map("/api", innerOwinAppForWebApi => { HttpConfiguration webApiConfig = new HttpConfiguration(); webApiConfig.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; webApiConfig.MapHttpAttributeRoutes(); webApiConfig.Routes.MapHttpRoute(name: "default", routeTemplate: "{controller}/{action}", defaults: new { action = RouteParameter.Optional }); innerOwinAppForWebApi.UseWebApi(webApiConfig); }); owinApp.Map("/signalr", innerOwinAppForSignalR => { innerOwinAppForSignalR.RunSignalR(new HubConfiguration { EnableDetailedErrors = true }); }); owinApp.UseStaticFiles(); owinApp.Run(async context => { await context.Response.WriteAsync("owin katana"); }); } } }
using OwinKatanaTest.Model; using System.Web.Http; using System.Web.OData; namespace OwinKatanaTest.ODataControllers { public class CategoriesController : ODataController { [HttpGet] public Category GetBestCategory() { return new Category { Id = 1, Name = "Test" }; } } }
using OwinKatanaTest.Model; using System.Collections.Generic; using System.Web.Http; namespace OwinKatanaTest.ApiControllers { public class ProductsController : ApiController { [HttpGet] [Route("products/{categoryId}")] public List<Product> GetProductsByCategoryId(int categoryId) { return new List<Product> { new Product { Id = 1 , Name = "Test" } }; } } }
<package id="Microsoft.Owin.Testing" version="3.0.1" targetFramework="net462" />
در ادامه تست خود را اینگونه مینویسیم
using Microsoft.Owin.Testing; using Microsoft.VisualStudio.TestTools.UnitTesting; using OwinKatanaTest; using System.Net; using System.Net.Http; using System.Threading.Tasks; namespace Test { [TestClass] public class Test { [TestMethod] public async Task TestWebApi() { using (TestServer server = TestServer.Create<OwinAppStartup>()) { HttpResponseMessage apiResponse = await server.HttpClient.GetAsync("/api/products/1"); apiResponse.EnsureSuccessStatusCode(); Assert.AreEqual(HttpStatusCode.OK, apiResponse.StatusCode); } } [TestMethod] public async Task TestOData() { using (TestServer server = TestServer.Create<OwinAppStartup>()) { HttpResponseMessage odataResponse = await server.HttpClient.GetAsync("odata/Categories/Test.GetBestCategory"); odataResponse.EnsureSuccessStatusCode(); Assert.AreEqual(HttpStatusCode.OK, odataResponse.StatusCode); } } } }
مفاهیم مقدماتی Data Warehouse :
OLTP ( Online Transaction Processing ) : سیستمهایی میباشند که برای اهداف اصلی سازمان استفاده میشوند و این سیستمها کار پردازش و ذخیره کردن دادهها را در OLTP Database انجام میدهند. مانند تمامی سیستمهای ERP,MIS,…
OLTP Database : پایگاه دادهی سیستمهای OLTP میباشد. به طور معمول هر تراکنش کاربر در کمترین زمان ممکن برروی این سیستمها ذخیره میگردد و در طول روز بارها دستورات ( Insert/Update/Delete ) برروی آنها انجام میشود. این پایگاههای داده، همان Main Data ها یا Source System ها میباشند.
ETL ( extract, transform, and load ) : مراحل انتقال داده از OLTP Database به پایگاه دادهی Stage میباشد. ETL سیستمی میباشد که توانایی اتصال به OLTP را دارد و اطلاعات را از OLTP واکشی میکند و به پایگاه دادهی Stage انتقال میدهد. سپس ETL دادهها را مجتمع ( integrates ) کرده و از Stage به DDS ( Dimensional Data Source ) انتقال میدهد .
Retrieves Data : عملیات واکشی دادهها طبق یک سری قوانین و قواعد میباشد .
برای انجام عملیات ETL دو روش وجود دارد
1. Data مجتمع ( Integrate ) و تمیز ( Data cleansing ) شود و در نهایت وارد Data Warehouse گردد.
2. Data وارد Data Warehouse گردد سپس مراحل مجتمع سازی و پاک سازی دادهها بر روی دادهها در خود Data Warehouse انجام گردد.
Consolidates Data : برخی شرکتها دادههای اصلی خودشان را در چندین پایگاه داده دارند. در این حالت برای انجام عملیات ETL باید دادهها تحکیم و مجتمع شوند و سپس در Data Warehouse ذخیره شوند.
به طور کلی موارد زیر در فرایند ETL در نظر گرفته میشود:
1. Data availability : برخی دادهها در یک سیستم وجود دارند ولی در سیستم دیگری وجود ندارند و یا تفاوت در نگهداری دادهها در سیستمهای مختلف داریم. مثلا در یک سیستم آدرس در سه فیلد نگه داری میشود (کشور-شهر-آدرس) اما در سیستمی دیگر در دو فیلد(کشور-آدرس) نگه داری میشود. در این حالت باید ما در ETL راه کار هایی برای مجتمع کردن این موارد در نظر بگیریم.
2. Time ranges : در سیستمهای مختلف امکان دارد بعدهای زمانی مختلف باشد . مثلا در یک سیستم بررسیها در بازهی ساعتی و در سیستم دیگر بررسیها در بازهی روزانه یا ماهانه باشد . بنابر این در تجمیع دادهها باید این مورد مد نظر گرفته شود.
3. Definitions : تعاریف در سیستمهای مختلف میتواند متفاوت باشد. مثلا در یک سیستم، مبلغ کل فاکتور شامل مالیات میباشد ولی در سیستمی دیگر این مبلغ فاقد مالیات میباشد.
4. Conversion : در فرآیند ETL باید باز از قواعد موجود در سیستمهای مختلف آگاهی داشته باشیم. مثلا در یک سیستم ممکن است دما را به صورت سانتیگراد و در دیگری فارنهایت نگه داری کنند.
5. Matching : باید بررسی لازم را انجام دهیم که کدام داده مرتبط با کدام سیستم میباشد. به عبارت دیگر کدام سیستم مالک داده میباشد و دقیقا دادهها در کدام سیستم معتبرتر میباشند. مثلا پرسنل، هم در سیستم حسابداری میباشند هم در سیستم پرسنلی؛ ولی معمولا دادههای اصلی از سیستم پرسنلی میآیند.
Periodically : عملیات واکشی دادهها ( Retrieves Data ) و مجتمع سازی دادهها ( Consolidates Data ) در فرآیند ETL فقط یکبار اتفاق نمیافتد و این مراحل در بازههای زمانی خاص تکرار میگردند. این واکشی و انتقال دادهها میتواند در روز چند بار تکرار شود یا میتواند چند روز یک بار اجرا گردد و این بستگی دارد به سیاست موجود در Data Warehouse .
DDS (Dimensional Data Source) (Data Warehouse) : یک پایگاه داده از نوع نرمال شده ( Normalized ) یا بعدی ( Dimensional ) میباشد. که دادههای مجتمع شده و تمیز شده سیستمهای OLTP را در خود جای داده است. این پایگاه داده برای واکشیهای سیستمهای آنالیز داده مورد استفاده قرار میگیرد. ورود اطلاعات در Data Warehouse به صورت Batch میباشد و به هیچ عنوان مانند پایگاه دادههای OLTP ویرایش دادهها به صورت Online و هر زمان که دادهها تغییر میکنند، صورت نمیگیرد. اطلاعات در Data Warehouse معمولا به صورت تجمیع شده روزانه، ماهانه، فصلی یا سالانه میباشد. DDS ها مجموعه ای از Dimensional Data Mart ها هستند. و عمدتا به صورت denormalized میباشند.
Dimensional Data Mart : مجموعه ای از جداول Fact , Dimension میباشند که در یک بیزینس خاص باهم در ارتباط و مشترک میباشند.
dimensional data store schemas : طراحیهای مختلفی از جداول Fact , Dimension در DDS وجود دارد که عبارتند از
1. Star schema : سادهترین روش پیاده سازی Data Warehouse
2. Snowflake : در این روش جداول Dimension کمی نرمال سازی بیشتری دارند. سیستمهای آنالیز داده با این روش بهتر کار میکنند.
3. Galaxy schemas : طراحی در این روش بسیار سخت و پیچیده میباشد. با این وجود فرایند ETL در این طراحی سادهتر انجام میشود.
نمونهی طراحی Star به صورت زیر میباشد :
تفاوتهای DDS و NDS :
1. در DDS ها هیچ گونه نرمال سازی خاصی انجام نمیدهیم و عملا تمامی جداول را دینرمال کرده ایم، در حالی که در NDS تمامی جداول تا سطح سوم و گاهی تا سطح پنجم نرمال شده اند.
2. سرعت واکشی و پردازش کوئریها روی DDS خیلی بیشتر از NDS ها میباشد.
3. در صورتی که نیاز باشد Data Warehouse های خیلی بزرگ طراحی کنیم با حجم بسیار زیاد توصیه میشود از NDS ها استفاده شود در حالی که برای Data Warehouse های کوچک و متوسط بهتر است از DDS ها استفاده شود.
تصویر طراحی یک (Enterprise Data Source = NDS) EDS در زیر آمده است :
History : جداول Data Warehouse میتوانند در طول زمان بسیار بزرگ شوند و دارای تعداد رکورد زیادی گردند. اینکه حداکثر دادههای چند سال را در Data Warehouse نگه داری کنیم بستگی به سیاستهای سازمانی دارد که سیستم OLAP برای آن تهیه میگردد. استفاده کردن از table partitioning میتواند در جبران افزایش تعداد رکورد کمک زیادی به ما بکند.
slowly changing dimension (SCD) : سه روش برای نگه داری سابقهی تغییرات در جداول Dimension وجود دارد.
1. SCD type 1 : هیچ گونه سابقهی تغییراتی را نگه داری نمیکنیم
2. SCD type 2 : سابقهی تغییرات در ردیفها نگه داری میشود. در این روش هر ردیف، شماره ردیف قبلی را دارد و تعداد نا محدودی از تغییرات را نگه داری میکنیم.
3. SCD type 3 : سابقهی تغییرات در ستونها نگه داری میشوند و فقط ردیف جاری و آخرین تغییرات را نگه داری میکنیم.
Query : فقط ETL حق تغییرات در Data Warehouse را دارد و کاربر نمیتواند Data Warehouse را تغییر دهد. البته کاربران حق Query کردن از Data Warehouse را دارند.
دقت داشته باشید که کوئریهای پیچیده در NDS ها بسیار کندتر از همان کوئری در DDS میباشد.
Business Intelligence : مجموعه ای از فعالیتها که در یک سازمان برای شناخت بهتر وضعیت Business آن سازمان انجام میشود. نتایج BI کمک بسیاری برای تصمیم گیریهای تکنیکی و استراتژیکی درون سازمان میکند. همچنین کمک به بهبود فرایندهای Business جاری میکند.
فعالیتهای Business Intelligence در سه دسته بندی قرار میگیرند :
1. Reporting : گزارشاتی که از Data Warehouse گرفته میشود و به کاربر نمایش داده میشود و عمدتا این گزارشات به صورت tabular form میباشند.
2. OLAP : فعالیتهای انجام شده روی MDB برای گرفتن گزارشات Drill-Down و ... میباشد.
3. Data mining : فرآیند واکشی و داده کاوی دادههای درون سیستم میباشد، که منجر به کشف الگوها و رفتارها و ارتباطات دادهها در سیستم میشود. توسط داده کاوی ما متوجه میشویم چرا برخی دادهها در سیستم تولید شده اند.
a. descriptive analytics : زمانی که از داده کاوی برای شرح وقایع گذشته و حال استفاده میشود.
b. predictive analytics : زمانی که از داده کاوی برای پیش بینی وقایع گذشته استفاده میشود.
Real time data warehouse : به DW هایی گفته میشود که در کمترین زمان، تغییرات OLTP را در خود خواهند داشت. امروزه این نوع DW ها تغییرات 5 دقیقه تا حداکثر 1 ساعت قبل را در خود دارند. برای دسترسی به چنین DW هایی دو راه زیر وجود دارد :
1. بر روی هر جدول، Trigger هایی باشد تا تغییرات را به DW انتقال دهد. (البته برای این منظور باید Business مربوط به ETL را در این تریگرها نوشت)
2. سورس برنامههای اصلی کاربر ( OLTP ) تغییر کند تا علاوه بر OLTP Database ها Data Warehouse را هم تغییر دهند.
روشهای فوق بسیار روی سرعت و کارایی برنامههای اصلی تاثیر خواهند گذاشت.
NDS ( Normalize Data Source ) : در صورتی که طراحی Data Warehouse به صورت Dimensional نباشد و به صورت Normalize باشد، نوع Data Warehouse از نوع NDS میباشد.
روش ساخت MDB :
OLTP Database -> ETL -> Stage Database -> DDS (Dimensional Data Source = Data Warehouse) -> SSAS -> MDB
روش سادهتر ساخت Data Warehouse :
منظور از Source System همان OLTP Database ها میباشد.
به خاطر داشته باشید که Source System ها جزئی از Data Warehouse نمیباشند.
از کاربردهای Data Warehouse میتوان به موارد زیر اشاره کرد
1. Data Mining
2. استفاده در گزارشات
3. تجمیع داده ها
Data Mining کمک به درک بهتر Business جاری در سازمان میکند. همچنین منجر به کشف دانش از درون دادهها میشود.
برای Data Mining میتوانید از انواع پایگاه دادههای موجود مانند رابطه ای ، سلسله مراتبی و چند بعدی استفاده کرد . حتا میتوان از فایلهای XML , Excel نیز استفاده کرد.
Customer Relationship Management (CRM) :
منظور از مشتری، مصرف کنندهی سرویسی است که سازمان شما ارایه میکند. یک سیستم CRM شامل تمامی برنامه ایی میباشد که تمام فعالیتهای مشتری را پشتیبانی میکند.
Operational Data Store (ODS) :
این پایگاه داده به صورت رابطه ای و نرمال شده میباشد و شامل تمامی اطلاعات پایگاه داده ای OLTP میباشد که در این پایگاه داده مجتمع شده اند. تفاوت ODS با Data Warehouse در این میباشد که دادهها در ODS با هر Transaction به روز میشوند (سرعت بروز رسانی اطلاعات در ODS بالاتر از DW میباشد).
Master Data Management (MDM) :
در یک نگاه میتوان دادهها را به دو دسته تقسیم کرد
1. transaction data
2. master data
transaction data : شامل داده ای transactional در سیستمهای OLTP میباشد.
master data : توضیح دهندهی Business جاری در سازمان میباشد.
برای تشخیص این دو نیاز است Business سازمان را به خوبی شناسایی نمایید. به عبارت دیگر رویدادهای Business ی همان transaction data میباشند و master data شامل پاسخهای این سوالها میباشد. چه کسی، چه چیزی و کجا در مورد Business transaction .
Customer data integration (CDI) : عبارت است از MDM در رابطه با مشتری داده ها. کار این قسمت عبارت است از واکشی، پاک سازی ، ذخیره سازی ، نگه داری و به اشتراک گذاشتن داده ای مشتری میباشد.
Unstructured Data : داده ای ذخیره شده در پایگاه داده ، structured Data میباشند و داده هایی مانند عکس و فیلم و صوت و ...
Service-Oriented Architecture (SOA) : یک متد ساخت برنامه میباشد که در این روش تمامی اجزا برنامه به صورت ماژول هایی دیده میشود که در آنها ارتباطات با دیگر سیستمها به صورت سرویس میباشد و این زیر سیستمها را میتوان در پروژههای مختلف به کار برد.
Real-Time Data Warehouse : DW هایی که توسط ETL به روز میشوند در هنگامی که یک Transaction روی OLTP اتفاق میافتد.
مراحل انتقال داده از OLTP Database به MDB به صورت زیر میباشد.
Data quality : مکانیسم اطمینان بخشی از این که در DW دادهای مناسب و درست وارد میشوند. به عبارت دیگر DQ همان firewall برای DW در مقابل دادههای نامناسب میباشد.
برای بهتر مشخص شدن مکان DQ شکل زیر را در نظر بگیرید
نحوهی حرکت داده ای از OLTP به MDB اولین چیزی میباشد که شما باید به آن فکر کنید و برای آن روشی را انتخاب نمایید قبل از ساخت Data Warehouse .
چهار روش برای معماری انتقال اطلاعات از OLTP به DW وجود دارد (البته به عنوان نمونه و شما میتوانید از روشهای دیگر و طراحیهای مختلف و ترکیبی نیز بهره ببرید)
1. single DDS : در این روش فقط Stage , DDS وجود دارد.
2. NDS + DDS : در این روش علاوه بر Stage,DDS از NDS نیز استفاده میشود.
3. ODS + DDS : در این روش از Stage,ODS,DDS استفاده میگردد.
4. federated data warehouse (FDW ) : استفاده از چندین DW که با هم تجمیع شده اند.
تصویر Single DDS :
تصویر NDS + DDS :
تصویر ODS + DDS :
تصویر federated data warehouse (FDW ) :
منبع : Building a Data Warehouse With Examples in SQL Server انتشارات Apress
زمانی که سیستم عامل های GUI مثل ویندوز به بازار آمدند، یکی از قسمتهای گرافیکی آنها AddressBar نام داشت که مسیر حرکت آنها را در فایل سیستم نشان میداد و در سیستم عاملهای متنی CLI با دستور cd یا pwd انجام میشد. بعدها در وب هم همین حرکت با نام BreadCrumb صورت گرفت که به عنوان مثال مسیر رسیدن به صفحهی یک محصول یا یک مقاله را نشان میداد. در یک پروژهی اندرویدی نیاز بود تا یک ساختار درختی را پیاده سازی کنم، ولی در برنامههای اندروید ایجاد یک درخت، کار هوشمندانه و مطلوبی نیست و روش کار به این صورت است که یک لیست از گروههای والد را نمایش داده و با انتخاب هر آیتم لیست به آیتمهای فرزند تغییر میکند. حالا مسئله این بود که کاربر باید مسیر حرکت خودش را بشناسد. به همین علت مجبور شدم یک BreadCrumb را برای آن طراحی کنم که در زیر تصویر آن را مشاهده میکنید.
از نکات جالب توجه در مورد این ماژول میتوان گفت که قابلیت این را دارد تا تصمیمات خود را بر اساس اندازههای مختلف صفحه نمایش بگیرد. به عنوان مثال اگر آیتمهای بالا بیشتر از سه عدد باشد و در صفحه جا نشود از یک مسیر جعلی استفاده میکند و همهی آیتمها با اندیس شماره 1 تا index-3 را درون یک آیتم با عنوان (...) قرار میدهد که من به آن میگویم مسیر جعلی. به عنوان نمونه مسیر تصویر بالا در صفحه جا شده است و نیازی به این کار دیده نشده است. ولی تصویر زیر از آن جا که مسیر، طول width صفحه نمایش رد کرده است، نیاز است تا چنین کاری انجام شود. موقعیکه کاربر آیتم ... را کلیک کند، مسیر باز شده و به محل index-3 حرکت میکند. یعنی دو مرحله به عقب باز میگردد.
نگاهی به کارکرد ماژول
قبل از توضیح در مورد سورس، اجازه دهید نحوهی استفاده از آن را ببینیم.
این سورس شامل دو کلاس است که سادهترین کلاس آن AndBreadCrumbItem میباشد که مشابه کلاس ListItem در بخش وب دات نت است و دو مقدار، یکی متن و دیگری Id را میگیرد:
سورس:
public class AndBreadCrumbItem { private int Id; private String diplayText; public AndBreadCrumbItem(int Id, String displayText) { this.Id=Id; this.diplayText=displayText; } public String getDiplayText() { return diplayText; } public void setDiplayText(String diplayText) { this.diplayText = diplayText; } public int getId() { return Id; } public void setId(int id) { Id = id; } }
به عنوان مثال میخواهیم یک breadcrumb را با مشخصات زیر بسازیم:
AndBreadCrumbItem itemhome=new AndBreadCrumbItem(0,"Home"); AndBreadCrumbItem itemproducts=new AndBreadCrumbItem(12,"Products"); AndBreadCrumbItem itemdigital=new AndBreadCrumbItem(15,"Digital"); AndBreadCrumbItem itemhdd=new AndBreadCrumbItem(56,"Hard Disk Drive");
AndBreadCrumb breadCrumb=new AndBreadCrumb(this); breadCrumb.AddNewItem(itemhome); breadCrumb.AddNewItem(itemproducts); breadCrumb.AddNewItem(itemdigital); breadCrumb.AddNewItem(itemhdd);
breadCumb.setContext(this);
پس از افزودن آیتم ها، تنظیمات زیر را اعمال نمایید:
LinearLayout layout=(LinearLayout)getActivity().findViewById(R.id.breadcumblayout); layout.setPadding(8, 8, 8, 8); breadCrumb.setLayout(layout); breadCrumb.SetTinyNextNodeImage(R.drawable.arrow); breadCrumb.setTextSize(25); breadCrumb.SetViewStyleId(R.drawable.list_item_style);
حال برای رسم آن متد UpdatePath را صدا میزنیم:
breadCrumb.UpdatePath();
الان اگر برنامه اجرا شود باید breadcrumb از چپ به راست رسم گردد. برای استفادههای فارسی، راست به چپ میتوانید از متد زیر استفاده کنید:
breadCrumb.setRTL(true);
در صورتیکه قصد دارید تنظیمات بیشتری چون رنگ متن، فونت متن و ... را روی هر المان اعمال کنید، از رویداد زیر استفاده کنید:
breadCrumb.setOnTextViewUpdate(new ITextViewUpdate() { @Override public TextView UpdateTextView(Context context, TextView tv) { tv.setTextColor(...); tv.setTypeface(...); return tv; } });
همچنین در صورتیکه میخواهید بدانید کاربر بر روی چه عنصری کلیک کرده است، از رویداد زیر استفاده کنید:
breadCumb.setOnClickListener(new IClickListener() { @Override public void onClick(int position, int Id) { //... } });
آخرین متد موجود که کمترین استفاده را دارد، متد SetNoResize است. در صورتیکه این متد با True مقداردهی گردد، عملیات تنظیم بر اساس صفحهی نمایش لغو میشود. این متد برای زمانی مناسب است که به عنوان مثال شما از یک HorozinalScrollView استفاده کرده باشید. در این حالت layout شما هیچ گاه به پایان نمیرسد و بهتر هست عملیات اضافه را لغو کنید.
نگاهی به سورس
کلاس زیر شامل بخشهای زیر است:
فیلدهای خصوصی
//=-=--=-=-=-=-=-=-=-=-=-=-=- Private Properties -=-=-=-=-=-=-=--=-=-= private List<AndBreadCrumbItem> items=null; private List<TextView> textViews; private int tinyNextNodeImage; private int viewStyleId; private Context context; private boolean RTL; private float textSize=20; private boolean noResize=false; LinearLayout layout; IClickListener clickListener; ITextViewUpdate textViewUpdate; LinearLayout.LayoutParams params ;
اینترفیسها هم با حرف I شروع و برای تعریف رویدادها ایجاد شدهاند. در ادامه از تعدادی متد get و Set برای مقدار دهی بعضی از فیلدهای خصوصی بالا استفاده شده است:
//=-=---=-=-=-=-- Constructor =--=-=-=-=-=--=-=- public AndBreadCrumb(Context context) { this.context=context; params = new LinearLayout.LayoutParams (LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); } //=-=-=--=--=-=-=-=-=-=-=-=- Public Properties --=-=-=-=-=-=--=-=-=-=-=-=- //each category would be added to create path public void AddNewItem(AndBreadCrumbItem item) { if(items==null) items=new ArrayList<>(); items.add(item); } // if you want a pointer or next node between categories or textviews public void SetTinyNextNodeImage(int resId) {this.tinyNextNodeImage=resId;} public void SetViewStyleId(int resId) {this.viewStyleId=resId;} public void setTextSize(float textSize) {this.textSize = textSize;} public boolean isRTL() { return RTL; } public void setRTL(boolean RTL) { this.RTL = RTL; } public void setLayout(LinearLayout layout) { this.layout = layout; } public void setContext(Context context) { this.context = context; } public boolean isNoResize() { return noResize; } public void setNoResize(boolean noResize) { this.noResize = noResize; }
بعد از آن به متدهای خصوصی میرسیم که متد زیر، متد اصلی ما برای ساخت breadcrumb است:
//primary method for render objects on layout private void DrawPath() { //stop here if essentail elements aren't present if (items == null) return ; if (layout == null) return; if (items.size() == 0) return; //we need to get size of layout,so we use the post method to run this thread when ui is ready layout.post(new Runnable() { @Override public void run() { //textviews created here one by one int position = 0; textViews = new ArrayList<>(); for (AndBreadCrumbItem item : items) { TextView tv = MakeTextView(position, item.getId()); tv.setText(item.getDiplayText()); textViews.add(tv); position++; } //add textviews on layout AddTextViewsOnLayout(); //we dont manage resizing anymore if(isNoResize()) return; //run this code after textviews Added to get widths of them TextView last_tv=textViews.get(textViews.size()-1); last_tv.post(new Runnable() { @Override public void run() { //define width of each textview depend on screen width BatchSizeOperation(); } }); } }); }
TextView tv=new TextView(this); tv.getWidth(); //return 0 layout.add(tv); tv.getWidth(); //return 0
TextView tv=new TextView(this); tv.post(new Runnable() { @Override public void run() { tv.getWidth(); //return x } });
باز میگردیم به متد DrawPath و داخل متد post
در اولین خط این پروسه به ازای هر آیتم، یک TextView توسط متد MakeTextView ساخته میشود که شامل کد زیر است:
private TextView MakeTextView(final int position, final int Id) { //settings for cumbs TextView tv=new TextView(this.context); tv.setEllipsize(TextUtils.TruncateAt.END); tv.setSingleLine(true); tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); tv.setBackgroundResource(viewStyleId); /*call custom event - this event will be fired when user click on one of textviews and returns position of textview and value that user sat as id */ tv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { SetPosition(position); clickListener.onClick(position, Id); } }); //if user wants to update each textviews if(textViewUpdate!=null) tv=textViewUpdate.UpdateTextView(context,tv); if(isRTL()) tv.setRotationY(180); return tv; }
در خطوط اولیه، یک Textview ساخته و متد Ellipsize را با Truncate.END مقداردهی مینماید. این مقدار دهی باعث میشود اگر متن، در Textview جا نشد، ادامهی آن با ... مشخص شود. در خط بعدی Textview را تک خطه معرفی میکنیم. در خط بعدی اندازهی قلم را بر اساس آنچه کاربر مشخص کرده است، تغییر میدهیم و بعد هم استایل را برای آن مقداردهی میکنیم. بعد از آن رویداد کلیک را برای آن مشخص میکنیم تا اگر کاربر بر روی آن کلیک کرد، رویداد اختصاصی خودمان را فراخوانی کنیم.
در خط بعدی اگر rtl با true مقدار دهی شده باشد، textview را حول محور Y چرخش میدهد تا برای زبانهای راست به چپ چون فارسی آماده گردد و در نهایت Textview ساخته شده و به سمت متد DrawPath باز میگرداند.
بعد از ساخته شدن TextViewها، وقت آن است که به Layout اضافه شوند که وظیفهی اینکار بر عهدهی متد AddTextViewOnLayout است:
//this method calling by everywhere to needs add textviews on the layout like master method :drawpath private void AddTextViewsOnLayout() { //prepare layout //remove everything on layout for recreate it layout.removeAllViews(); layout.setOrientation(LinearLayout.HORIZONTAL); layout.setVerticalGravity(Gravity.CENTER_VERTICAL); if(isRTL()) layout.setRotationY(180); //add textviews one by one int position=0; for (TextView tv:textViews) { layout.addView(tv,params); //add next node image between textviews if user defined a next node image if(tinyNextNodeImage>0) if(position<(textViews.size()-1)) { layout.addView(GetNodeImage(), params); position++; } } }
تا به اینجا کار چیدمان به ترتیب انجام شده است ولی از آنجا که اندازهی Layout در هر گوشی و در دو حالت حالت افقی یا عمودی نگه داشتن گوشی متفاوت است، نمیتوان به این چینش اعتماد کرد که به چه نحوی عناصر نمایش داده خواهند شد و این مشکل توسط متد BatchSizeOperation (تغییر اندازه دسته جمعی) حل میگردد. در اینجا هم باز متد post به آخرین textview اضافه شده است. به این علت که موقعیکه همهی textviewها در ui جا خوش کردند، بتوانیم به خاصیتهای ui آنها دستیابی داشته باشیم. حالا بعد از ترسیم باید اندازه آنها را اصلاح کنیم. قدم به قدم متد BatchSizeOperation را بررسی میکنیم:
//set textview width depend on screen width private void BatchSizeOperation() { //get width of next node between cumbs Bitmap tinyBmap = BitmapFactory.decodeResource(context.getResources(), tinyNextNodeImage); int tinysize=tinyBmap.getWidth(); //get sum of nodes tinysize*=(textViews.size()-1); ... }
//get width size of screen(layout is screen here) int screenWidth=GetLayoutWidthSize(); //get sum of arrows and cumbs width int sumtvs=tinysize; for (TextView tv : textViews) { int width=tv.getWidth(); sumtvs += width; }
private int GetLayoutWidthSize() { int width=layout.getWidth(); int padding=layout.getPaddingLeft()+layout.getPaddingRight(); width-=padding; return width; }
private void BatchSizeOperation() { .... //if sum of cumbs is less than screen size the state is good so return same old textviews if(sumtvs<screenWidth) return ; if(textViews.size()>3) { //make fake path MakeFakePath(); //clear layout and add textviews again AddTextViewsOnLayout(); } //get free space without next nodes -> and spilt rest of space to textviews count to get space for each textview int freespace =screenWidth-tinysize; int each_width=freespace/textViews.size(); //some elements have less than each_width,so we should leave size them and calculate more space again int view_count=0; for (TextView tv:textViews) { if (tv.getWidth()<=each_width) freespace=freespace-tv.getWidth(); else view_count++; } if (view_count==0) return; each_width=freespace/view_count; for (TextView tv:textViews) { if (tv.getWidth()>each_width) tv.setWidth(each_width); } }
اگر آیتمها بیشتر از سه عدد باشند، میتوانیم از حالت مسیر جعلی استفاده کنیم که توسط متد MakeFakePath انجام میشود. البته بعد از آن هم باید دوباره viewها را چینش کنیم تا مسیر جدید ترسیم گردد، چون ممکن است بعد از آن باز هم جا نباشد یا آیتمها بیشتر از سه عدد نیستند. در این حالت، حداقل کاری که میتوانیم انجام دهیم این است که فضای موجود را بین آنها تقسیم کنیم تا همهی کاسه، کوزهها سر آیتم آخر نشکند و متنش به ... تغییر یابد و حداقل از هر آیتم، مقداری از متن اصلی نمایش داده شود. پس میانگین فضای موجود را گرفته و بر تعداد المانها تقسیم میکنیم. البته این را هم باید در نظر گرفت که در تقسیم بندی، بعضی آیتمها آن مقدار پهنا را نیاز ندارند و با پهنای کمتر هم میشود کل متنشان را نشان داد. پس یک کار اضافهتر این است که مقدار پهنای اضافی آنها را هم حساب کنیم و فقط آیتمهایی را پهنا دهیم که به مقدار بیشتری از این میانگین احتیاج دارند. در اینجا کار به پایان میرسد و مسیر نمایش داده میشود.
نحوهی کارکرد متد MakeFakePath بدین صورت است که 4 عدد TextView را ایجاد کرده که المانهای با اندیس 0 و 2 و 3 به صورت نرمال و عادی ایجاد شده و همان کارکرد سابق را دارند. ولی المان شماره دو با اندیس 1 با متن ... نمایندهی آیتمهای میانی است و رویدادکلیک آن به شکل زیر تحریف یافته است:
//if elements are so much(mor than 3),we make a fake path to decrease elements private void MakeFakePath() { //we make 4 new elements that index 1 is fake element and has a rest of real path in its heart //when user click on it,path would be opened textViews=new ArrayList<>(4); TextView[] tvs=new TextView[4]; int[] positions= {0,items.size()-3,items.size()-2,items.size()-1}; for (int i=0;i<4;i++) { //request for new textviews tvs[i]=MakeTextView(positions[i],items.get(positions[i]).getId()); if(i!=1) tvs[i].setText(items.get(positions[i]).getDiplayText()); else { tvs[i].setText("..."); //override click event and change it to part of code to open real path by call setposition method and redraw path tvs[i].setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int pos = items.size() - 3; int id = items.get(pos).getId(); SetPosition(items.size() - 3); clickListener.onClick(pos, id); } }); } textViews.add(tvs[i]); } }