Thêm các quy tắc riêng của bạn vào Rational Software Analyzer

Rational® Software Analyzer (Nhà phân tích phần mềm) của IBM® là một khung phân tích tĩnh cho phép phát hiện các vấn đề tiềm ẩn trong mã nguồn. Nó có thể phát hiện một loạt các vấn đề, từ các vấn đề về phong cách mã hóa đến các chỗ rò tài nguyên và thiếu tiềm năng về các tham khảo đến con trỏ rỗng (null). Mặc dù phần mềm này khắc họa nhiều quy tắc khác nhau, cũng không thể trình bày hết các yêu cầu phân tích tĩnh của mọi người. Nhưng nhờ viết các quy tắc tùy chỉnh cho Rational Software Analyzer, các nhà phát triển có thể nắm được hầu hết các vấn đề mà công cụ này chưa có. Vì nó là một khung công tác, nên để chỉnh sửa và mở rộng nó cho các yêu cầu miền cụ thể là rất đơn giản. Bài viết này đưa bạn qua quá trình tạo một quy tắc có ích và trình bày cách làm cho nó tốt hơn. Ngoài ra, bạn sẽ thêm một bản sửa lỗi nhanh cho quy tắc đó để cho bạn có thể nhanh chóng giải quyết vấn đề nếu bạn tìm thấy nó trong mã nguồn của bạn.

Mohammed Mostafa, Kỹ sư phần mềm, IBM

Mohammed MostafaMostafa Mohammed là một nhà phát triển phần mềm cao cấp về phần mềm Rational của IBM tại Ottawa, Ontario, Canada và là kiến trúc sư chính về Rational Software Analyzer. Trong chín năm qua, ông vẫn đang làm về các ứng dụng mô hình hóa và phân tích tĩnh khác nhau. Mohammed đã làm về hỗ trợ kết hợp-so sánh UML cho cả các mô hình XDE lẫn EMF của IBM Rational và về trình mô hình hóa UML trong Rational Software Architect (Kiến trúc sư phần mềm Rational) của IBM. Ông cũng đã làm việc về tầng biểu đồ cho thời gian chạy GMF.



Joshua Tessier, Software Developer, IBM

Joshua TessierJoshua Tessier là một nhà phát triển phần mềm cho đội Rational Software Analyzer. Anh tốt nghiệp vào năm 2009 tại Đại học Carleton với bằng cử nhân khoa học về khoa học máy tính.



20 09 2011

Khi phát triển một ứng dụng, rất dễ mắc lỗi. Cho dù lỗi đó chỉ là sự bổ sung đơn giản của một đặc tính phụ hay một sự nghịch đảo logic ngẫu nhiên trong một dòng mã, các lỗi hầu như luôn luôn dẫn đến các lỗi phá hỏng các ứng dụng theo mọi kích thước. Những lỗi này rõ ràng bắt nguồn từ các nhà phát triển ứng dụng, nhưng thật dễ vô ý gây ra lỗi trong một ứng dụng, bất kể trình độ học vấn hoặc kinh nghiệm của các nhà phát triển.

Thật không may, các lỗi vẫn xuất hiện bất chấp những nỗ lực to lớn mà mọi người trong ngành công nghệ đã dùng để sản xuất mã chất lượng cao. Những nỗ lực này trải khắp từ sự nhấn mạnh đổi mới về quy trình linh hoạt để đánh giá mã ngang hàng. Do điều này, nên càng trở nên rõ ràng rằng chúng ta cần sự trợ giúp của các công cụ phân tích tự động hóa mạnh mẽ và linh hoạt. Các công cụ phân tích tĩnh là một cách để cải thiện toàn bộ chất lượng mã. Thay vì chờ cho đến khi ứng dụng đang ở trong giai đoạn xây dựng hoặc đang chạy, thì ngay trong quá trình phát triển loại công cụ này có thể phân tích mã nguồn và nhận ra các vấn đề tiềm ẩn, do đó làm giảm tổng số lỗi trong một ứng dụng. Các công cụ này có thể giúp nhận biết một loạt vấn đề, từ các lỗi đơn giản cho đến các vấn đề logic ứng dụng phức tạp.

Mặc dù các công cụ giúp nhận biết các vấn đề và nâng cao chất lượng mã, chúng chỉ đơn thuần là một sự trợ giúp, không phải là một sự thay thế cho các đánh giá và thử nghiệm mã thủ công. Một trong những lý do chính cho điều này là các công cụ chung không thể nhận ra được các vấn đề miền-cụ thể có liên quan đến logic nghiệp vụ hoặc đến các hướng dẫn cụ thể chỉ thích hợp trong tổ chức đó.

Bài viết này sẽ giới thiệu cho bạn về khung công tác và các công cụ phân tích tĩnh Rational® Software Analyzer của IBM®. Thông qua giao diện công khai do phần mềm này trưng ra, bạn có thể viết các quy tắc tùy chỉnh để kiểm soát các vấn đề miền-cụ thể và để nâng cao toàn bộ sự kiểm soát của công cụ. Mục đích của bài viết là để giới thiệu cho bạn API có thể mở rộng của Rational Software Analyzer và để hướng dẫn cho bạn viết các quy tắc tùy chỉnh riêng của mình để bạn có thể nắm bắt được các vấn đề không thể diễn tả chung chung được. Để thực hiện điều này, chúng ta sẽ mô tả hai cách thực hiện một phân tích khác nhau. Bạn cũng sẽ tìm hiểu cách tạo một bản sửa lỗi nhanh chóng cho một vấn đề.

Kiến thức và các công cụ cần thiết

Để làm theo bài viết này, ít nhất bạn cần một số kiến thức và kinh nghiệm với các công nghệ và các khái niệm sau:

  • Eclipse (Phiên bản 3.4 hoặc mới hơn).
  • Phát triển trình cắm thêm Eclipse.
  • Công nghệ Java™ (Phiên bản 1.5 hoặc mới hơn).

Ngoài ra, ít nhất bạn cần một số hiểu biết về các khái niệm và công nghệ sau đây:

  • Trừu tượng hóa các cây cú pháp
  • Các công cụ phát triển Java (JDT) trong Eclipse.
  • Rational Software Analyzer phiên bản 7.1.

Ngoài ra, bạn phải có bản Rational Software Analyzer đã cài đặt trên hệ thống của mình hoặc truy cập vào các trình cắm thêm của nó để có thể hoàn thành các bước trong bài viết này.

Hãy bắt đầu

Bước đầu tiên để viết một quy tắc tùy chỉnh là nhận ra đúng vấn đề mà bạn muốn phát hiện.

Nhận biết vấn đề

Trong ví dụ này, bạn sẽ giải quyết vấn đề các nhập khẩu vô ích trong mã nguồn Java. Mặc dù các nhập khẩu vô ích không ảnh hưởng đến hiệu năng thời gian chạy, chúng có thể gây nhầm lẫn cho người đọc và ảnh hưởng đến khả năng dễ đọc của mã này. Ngoài ra, bằng bất cứ giá nào cũng nên tránh các phụ thuộc vào các lớp không cần thiết. Xem xét trường hợp mã đó minh họa trong Liệt kê 1.

Liệt kê 1. Mã mẫu với một nhập khẩu vô ích
import org.eclipse.core.resources.IResource; public class 
Internal { //... code that does not use IResource }

Mã trong Liệt kê 1 nhập khẩu lớp org.eclipse.core.resources.IResource, lớp này tạo ra một phụ thuộc vào trình cắm thêm org.eclipse.core.resources. Mặc dù điều này có vẻ vô hại, hãy tưởng tượng xem điều gì sẽ xảy ra khi lớp Internal là một lớp cơ sở cho rất nhiều lớp bên trong của bạn. Với mỗi lần sử dụng lớp này, sẽ có một sự phụ thuộc ngầm định trong trình cắm thêm tài nguyên. Điều này có thể tạo ra một chuỗi các phụ thuộc dài vào trình cắm thêm không cần thiết.

Nhận biết giải pháp

Bước tiếp theo là để nhận biết giải pháp. Tuy nhiên, các giải pháp có thể khác nhau rất nhiều. Chúng có thể phức tạp và đòi hỏi triển khai thực hiện các phương thức nào đó và thay thế các thuật toán hoặc chúng có thể đơn giản như khi loại bỏ lỗi. Trong ví dụ này, giải pháp khá đơn giản: chỉ cần loại bỏ các nhập khẩu vô ích trong bất kỳ tệp Java nào. Việc nhận ra giải pháp đúng đóng một vai trò quan trọng trong việc hiểu biết đúng vấn đề đó và xác nhận xem vấn đề đã đến mức cần nhận biết bằng cách phân tích tĩnh chưa.


Viết quy tắc riêng của bạn

Tạo dự án và thêm các phụ thuộc cần thiết

Trước tiên, bạn phải tạo một dự án trình cắm thêm.

  1. Mở trình đơn File và chọn New > New Project.
  2. Chọn Plug-in Project (Dự án trình cắm thêm) trong hộp thoại và đặt một tên cho dự án. Với các ý định của trình diễn này, đặt tên dự án là com.ibm.rsar.example.

Tiếp theo, thêm các phụ thuộc cần thiết vào dự án.

  1. Mở tệp MANIFEST.MF trong thư mục META-INF trong dự án của bạn và chọn thẻ Dependencies (Các phụ thuộc).
  2. Nhấn Add, Chọn com.ibm.rsaz.analysis.core, và sau đó lưu tệp này.
  3. Làm theo các thủ tục tương tự và thêm com.ibm.rsaz.analysis.codereview.java làm phụ thuộc khác.

Bây giờ bạn đã tạo ra dự án và đã thêm vào các phụ thuộc cần thiết để bắt đầu. Vào lúc này, tệp MANIFEST.MF của bạn trông tương tự như trong Liệt kê 2.

Liệt kê 2. Tệp MANIFEST.MF đã cập nhật
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Example 
Plug-in Bundle-SymbolicName: com.ibm.rsar.example;singleton:=true
Bundle-Version: 1.0.0 Bundle-Activator: com.ibm.rsar.example.
Activator Bundle-Vendor: IBM Require-Bundle:org.eclipse.ui, 
org.eclipse.core.runtime, com.ibm.rsaz.analysis.core;bundle-
version="7.1.0", com.ibm.rsaz.analysis.codereview.java;bundle-
version="7.0.102", Bundle-RequiredExecutionEnvironment: 
JavaSE-1.6 Bundle-ActivationPolicy: lazy

Sử dụng các điểm mở rộng và tạo ra thể loại

Nếu bạn không quen với các khung công tác điểm mở rộng Eclipse, thì có rất nhiều tài nguyên trên mạng Internet sẽ giúp bạn làm quen với nó. Tuy nhiên, như là sự tương đồng: các điểm mở rộng giống như các ổ cắm điện còn các phần mở rộng giống như phích cắm. Các nhà thiết kế của một ứng dụng quy định các điểm mở rộng và các nhà phát triển muốn mở rộng ứng dụng có thể tạo các phần mở rộng. Trong ví dụ này, chúng ta dùng các điểm mở rộng sau:

  • com.ibm.rsaz.analysis.core.analysisCategory
  • com.ibm.rsaz.analysis.core.analysisRule

Một thể loại phân tích biểu diễn một nhóm các quy tắc trong khung công tác phân tích tĩnh. Theo mặc định, nó đơn giản cho phép mỗi quy tắc thực hiện riêng rẽ để xác định các vấn đề. Tuy nhiên, có thể có các trường hợp tại đó logic đặc biệt được yêu cầu trước và sau khi thực hiện từng quy tắc, sẽ nhắc sử dụng một thể loại tùy chỉnh. Một quy tắc phân tích là một bộ phận không tách rời của công việc sẽ được thực hiện trong suốt một phân tích. Nói cách khác, các quy tắc là cái để nhận biết các vấn đề trong mã nguồn.

Một điểm mở rộng chưa được đề cập trong hướng dẫn này là điểm mở rộng com.ibm.rsaz.analysis.core.analysisProvider. Một nhà cung cấp phân tích hỗ trợ các quy tắc và các thể loại nhóm cho một tên miền phân tích cụ thể. Một miền phân tích bao gồm các ngôn ngữ mã hóa khác hoặc các loại phân tích khác nhau. Trong ví dụ này, bạn sẽ làm việc với một vấn đề đánh giá lại mã Java™ đơn giản sẽ yêu cầu chỉ tạo ra một thể loại và một quy tắc.

Để bắt đầu, hãy chỉ ra một thể loại.

  1. Mở lại tệp MANIFEST.MF và chuyển sang thẻ Extensions.
  2. Trong bộ lọc Extension Point (Điểm mở rộng), nhập com.ibm.rsaz.analysis.core.analysisCategory, và chọn nó từ danh sách.

Bây giờ bạn phải quy định một vài thuộc tính cho phần mở rộng mà bạn đang xác định.

  1. Trường "id" phải là một mã định danh duy nhất cho thể loại. Với mục đích này, hãy gọi nó là com.ibm.rsar.demoCategory.
  2. Thêm một nhãn cho thể loại đó: Demo Category.
  3. Đối với lớp, hãy quy định thể loại đánh giá lại mã Java tiêu chuẩn:
    com.ibm.rsaz.analysis.codereview.java.CodeReviewCategory .
  4. Bạn phải chỉ rõ một nhà cung cấp. Thiết lập trường nhà cung cấp cho codereview.java.analysisProvider, là mã định danh của nhà cung cấp để xem xét lại mã Java..

Bạn vừa thêm một thể loại cho nhà cung cấp xem xét lại mã Java mà không cần viết bất kỳ mã nào!

Để kiểm tra xem điều này thực sự hoạt động chưa, hãy khởi động một môi trường thời gian chạy..

Lưu ý:
Hãy nhớ rằng điều này là tốt để phát triển, nhưng khi nói đến thời gian triển khai trình cắm thêm, bạn phải xuất khẩu trình cắm thêm và đặt nó trong môi trường phát triển.

Làm theo các bước sau để khởi động một môi trường thời gian chạy:

  1. Mở trình đơn Run (Chạy).
  2. Chọn Run Configurations.
  3. Nhấn đúp vào Eclipse Application để tạo ra một Eclipse Run Configuration (Cấu hình chạy Eclipse) mới.
  4. Chọn một vùng làm việc thích hợp và nhấn Run.

Sau khi đã nạp bàn làm việc, bạn có thể kiểm tra công việc của mình theo các bước sau:

  1. Mở trình đơn Run (Chạy).
  2. Chọn Analysis.
  3. Tạo một cấu hình phân tích mới bằng cách nhấn đúp vào Software Analyzer.
  4. Chuyển sang thẻ Rules.
  5. Mở rộng mục cây Java Code Review.

Bây giờ bạn sẽ thấy một màn hình giống như Hình 1.

Hình 1. Thể loại Trình diễn được hiển thị trong Java Code Review
Ảnh chụp màn hình của cây Các quy tắc và Các miền phân tích

Tạo quy tắc cơ bản

Bây giờ là lúc để tạo ra quy tắc của bạn. Trước khi bạn quy định phần mở rộng của mình, bạn cần phải tạo ra nó.

  1. Đầu tiên, tạo một lớp bằng cách nhấn chuột phải vào dự án của bạn (com.ibm.rsar.example) trong khung nhìn Package Explorer (Trình thám hiểm đóng gói).
  2. Sau đó, chọn New > Class từ trình đơn.
  3. Trong ví dụ này, đặt tên lớp của bạn là RuleAvoidUnusedImports.
  4. Rồi nhấn Finish.

Điều đó tạo ra lớp trong Liệt kê 3.

Liệt kê 3. Trình vỏ (shell) rỗng của quy tắc
package  com.ibm.rsar.example; public class AvoidUnusedImports { }
  1. Bước tiếp theo là thay đổi lớp để mở rộng AbstractCodeReviewRule.

AbstractCodeReviewRule là một lớp trừu tượng Java Code Review (Xem xét lại mã Java) có chứa một loạt các phương thức tiện ích và các hàm trợ giúp để viết các quy tắc xem xét lại mã. Bạn sẽ cần mở rộng phương thức analyze(AnalysisHistory, CodeReviewResource) và thêm System.out.println("Example"); dòng bên trong của nó. Phương thức phân tích là nơi tất cả mọi công việc được thực hiện; đó là những gì được gọi và nó thực hiện phân tích trong một quá trình quét. Ở đây, bạn sẽ chỉ cần làm cho nó hiển thị một số thông tin trên bàn điều khiển; tuy nhiên, bạn sẽ triển khai điều này trong phần tiếp theo.

Liệt kê 4. Quy tắc AvoidUnusedImports cơ bản
package com.ibm.rsar.example;
import com.ibm.rsaz.analysis.codereview.java.AbstractCodeReviewRule;
import com.ibm.rsaz.analysis.codereview.java.CodeReviewResource;
import com.ibm.rsaz.analysis.core.history.AnalysisHistory;
public class AvoidUnusedImports extends AbstractCodeReviewRule {
	public void analyze(AnalysisHistory history,
	CodeReviewResource resource) { 
	System.out.println("Avoid unused imports");
	}
}

Mã trong Liệt kê 4 (ở trên) là cấu trúc cốt lõi của một quy tắc trong khung công tác Rational Software Analyzer. Quy tắc này sẽ được thực hiện cho mỗi tệp Java được quy định trong phạm vi phân tích (bất kể đó là toàn bộ vùng làm việc hay một dự án cụ thể) và nó sẽ hiển thị "Avoid unused imports" (Tránh nhập khẩu vô ích) trên bàn điều khiển với mỗi tệp.

Bây giờ là lúc để bảo đảm rằng quy tắc được nạp và được hiển thị trong giao diện người dùng.

  1. Một lần nữa, mở tệp MANIFEST.MF rồi mở thẻ Extensions.
  2. Thêm một phần mở rộng mới bằng cách nhấn vào Add và chọn phần mở rộng com.ibm.rsaz.analysis.core.analysisRule.

Như các bạn đã làm với thể loại này, bây giờ bạn phải xác định các trường khác nhau để chỉ ra quy tắc mới được tạo của bạn.

  1. Đầu tiên, chọn lớp. Nhấn vào Browse và chọn quy tắc mà bạn tạo ra trước đó (com.ibm.rsar.example.AvoidUnusedImports).
  2. Chỉ rõ mã định danh ID của quy tắc này. Thông thường, tên lớp là đủ. Trong ví dụ này, chọn com.ibm.rsar.example.AvoidUnusedImports.
  3. Chỉ rõ một nhãn bằng cách gõ "Avoid Unused Imports" vào trường Label (Nhãn).
  4. Chỉ rõ thể loại. Trường thể loại phải tương ứng với ID của thể loại. Ở trên, bạn đã dùng com.ibm.rsar.demoCategory, do đó, nhập ID đó vào trường tương ứng.
  5. Thiết lập trường tính chặt chẽ là 0, nó biểu thị cho một đề xuất.

Vậy đó! Quy tắc của bạn bây giờ sẽ xuất hiện trong hộp thoại Analysis Configurations. Khởi động một môi trường thời gian chạy mới để kiểm tra xem tất cả mọi thứ đang làm việc. Hình 2 cho thấy những gì mong đợi.

Hình 2. Quy tắc Avoid Unused Imports
Cây với quy tắc tương tự đã kiểm tra kết hợp với thẻ Các đặc tính

Bây giờ, hãy thử những gì bạn đã tạo ra

  1. Đánh dấu chọn hộp kiểm tra để bảo đảm rằng quy tắc được chọn và sau đó nhấn Analyze (Phân tích).

Việc này sẽ thực hiện quy tắc trên tất cả các tệp Java trong phạm vi cấu hình đã chọn. Nếu ít nhất có một tệp Java trong phạm vi đó, bàn điều khiển sẽ hiển thị thông báo mà bạn đã quy định cho quy tắc để in ra:
"Tránh các nhập khẩu vô ích"


Các mẫu quy tắc chung

Cho đến nay, bạn đã tạo ra một quy tắc chỉ in một giá trị mã hóa cứng cho bàn điều khiển. Bây giờ bạn cần bắt đầu viết logic sẽ nhận biết các vấn đề trong mã nguồn. Để bắt đầu, chúng ta sẽ giới thiệu một vài mẫu rất đơn giản để viết các quy tắc có thể được dùng lại và sau đó chúng ta sẽ trình bày các ví dụ về bắt giữ các nhập khẩu vô ích. Mặc dù các mẫu này tương đối đơn giản, nhưng chúng giúp chúng ta nhận biết được hầu hết các vấn đề trong mã nguồn theo cách rất hiệu quả và hợp lý.

Mẫu Tìm nạp và Cờ

Mẫu đầu tiên là mẫu Tìm nạp và Cờ (Fetch và Flag). Mẫu này được sử dụng để xác định các phần tử trong một tệp chỉ đơn giản là không thuộc về nơi chúng đang ở hiện tại. Một ví dụ về việc này đang tìm kiếm các câu lệnh return trong một khối finally. Để định vị vấn đề này, bạn cần một quy tắc chỉ gồm có việc tìm nạp.

Việc tìm nạp (fetching) được định nghĩa như là việc tìm kiếm tài nguyên và nhận ra các phần tử đang quan tâm bên trong nó. Bất kỳ các phần tử nào nhận được với hoạt động tìm nạp này thì ngay lập tức được gắn cờ như là các vấn đề.

Mẫu Tìm nạp và Lọc

Mẫu thứ hai là mẫu Tìm nạp và Lọc (Fetch and Filter), mà bạn sẽ sử dụng sau này. Mẫu này giống với mẫu Fetch và Flag, vì nó cũng có một bước tìm nạp. Tuy nhiên, thay vì chỉ tìm nạp rồi gắn cờ cho các kết quả, mẫu này áp dụng một bước lọc để tìm sâu hơn vào các phần tử đã tìm thấy và xác định các phần tử có vấn đề. Mẫu này thường được áp dụng vài lần thông qua một quy tắc khi bạn đi sâu vào một cây cú pháp trừu tượng.

Một ví dụ về mẫu này là nhận ra tất cả các biến được khai báo là công cộng và gắn cờ cho chúng như là các vấn đề bảo mật tiềm năng. Quy tắc này sẽ phải tìm kiếm tất cả các biến và nhận ra biến nào là công cộng và lọc ra các biến khác.


Thêm chức năng cho quy tắc này bằng cách sử dụng Eclipse

Lúc này người dùng Eclipse dày dạn sẽ nhận thấy rằng Eclipse đã cung cấp các cảnh báo lập sẵn cho các nhập khẩu vô ích. Nhờ sử dụng các công cụ hiện có, bạn có thể tự động phát hiện bất kỳ các nhập khẩu vô ích nào và gắn cờ cho vấn đề theo một cách dễ thấy hơn.

Có hai phụ thuộc mới để thêm vào trình cắm thêm để truy cập chức năng này:

  • org.eclipse.core.resources
  • org.eclipse.jdt.core

Sau khi đã thêm chúng vào, tệp MANIFEST.MF trông giống như Liệt kê 5.

Liệt kê 5. Tệp MANIFEST.MF đã cập nhật
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name:
Example Plug-in Bundle-SymbolicName: com.ibm.rsar.example;singleton:=true
Bundle-Version: 1.0.0 Bundle-Activator: com.ibm.rsar.example.Activator Bundle-Vendor:
IBM Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime,
com.ibm.rsaz.analysis.core;bundle-version="7.1.0",
com.ibm.rsaz.analysis.codereview.java;bundle-version="7.0.102",
org.eclipse.jdt.core;bundle-version="3.4.4",
org.eclipse.core.resources;bundle-version="3.4.2"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6 
Bundle-ActivationPolicy: lazy

Bây giờ bạn cần thay đổi phương pháp phân tích của bạn để sử dụng các cảnh báo Eclipse. Nhắc lại: mỗi quy tắc phân tích có một phương pháp phân tích thực hiện tất cả công việc trong một quá trình phân tích. Ở trên, bạn chỉ cần hiển thị thông tin tới bàn điều khiển. Tuy nhiên, bây giờ là lúc để làm một chút gì đó thú vị hơn. Hãy thay thế các nội dung của phương pháp phân tích bằng đoạn mã trong Liệt kê 6.

Liệt kê 6. Phương pháp phân tích mới
public void analyze(AnalysisHistory history, CodeReviewResource 
resource) {
IResource iResource = resource.getIResource();
String historyId = history.getHistoryId();
try { 
IMarker[] markers =
      iResource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
false,
IResource.DEPTH_INFINITE);
	for (int i = 0; i < markers.length; i++) {
		IMarker marker = markers[i]; 
		int id = marker.getAttribute(IJavaModelMarker.ID, -1);
		if (id == IProblem.UnusedImport) {
int start = marker.getAttribute(IMarker.CHAR_START, 0);
int length = marker.getAttribute(IMarker.CHAR_END, 0) - start;
int lineNumber = marker.getAttribute(IMarker.LINE_NUMBER, 0);
ResourceAnalysisResult result = 
	new CodeReviewResult(iResource.getFullPath().toString(),
		 lineNumber,start,length,
		 iResource,this,historyId, true);
result.setOwner(this);
addHistoryResultSet(historyId, result);
			} 
		} 
	} catch (CoreException e) {
		Log.severe(e.getLocalizedMessage());
	}
}

Đoạn mã đó là tất cả logic mà bạn cần nhận biết các nhập khẩu vô ích trong một tệp nguồn Java trong Eclipse. Khi nào cần bạn chỉ phải thả mã này vào quy tắc, nó sẽ làm việc. Tuy nhiên, chúng ta sẽ phân chia nó theo từng phần một để giải thích việc này thực hiện cái gì.

Thứ nhất, quy tắc này làm theo mô hình Tìm nạp và Lọc. Đầu tiên chúng ta tìm nạp tất cả các dấu vấn đề Eclipse rồi lặp lại trên các phần tử đã tìm nạp để tìm ra các phần tử có vấn đề. Dưới đây là phân tích thống kê:

  1. Nhận IResource. IResource là biểu diễn của Eclipse của một tài nguyên hệ thống (chẳng hạn như một tệp) trong vùng làm việc. IResource này có chứa thông tin có ích mà bạn sẽ sử dụng.
    IResource iResource = resource.getIResource();
  2. Đây là bước tìm nạp. Dòng mã này yêu cầu IResource tìm tất cả các dấu vấn đề Java trong một tệp. Các dấu vấn đề là các cảnh báo và các lỗi mà trình biên dịch Java trong Eclipse trưng ra cho người dùng.
    IMarker[] markers =
    iResource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER,
    false,
    IResource.DEPTH_INFINITE);
  3. Đây là khởi đầu của bước lọc. Bạn phải lặp lại tất cả các dấu mà bạn thấy:
    for (int i = 0; i < markers.length; i++){
  4. Bây giờ bạn phải xác định đây là loại dấu nào. Eclipse sử dụng một trình định danh bằng số để xác định loại dấu:
    int id = marker.getAttribute(IJavaModelMarker.ID, -1);
  5. Đây là nơi mà logic của bước lọc của bạn diễn ra. Hãy nhận biết các dấu là UnusedImports:
    if (id == IProblem.UnusedImport) {
  6. Dòng này sẽ tạo ra một kết quả Rational Software Analyzer:
    ResourceAnalysisResult result =
    new CodeReviewResult(iResource.getFullPath().toString(), lineNumber, start,
    length, iResource, this, historyId, true);
  7. Bây giờ chúng ta thiết lập chủ sở hữu kết quả theo quy tắc đặc biệt này do nó đã tạo ra kết quả:
    result.setOwner(this);
  8. Cuối cùng, bước này thêm kết quả vào lịch sử của quá trình quét hiện tại. Một Analysis History (Lịch sử phân tích) biểu diễn một phân tích tĩnh quét qua một số tài nguyên.
    addHistoryResultSet(historyId,result);

Vậy đó. Có một vài dòng mà chúng ta bỏ qua để cho mọi thứ đơn giản hơn. Các dòng mà chúng ta bỏ qua đã có ở đó chỉ để nhận các giá trị cho mục đích nhận biết vị trí của vấn đề. Tất cả điều này đã được hoàn thành chỉ với khoảng 20 dòng mã.

Bây giờ là lúc để thử quy tắc và xem nó tìm thấy tất cả các nhập khẩu vô ích. Hãy làm theo các thủ tục giống như ở trên bằng cách khởi động một môi trường thời gian chạy và chạy quy tắc đó trên các tệp Java. Nếu có bất kỳ tệp nào có các nhập khẩu vô ích, bạn sẽ thấy một cái gì đó tương tự như Hình 3.

Hình 3. Quy tắc Avoid Unused Imports đang hoạt động
Tránh mã quy tắc nhập khẩu vô ích, dẫn đến UI

Sử dụng cây cú pháp trừu tượng khi bạn không thể sử dụng các dấu Eclipse

Thật không may, các dấu Eclipse không phải luôn luôn có mặt trong môi trường phát triển. Ví dụ, nếu bạn đang sử dụng tính năng dòng lệnh của ấn bản cho doanh nghiệp Rational Software Analyzer, các dấu sẽ không có mặt. Nếu bạn quyết định vô hiệu hóa cảnh báo Các nhập khẩu vô ích, thì các dấu này cũng sẽ không có mặt. Điều này chỉ ra một lỗi quan trọng trong logic của quy tắc, vì nó có một sự phụ thuộc không chỉ về cách sử dụng Eclipse như là một môi trường phát triển, mà nó cũng phụ thuộc vào việc có các cảnh báo được kích hoạt.

Để tránh lỗi này, bạn sẽ xây dựng lại quy tắc đó bằng cách sử dụng cây cú pháp trừu tượng (AST) của Java để nhận biết các vấn đề. AST là một biểu diễn cây của mã nguồn có chứa tất cả mọi thứ từ các khai báo biến đến các định nghĩa hàm và các cuộc gọi phương thức. Thay vì đi ngang qua cây bằng thủ công, bạn sẽ sử dụng các phương pháp tiện ích hiện có của Rational Software Analyzer để đi ngang qua cây và tìm nạp các nút thích hợp. Sau đó bạn sẽ sử dụng các khả năng lọc để thu hẹp việc tìm kiếm của bạn về các nhập khẩu vô ích.

Xác định các lớp đang được sử dụng

Bước đầu tiên để viết quy tắc này là việc nhận dạng các lớp đang được sử dụng trong tài nguyên. Thay vì đi ngang qua cây, bạn sẽ sử dụng khả năng Tìm kiếm của các công cụ phát triển Java (JDT) để nhận dạng các lớp đã sử dụng.

Để làm điều này, trước tiên bạn phải tạo một Search Requestor (Trình yêu cầu tìm kiếm).

  1. Tạo một lớp được gọi là UsageSearchRequestormở rộngSearchRequestor.
  2. Thêm một thành viên dữ liệu kiểu Set<IType> vào UsageSearchRequestor:
    private Set<IType>
    usedTypes = new HashSet<IType>(2);
  3. Thêm phương thức nhận (get) tương ứng cho thành viên dữ liệu của bạn:
    public Set<IType> getUsedTypes(){
    return usedTypes;
    }
  4. Thực hiện phương thức acceptSearchMatch (được định nghĩa trong SearchRequestor):
    public void acceptSearchMatch(SearchMatch match) throws CoreException {
    Object matchedElement = match.getElement();
    if(!match.isInsideDocComment()) {
    IType type = (IType) matchedElement;
    usedTypes.add(type.getFullyQualifiedName());
    }
    }

UsageSearchRequestor sẽ nhận dạng tất cả các loại đang được sử dụng trong một tệp nguồn. Nó sẽ được thông báo mỗi khi có một sự phù hợp với việc tìm kiếm mà bạn đang thực hiện.

Sửa đổi quy tắc

Bây giờ bạn phải sửa đổi quy tắc để sử dụng máy tìm kiếm và trình yêu cầu của bạn, nhưng chưa tận dụng các cảnh báo Eclipse.

  1. Đầu tiên, thêm một thành viên dữ liệu mới vào quy tắc để duy trì SearchEngine:
    static private final SearchEngine engine = new SearchEngine();
  2. Sửa đổi phương thức analyze trong quy tắc để sử dụng máy tìm kiếm:
    public void analyze(AnalysisHistory history,CodeReviewResource resource) {
    try { List<ASTNode>
    imports = resource.getTypedNodeList(resource .getResourceCompUnit(),
    ASTNode.IMPORT_DECLARATION, true);
    UsageSearchRequestor requestor = new UsageSearchRequestor();
    engine.searchDeclarationsOfReferencedTypes(resource
    .getICompilationUnit(), requestor, null);
    Set<String>usedTypes = requestor.getUsedTypes();
    for (ASTNode node : imports) {
    ImportDeclaration importDecl = (ImportDeclaration) node;
    String name = importDecl.getName().getFullyQualifiedName();
    if (!usedTypes.contains(name)){
    resource.generateResultsForASTNode(this, history .getHistoryId(), node);
    }
    }
    } catch (JavaModelException e) {
    Log.severe(e.getLocalizedMessage());
    }
    }

Bây giờ quy tắc này trông như trong Liệt kê 7.

Liệt kê 7. Quy tắc cuối cùng.
package com.ibm.rsar.example;
import java.util.List;
import java.util.Set;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.search.SearchEngine;
import com.ibm.rsaz.analysis.codereview.java.AbstractCodeReviewRule;
import com.ibm.rsaz.analysis.codereview.java.CodeReviewResource;
import com.ibm.rsaz.analysis.core.history.AnalysisHistory;
import com.ibm.rsaz.analysis.core.logging.Log;
public class AvoidUnusedImports extends AbstractCodeReviewRule {
  static private final SearchEngine engine = new SearchEngine();
  public void analyze(AnalysisHistory history, CodeReviewResource resource) {
	try { 
		List<ASTNode>
		imports = resource.getTypedNodeList(resource.getResourceCompUnit(),
			ASTNode.IMPORT_DECLARATION,
			true);
	    UsageSearchRequestor requestor = new
UsageSearchRequestor();
	    engine.searchDeclarationsOfReferencedTypes(
resource.getICompilationUnit(), requestor, null);
	    Set<String> usedTypes = requestor.getUsedTypes();
	    for (ASTNode node : imports) {
			ImportDeclaration importDecl = (ImportDeclaration) node;
			String name = importDecl.getName().getFullyQualifiedName();
			if (!usedTypes.contains(name)) {
resource.generateResultsForASTNode(this,	 history .getHistoryId(),	 node);
			 }
		 }
	} catch (JavaModelException e) {
	   Log.severe(e.getLocalizedMessage());
    }
  }
}

Phân chia logic

Như trước, đoạn mã ở trên chứa tất cả logic cần thiết để nhận biết các nhập khẩu vô ích trong một tệp nguồn. Tuy nhiên phiên bản này mạnh mẽ hơn nhiều và không phụ thuộc vào việc chúng ta có cho phép cảnh báo không.

  1. Lúc này, bạn sẽ tìm nạp tất cả các câu lệnh nhập khẩu trong cây cú pháp trừu tượng. Phương thức getTypedNodeList là một phương thức có ích được định nghĩa trong lớp CodeReviewResource để giúp tìm kiếm AST.
    List<ASTNode>
    imports = resource.getTypedNodeList(resource.getResourceCompUnit(),
    ASTNode.IMPORT_DECLARATION,true);
  2. Sau đó, bạn có thể gọi máy tìm kiếm và chuyển nó qua trình yêu cầu tìm kiếm của bạn:
    UsageSearchRequestor requestor = new UsageSearchRequestor();
    engine.searchDeclarationsOfReferencedTypes(
    resource.getICompilationUnit(),requestor, null);
  3. Khi việc tìm kiếm hoàn tất, bạn có thể giữ lấy tập các kiểu đã sử dụng từ trình yêu cầu:
    Set<String>
    usedTypes = requestor.getUsedTypes();
  4. Sau đó, bạn đi qua tất cả các nhập khẩu mà bạn đã thu gom được ở trên:
    for (ASTNode node : imports) {
    ImportDeclaration importDecl = (
    ImportDeclaration)node;
    String name = importDecl.getName().getFullyQualifiedName();
    if(!usedTypes.contains(name)){
    resource.generateResultsForASTNode(this,
    history.getHistoryId(),node); }
    }
  5. Và với mỗi nhập khẩu mà bạn vẫn chưa tìm thấy trong tập usedTypes của bạn, hãy tạo ra một kết quả (đây là một nhập khẩu vô ích). Dưới đây là bước lọc của bạn:
    for (ASTNode node : imports) {
    ImportDeclaration importDecl = (ImportDeclaration)node;
    String name = importDecl.getName().getFullyQualifiedName();
    if (!usedTypes.contains(name)){
    resource.generateResultsForASTNode(this,history.getHistoryId(),node);
    }
    }

Bây giờ bạn đã hoàn tất thành công quy tắc của bạn, vì vậy hãy thử nó, để đảm bảo rằng nó hoạt động. Khởi động lại một môi trường thời gian chạy và chạy quy tắc đó. Kết quả sẽ tương tự như ở trên. Tuy nhiên, lúc này, hãy thử vô hiệu hóa các cảnh báo Eclipse về các nhập khẩu vô ích.


Thêm một bản sửa lỗi nhanh

Bước cuối cùng để viết quy tắc bất kỳ là nhận biết xem có một bản sửa lỗi dễ dàng đang tồn tại cho nó không. Như đã nói ở trên, chỉ cần loại bỏ nhập khẩu là đủ. Vì có một bản sửa lỗi dễ dàng cho vấn đề này, rõ ràng bản sửa lỗi này phải được tự động hóa.

  1. Trước khi chuyển sang viết bản sửa lỗi nhanh, đầu tiên bạn phải thêm một phụ thuộc vào org.eclipse.jface.text trong tệp MANIFEST.MF. Liệt kê 8 cho thấy tệp MANIFEST.MF cuối cùng của chúng ta.
Liệt kê 8. Tệp MANIFEST.MF cuối cùng
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 
Bundle-Name: Example Plug-in 
Bundle-SymbolicName: com.ibm.rsar.example;singleton:=true
Bundle-Version: 1.0.0 Bundle-Activator: com.ibm.rsar.example.Activator
Bundle-Vendor: IBM
Require-Bundle: org.eclipse.ui, org.eclipse.core.runtime,
com.ibm.rsaz.analysis.core;bundle-version="7.1.0",
com.ibm.rsaz.analysis.codereview.java;bundle-version="7.0.102",
org.eclipse.jdt.core;bundle-version="3.4.4",
org.eclipse.core.resources;bundle-version="3.4.2",
org.eclipse.jface.text;bundle-version="3.4.2"
Bundle-RequiredExecutionEnvironment: JavaSE-1.6 
Bundle-ActivationPolicy: lazy
  1. Để tiếp tục, bạn phải tạo một lớp mới gọi là UnusedImportsQuickFix để mở rộng JavaCodeReviewQuickFix. Liệt kê 9 hiển thị mã mà bạn sẽ sử dụng cho lớp này.
Liệt kê 9. Mã cho bản sửa lỗi nhanh
package com.ibm.rsar.example; 
import org.eclipse.jdt.core.dom.AST; 
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jface.text.IDocument;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;
import com.ibm.rsaz.analysis.codereview.java.quickfix.JavaCodeReviewQuickFix; 
public class UnusedImportsQuickFix extends JavaCodeReviewQuickFix { 
	public TextEdit fixCodeReviewResult(ASTNode theNode,
	    IDocument docToChange) {
		AST ast = theNode.getAST(); 
		ASTRewrite rewriter = ASTRewrite.create(ast);
		rewriter.remove(theNode, null);
		TextEdit edits = new MultiTextEdit();
		edits.addChild(rewriter.rewriteAST(docToChange, null)); 
                 return edits; 
	}
}

Các bản sửa lỗi nhanh được cung cấp chỉ với lỗi ASTNode (nút từ cây cú pháp trừu tượng) và lỗi IDocument, là nguồn duy trì nút đó.

  1. Bước đầu tiên là lấy AST từ nút đó (về cơ bản là một nhà máy ASTNode). Tất cả các hàm của cây cú pháp trừu tượng thường đi qua các đối tượng AST:
    AST ast = theNode.getAST();
  2. Sau đó, tất cả những việc còn lại phải làm là loại bỏ nút đó và tạo ra edits (thay đổi trong tệp). Trong trường hợp này, việc chỉnh sửa chỉ đơn giản là loại bỏ nút nhập khẩu:
    ASTRewrite rewriter = ASTRewrite.create(ast);
    rewriter.remove(theNode, null);
    TextEdit edits = new MultiTextEdit();
    edits.addChild(rewriter.rewriteAST(docToChange, null));
    return edits;

Bây giờ bạn đã trình bày mã, bạn phải tích hợp bản sửa lỗi nhanh vào các bản sửa lỗi nhanh của Rational Software Analyzer. Tất cả các bản đó sử dụng điểm mở rộng analysisRuleQuickFix.

  1. Mở tệp MANIFEST.MF lần cuối và chuyển sang thẻ Extensions.
  2. Nhấn Add.
  3. Thay đổi lớp này thành lớp của bản sửa lỗi nhanh mà bạn vừa tạo ra. (Trình diễn này sẽ sử dụng com.ibm.rsar.example.UnusedImportsQuickFix).
  4. Thay đổi mã định danh ID của bản sửa lỗi nhanh thành com.ibm.rsar.example.UnusedImportsQuickFix.
  5. Mở rộng phần mở rộng quy tắc phân tích mà bạn đã thêm vào ở trên.
  6. Nhấn chuột phải vào quy tắc Unused Imports mà bạn tạo ra ở trên.
  7. Chọn New > quickFix.
  8. Thiết lập ID của bản sửa lỗi nhanh là một ID mà bạn đã sử dụng trong bước 3 (trong ví dụ này là com.ibm.rsar.example.UnusedImportsQuickFix).

Điều duy nhất còn lại là xem công việc của bạn đang hoạt động.

  1. Khởi động một môi trường thời gian chạy và chạy phân tích này một lần nữa. Nếu có kết quả, bạn sẽ thấy tương tự như trong Hình 4.
Hình 4. Kết quả với một bản sửa lỗi nhanh
Kết quả có một bản sửa lỗi nhanh

Lưu ý rằng có một hình ảnh bóng đèn nhỏ bên cạnh biểu tượng cho quy tắc này. Điều này chỉ ra rằng quy tắc này có một bản sửa lỗi nhanh sẵn sàng để sử dụng.

  1. Nhấn chuột phải vào kết quả và chạy bản sửa lỗi nhanh .

Tóm tắt

Tóm tắt này kết thúc quá trình viết một quy tắc và một bản sửa lỗi nhanh cho Rational Software Analyzer. Trong bài viết này, chúng ta trình bày hai cách viết khác nhau cho cùng một quy tắc và chúng ta đã thảo luận các lý do để chọn cả hai cách tiếp cận.

Ngoài ra, chúng ta đã trình bày cách tạo một bản sửa lỗi nhanh đơn giải cho quy tắc của bạn, giúp bạn tự động làm sạch mã nguồn của mình. Thật có ích là có các bản sửa lỗi nhanh cho các quy tắc tùy chỉnh, vì nó loại bỏ tính phức tạp về việc phải đưa ra một bản sửa lỗi và nó cũng chuẩn hóa cách sửa chữa vấn đề.

Với những gì bạn đã học được trong bài viết này, bạn có các công cụ và các điểm khởi đầu cần thiết để viết bất kỳ quy tắc nào cho miền cụ thể và bản sửa lỗi nhanh đi kèm của nó. Tuy nhiên, bạn phải có nhiệm vụ theo kịp các ý tưởng quy tắc và xác định cách để nhận ra chúng trong mã nguồn.


Tải về

Mô tảTênKích thước
Completed Rule and Quick Fix from this articleRSARDemoProject.zip12KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Rational, Công nghệ Java
ArticleID=758584
ArticleTitle=Thêm các quy tắc riêng của bạn vào Rational Software Analyzer
publish-date=09202011