├── .classpath
├── .gitignore
├── .project
├── LICENSE
├── README.md
├── lib
├── LICENSE
├── gson-2.2.4-javadoc.jar
├── gson-2.2.4-sources.jar
└── gson-2.2.4.jar
├── resource
├── DocumentIcon.png
├── FolderIcon.png
└── MacProIcon.png
├── screenshot
├── edit - before exiting.png
├── edit.png
├── main - new file.png
├── main - new folder error.png
├── main - popup menu.png
└── main.png
└── src
├── Main.java
├── controller
├── DiskManager.java
├── IO.java
├── MainController.java
└── SystemCore.java
├── model
└── sys
│ ├── Block.java
│ ├── Config.java
│ └── FCB.java
└── view
├── Config.java
├── DocumentIconPanel.java
├── EditView.java
└── MainView.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # workspace files are user-specific
2 | *.sublime-workspace
3 |
4 | # project files should be checked into the repository, unless a significant
5 | # proportion of contributors will probably not be using SublimeText
6 | *.sublime-project
7 |
8 | #sftp configuration file
9 | sftp-config.json
10 |
11 | # Eclipse
12 | *.pydevproject
13 | .metadata
14 | .gradle
15 | bin/
16 | tmp/
17 | *.tmp
18 | *.bak
19 | *.swp
20 | *~.nib
21 | local.properties
22 | .settings/
23 | .loadpath
24 |
25 | # External tool builders
26 | .externalToolBuilders/
27 |
28 | # Locally stored "Eclipse launch configurations"
29 | *.launch
30 |
31 | # CDT-specific
32 | .cproject
33 |
34 | # PDT-specific
35 | .buildpath
36 |
37 | # sbteclipse plugin
38 | .target
39 |
40 | # TeXlipse plugin
41 | .texlipse
42 |
43 | #Java
44 | *.class
45 |
46 | # Mobile Tools for Java (J2ME)
47 | .mtj.tmp/
48 |
49 | # Package Files #
50 | # *.jar
51 | *.war
52 | *.ear
53 |
54 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
55 | hs_err_pid*
56 |
57 | # Mac OS Icon File
58 | *.icns
59 |
60 | # local virtual disk data
61 | disk/
62 | *.bin
63 | fileSystem.jar
64 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | OS_File_Managment
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Tom Hu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | OS File Management
2 | =================
3 |
4 | 程序打开时会有点慢,因为需要初始化并读取数据,请耐心等待
5 |
6 | ## 目录
7 |
8 | - 项目简介
9 | - 界面简介
10 | - 项目结构
11 | - 实现思想
12 | - 改进之处
13 | - 代码文档
14 |
15 | ## 项目简介
16 |
17 | - 项目名称:
18 |
19 | 文件系统模拟程序
20 |
21 | - 开发环境:
22 |
23 | Eclipse + JDK SE 7 + Mac OS X 10.9.3
24 |
25 | - 项目概述:
26 |
27 | 本项目模拟了一个文件系统,用户可以做到新建文件、新建文件夹、编辑文件、删除文件、删除文件夹、格式化磁盘等操作,就像平时系统中真正的文件管理器一样,只是本项目将一些功能简化了,当用户退出时,所有的信息将会被保存在电脑真实的磁盘上,下次打开时重新读入
28 |
29 | ## 界面简介
30 |
31 | 整体界面借鉴了_[邱峰学长的项目](https://github.com/VioletHill/OS_FileSystem)_的界面
32 |
33 | - 主界面
34 |
35 | 1. 主界面,根目录
36 |
37 | 
38 |
39 | 2. 右键点击弹出菜单
40 |
41 | 
42 |
43 | 3. 新建文件时,输入文件名
44 |
45 | 
46 |
47 | 4. 新建文件夹时出错
48 |
49 | 
50 |
51 | - 编辑界面
52 |
53 | 1. 双击打开某文件,弹出编辑界面
54 |
55 | 
56 |
57 | 2. 关闭编辑界面时,如果有更改过文件内容,弹出对话框询问是否保存
58 |
59 | 
60 |
61 | ## 项目结构
62 |
63 | 本项目依照MVC的思想进行架构,各层次分明
64 |
65 | ```
66 | src/
67 | ├── controller/
68 | │ ├── DiskManager.java
69 | │ ├── IO.java
70 | │ ├── MainController.java
71 | │ └── SystemCore.java
72 | │
73 | ├── model/
74 | │ └── sys/
75 | │ ├── Block.java
76 | │ ├── Config.java
77 | │ └── FCB.java
78 | │
79 | └── view/
80 | ├── Config.java
81 | ├── DocumentIconPanel.java
82 | ├── EditView.java
83 | └── MainView.java
84 | ```
85 |
86 | #### View
87 |
88 | 这一部分关乎整个项目的用户界面
89 |
90 | 1. Config.java
91 |
92 | 该文件与界面无关,主要定义了一些整个View中用到的一些公共的参数,比如窗口长宽、文件图标的大小等
93 |
94 | ```
95 | package view;
96 |
97 | public class Config {
98 |
99 | // Window
100 | public static final int WINDOW_WIDTH = 800;
101 | public static final int WINDOW_HEIGHT = 600;
102 |
103 | // File icon
104 | public static final int FILE_ICON_SIZE = 80;
105 | public static final int FILE_ICON_PANEL_SIZE = 100;
106 |
107 | }
108 | ```
109 |
110 | 2. DocumentIconPanel.java
111 |
112 | 该文件与文件的图标有关,添加了鼠标点击事件的监听,支持单击选中、双击打开、右键弹出菜单
113 |
114 | 3. EditView.java
115 |
116 | 该文件与文件的编辑界面有关,可以编辑文字
117 |
118 | 如果对文件进行过修改,窗口标题中的 ___"Edit"___ 会变成 ___"Edited"___ ,并且在退出时会询问用户是否需要保存;如果没有修改则直接退出
119 |
120 | 4. MainView.java
121 |
122 | 该文件与程序主界面有关
123 |
124 | 主界面上部分由 ___后退(Back)___ 、 ___前往(Go)___ 、 ___地址栏___ 组成
125 |
126 | 下部分为主要部分,展示当前目录下的文件和文件夹,添加了鼠标点击事件的监听,右键弹出菜单,可以选择 ___"新建文件"___ 、 ___"新建文件夹"___ 、 ___"格式化"(仅根目录下)___
127 |
128 | #### Model
129 |
130 | 下文叙述时“磁盘”指本程序中的“虚拟磁盘”,“物理块”指“虚拟磁盘中的块”;当提到“真实物理磁盘”时,才是指用户电脑的“物理磁盘”磁盘中“物理块”
131 |
132 | 1. Config.java
133 |
134 | 该文件同样定义了一些整个Model中用到的一些公共参数
135 |
136 | ```
137 | package model.sys;
138 |
139 | import java.nio.charset.Charset;
140 |
141 | public class Config {
142 |
143 | public static final int BLOCK_SIZE = 512;
144 | public static final int BLOCK_COUNT = 8000;
145 |
146 | public static final int SYS_BLOCK_COUNT = 256;
147 |
148 | public static final int FILE_MAX_BLOCKS = 10;
149 |
150 | public static enum FILE_TYPE {
151 | FILE, DIRECTORY
152 | };
153 |
154 | public static final Charset CHARSET = Charset.forName("UTF-8");
155 | }
156 | ```
157 |
158 | 2. Block.java
159 |
160 | 该文件描述了磁盘中“物理块”的模型,它可以从真实物理磁盘中同步数据
161 |
162 | 3. FCB.java
163 |
164 | 该文件定义了文件系统中的文件控制块模型
165 |
166 |
167 | #### Controller
168 |
169 | 下文叙述时“磁盘”指本程序中的“虚拟磁盘”;当提到“真实物理磁盘”时,才是指用户电脑的“物理磁盘”
170 |
171 | 1. DiskManager.java
172 |
173 | 该部分负责磁盘位图的管理,以及请求/释放空间
174 |
175 | 2. IO.java
176 |
177 | 该部分负责对磁盘的读写
178 |
179 | 3. MainController.java
180 |
181 | 该部分是项目中最主要的部分,负责Model与View的交流,通过Model的数据来填充View并展示给用户
182 |
183 | 4. SystemCore.java
184 |
185 | 该部分是文件系统的核心,负责以下功能:
186 |
187 | - 创建文件
188 | - 创建文件夹
189 | - 删除文件
190 | - 删除文件夹
191 | - 读文件
192 | - 写文件
193 | - 进入目录
194 | - 离开目录
195 | - 格式化磁盘
196 |
197 | ## 实现思想
198 |
199 | - 文件存储空间管理:连续结构
200 | - 空间分配的方法:连续分配
201 | - 空闲空间的管理:位图法
202 | - 目录结构:树型目录结构
203 |
204 | 其中位图存放在前两个物理块中
205 |
206 | ## 改进之处
207 |
208 | 文件存储采用 ___连续结构___ 虽然容易实现,但是也有诸多缺点,文件不能动态增长,预留的空间如同一种浪费,采用 ___索引结构___ 会更好
209 |
210 | 另外,空间分配的方法其实与文件存储空间的管理方法有关,如果采用 ___索引结构___ ,那么空间可以按需分配,不需要给出预留空间
211 |
212 | ## 代码文档
213 |
214 | ___(见代码中的注释)___
215 |
--------------------------------------------------------------------------------
/lib/LICENSE:
--------------------------------------------------------------------------------
1 | Google Gson
2 |
3 | Apache License
4 | Version 2.0, January 2004
5 | http://www.apache.org/licenses/
6 |
7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8 |
9 | 1. Definitions.
10 |
11 | "License" shall mean the terms and conditions for use, reproduction,
12 | and distribution as defined by Sections 1 through 9 of this document.
13 |
14 | "Licensor" shall mean the copyright owner or entity authorized by
15 | the copyright owner that is granting the License.
16 |
17 | "Legal Entity" shall mean the union of the acting entity and all
18 | other entities that control, are controlled by, or are under common
19 | control with that entity. For the purposes of this definition,
20 | "control" means (i) the power, direct or indirect, to cause the
21 | direction or management of such entity, whether by contract or
22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
23 | outstanding shares, or (iii) beneficial ownership of such entity.
24 |
25 | "You" (or "Your") shall mean an individual or Legal Entity
26 | exercising permissions granted by this License.
27 |
28 | "Source" form shall mean the preferred form for making modifications,
29 | including but not limited to software source code, documentation
30 | source, and configuration files.
31 |
32 | "Object" form shall mean any form resulting from mechanical
33 | transformation or translation of a Source form, including but
34 | not limited to compiled object code, generated documentation,
35 | and conversions to other media types.
36 |
37 | "Work" shall mean the work of authorship, whether in Source or
38 | Object form, made available under the License, as indicated by a
39 | copyright notice that is included in or attached to the work
40 | (an example is provided in the Appendix below).
41 |
42 | "Derivative Works" shall mean any work, whether in Source or Object
43 | form, that is based on (or derived from) the Work and for which the
44 | editorial revisions, annotations, elaborations, or other modifications
45 | represent, as a whole, an original work of authorship. For the purposes
46 | of this License, Derivative Works shall not include works that remain
47 | separable from, or merely link (or bind by name) to the interfaces of,
48 | the Work and Derivative Works thereof.
49 |
50 | "Contribution" shall mean any work of authorship, including
51 | the original version of the Work and any modifications or additions
52 | to that Work or Derivative Works thereof, that is intentionally
53 | submitted to Licensor for inclusion in the Work by the copyright owner
54 | or by an individual or Legal Entity authorized to submit on behalf of
55 | the copyright owner. For the purposes of this definition, "submitted"
56 | means any form of electronic, verbal, or written communication sent
57 | to the Licensor or its representatives, including but not limited to
58 | communication on electronic mailing lists, source code control systems,
59 | and issue tracking systems that are managed by, or on behalf of, the
60 | Licensor for the purpose of discussing and improving the Work, but
61 | excluding communication that is conspicuously marked or otherwise
62 | designated in writing by the copyright owner as "Not a Contribution."
63 |
64 | "Contributor" shall mean Licensor and any individual or Legal Entity
65 | on behalf of whom a Contribution has been received by Licensor and
66 | subsequently incorporated within the Work.
67 |
68 | 2. Grant of Copyright License. Subject to the terms and conditions of
69 | this License, each Contributor hereby grants to You a perpetual,
70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71 | copyright license to reproduce, prepare Derivative Works of,
72 | publicly display, publicly perform, sublicense, and distribute the
73 | Work and such Derivative Works in Source or Object form.
74 |
75 | 3. Grant of Patent License. Subject to the terms and conditions of
76 | this License, each Contributor hereby grants to You a perpetual,
77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78 | (except as stated in this section) patent license to make, have made,
79 | use, offer to sell, sell, import, and otherwise transfer the Work,
80 | where such license applies only to those patent claims licensable
81 | by such Contributor that are necessarily infringed by their
82 | Contribution(s) alone or by combination of their Contribution(s)
83 | with the Work to which such Contribution(s) was submitted. If You
84 | institute patent litigation against any entity (including a
85 | cross-claim or counterclaim in a lawsuit) alleging that the Work
86 | or a Contribution incorporated within the Work constitutes direct
87 | or contributory patent infringement, then any patent licenses
88 | granted to You under this License for that Work shall terminate
89 | as of the date such litigation is filed.
90 |
91 | 4. Redistribution. You may reproduce and distribute copies of the
92 | Work or Derivative Works thereof in any medium, with or without
93 | modifications, and in Source or Object form, provided that You
94 | meet the following conditions:
95 |
96 | (a) You must give any other recipients of the Work or
97 | Derivative Works a copy of this License; and
98 |
99 | (b) You must cause any modified files to carry prominent notices
100 | stating that You changed the files; and
101 |
102 | (c) You must retain, in the Source form of any Derivative Works
103 | that You distribute, all copyright, patent, trademark, and
104 | attribution notices from the Source form of the Work,
105 | excluding those notices that do not pertain to any part of
106 | the Derivative Works; and
107 |
108 | (d) If the Work includes a "NOTICE" text file as part of its
109 | distribution, then any Derivative Works that You distribute must
110 | include a readable copy of the attribution notices contained
111 | within such NOTICE file, excluding those notices that do not
112 | pertain to any part of the Derivative Works, in at least one
113 | of the following places: within a NOTICE text file distributed
114 | as part of the Derivative Works; within the Source form or
115 | documentation, if provided along with the Derivative Works; or,
116 | within a display generated by the Derivative Works, if and
117 | wherever such third-party notices normally appear. The contents
118 | of the NOTICE file are for informational purposes only and
119 | do not modify the License. You may add Your own attribution
120 | notices within Derivative Works that You distribute, alongside
121 | or as an addendum to the NOTICE text from the Work, provided
122 | that such additional attribution notices cannot be construed
123 | as modifying the License.
124 |
125 | You may add Your own copyright statement to Your modifications and
126 | may provide additional or different license terms and conditions
127 | for use, reproduction, or distribution of Your modifications, or
128 | for any such Derivative Works as a whole, provided Your use,
129 | reproduction, and distribution of the Work otherwise complies with
130 | the conditions stated in this License.
131 |
132 | 5. Submission of Contributions. Unless You explicitly state otherwise,
133 | any Contribution intentionally submitted for inclusion in the Work
134 | by You to the Licensor shall be under the terms and conditions of
135 | this License, without any additional terms or conditions.
136 | Notwithstanding the above, nothing herein shall supersede or modify
137 | the terms of any separate license agreement you may have executed
138 | with Licensor regarding such Contributions.
139 |
140 | 6. Trademarks. This License does not grant permission to use the trade
141 | names, trademarks, service marks, or product names of the Licensor,
142 | except as required for reasonable and customary use in describing the
143 | origin of the Work and reproducing the content of the NOTICE file.
144 |
145 | 7. Disclaimer of Warranty. Unless required by applicable law or
146 | agreed to in writing, Licensor provides the Work (and each
147 | Contributor provides its Contributions) on an "AS IS" BASIS,
148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149 | implied, including, without limitation, any warranties or conditions
150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151 | PARTICULAR PURPOSE. You are solely responsible for determining the
152 | appropriateness of using or redistributing the Work and assume any
153 | risks associated with Your exercise of permissions under this License.
154 |
155 | 8. Limitation of Liability. In no event and under no legal theory,
156 | whether in tort (including negligence), contract, or otherwise,
157 | unless required by applicable law (such as deliberate and grossly
158 | negligent acts) or agreed to in writing, shall any Contributor be
159 | liable to You for damages, including any direct, indirect, special,
160 | incidental, or consequential damages of any character arising as a
161 | result of this License or out of the use or inability to use the
162 | Work (including but not limited to damages for loss of goodwill,
163 | work stoppage, computer failure or malfunction, or any and all
164 | other commercial damages or losses), even if such Contributor
165 | has been advised of the possibility of such damages.
166 |
167 | 9. Accepting Warranty or Additional Liability. While redistributing
168 | the Work or Derivative Works thereof, You may choose to offer,
169 | and charge a fee for, acceptance of support, warranty, indemnity,
170 | or other liability obligations and/or rights consistent with this
171 | License. However, in accepting such obligations, You may act only
172 | on Your own behalf and on Your sole responsibility, not on behalf
173 | of any other Contributor, and only if You agree to indemnify,
174 | defend, and hold each Contributor harmless for any liability
175 | incurred by, or claims asserted against, such Contributor by reason
176 | of your accepting any such warranty or additional liability.
177 |
178 | END OF TERMS AND CONDITIONS
179 |
180 | APPENDIX: How to apply the Apache License to your work.
181 |
182 | To apply the Apache License to your work, attach the following
183 | boilerplate notice, with the fields enclosed by brackets "[]"
184 | replaced with your own identifying information. (Don't include
185 | the brackets!) The text should be enclosed in the appropriate
186 | comment syntax for the file format. We also recommend that a
187 | file or class name and description of purpose be included on the
188 | same "printed page" as the copyright notice for easier
189 | identification within third-party archives.
190 |
191 | Copyright 2008-2011 Google Inc.
192 |
193 | Licensed under the Apache License, Version 2.0 (the "License");
194 | you may not use this file except in compliance with the License.
195 | You may obtain a copy of the License at
196 |
197 | http://www.apache.org/licenses/LICENSE-2.0
198 |
199 | Unless required by applicable law or agreed to in writing, software
200 | distributed under the License is distributed on an "AS IS" BASIS,
201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
202 | See the License for the specific language governing permissions and
203 | limitations under the License.
204 |
--------------------------------------------------------------------------------
/lib/gson-2.2.4-javadoc.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/lib/gson-2.2.4-javadoc.jar
--------------------------------------------------------------------------------
/lib/gson-2.2.4-sources.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/lib/gson-2.2.4-sources.jar
--------------------------------------------------------------------------------
/lib/gson-2.2.4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/lib/gson-2.2.4.jar
--------------------------------------------------------------------------------
/resource/DocumentIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/resource/DocumentIcon.png
--------------------------------------------------------------------------------
/resource/FolderIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/resource/FolderIcon.png
--------------------------------------------------------------------------------
/resource/MacProIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/resource/MacProIcon.png
--------------------------------------------------------------------------------
/screenshot/edit - before exiting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/edit - before exiting.png
--------------------------------------------------------------------------------
/screenshot/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/edit.png
--------------------------------------------------------------------------------
/screenshot/main - new file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/main - new file.png
--------------------------------------------------------------------------------
/screenshot/main - new folder error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/main - new folder error.png
--------------------------------------------------------------------------------
/screenshot/main - popup menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/main - popup menu.png
--------------------------------------------------------------------------------
/screenshot/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Course-Project/OS_File_Management/82b3e17108fdf0e7235a3505da6b3c5e376ba9f0/screenshot/main.png
--------------------------------------------------------------------------------
/src/Main.java:
--------------------------------------------------------------------------------
1 | import controller.MainController;
2 |
3 | /**
4 | *
5 | * @author Tom Hu
6 | *
7 | */
8 | public class Main {
9 |
10 | /**
11 | * @param args
12 | */
13 | public static void main(String[] args) {
14 | // TODO Auto-generated method stub
15 | MainController mainController = new MainController();
16 | mainController.showMainView();
17 |
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/controller/DiskManager.java:
--------------------------------------------------------------------------------
1 | package controller;
2 |
3 | import java.nio.ByteBuffer;
4 |
5 | import model.sys.Config;
6 | import model.sys.FCB;
7 |
8 | public class DiskManager {
9 |
10 | private byte[] bitMap;
11 | public IO io;
12 | public boolean online;
13 |
14 | /**
15 | * 构造函数
16 | * 为bitMap和io赋初值
17 | */
18 | public DiskManager() {
19 | super();
20 | this.bitMap = new byte[Config.BLOCK_COUNT / 8];
21 | this.io = new IO();
22 | this.online = false;
23 | }
24 |
25 | /**
26 | * 初始化
27 | */
28 | public void init() {
29 | if (this.online) {
30 | return;
31 | }
32 |
33 | // 初始化io
34 | this.io.init();
35 |
36 | // 从Block中读取位图
37 | this.bitMap = this.io.read(0, 2).array();
38 |
39 | this.online = true;
40 | }
41 |
42 | /**
43 | * 格式化磁盘
44 | */
45 | public void format() {
46 | this.online = false;
47 | this.io.online = false;
48 | for (int i = 0; i < Config.BLOCK_COUNT; i++) {
49 | this.io.write(i, 1, "");
50 | }
51 |
52 | // 重新初始化位图以及根目录
53 | this.io.initRootFile();
54 | }
55 |
56 | /**
57 | * 更新位图,存入Block
58 | */
59 | public void updateBitMap() {
60 | this.io.write(0, this.bitMap.length / 512 + 1, this.bitMap);
61 | }
62 |
63 | /**
64 | * 申请连续length个块
65 | * @param startBlockId 申请块的起始id
66 | * @param length 申请的块的个数
67 | */
68 | public void alloc(int startBlockId, int length) {
69 | int max = Math.min(startBlockId + length, this.bitMap.length);
70 | for (int i = 0; i < max; i++) {
71 | this.bitMap[i / 8] |= (1 << (i % 8));
72 | }
73 | this.updateBitMap();
74 | }
75 |
76 | /**
77 | * 连续释放length个块
78 | * @param startBlockId 释放块的起始id
79 | * @param length 释放的块的个数
80 | */
81 | public void free(int startBlockId, int length) {
82 | int max = Math.min(startBlockId + length, this.bitMap.length);
83 | for (int i = 0; i < max; i++) {
84 | this.bitMap[i / 8] &= (~(1 << (i % 8)));
85 | }
86 | this.updateBitMap();
87 | }
88 |
89 | /**
90 | * 所有数据写回磁盘
91 | */
92 | public void update() {
93 | this.io.update();
94 | }
95 |
96 | /**
97 | * 搜索连续长度为length的空闲块
98 | * @param length 所请求的空闲块链的长度
99 | * @return 空闲块链起点
100 | */
101 | public int getFreeSpace(int length) {
102 | return this.getFreeSpace(length, 256);
103 | }
104 |
105 | /**
106 | * 从startBlockId开始,搜索连续长度为length的空闲块链
107 | * @param length 所请求的空闲块链的长度
108 | * @param startBlockId 搜索的起点
109 | * @return 空闲块起点
110 | */
111 | public int getFreeSpace(int length, int startBlockId) {
112 | startBlockId = Math.max(Config.SYS_BLOCK_COUNT, startBlockId);
113 | int max = this.bitMap.length;
114 |
115 | for (int i = startBlockId; i < max; ) {
116 | int j = i;
117 | while (j < max && (this.bitMap[j / 8] & (byte) (1 << (j % 8))) == 0) {
118 | j++;
119 | }
120 | if (j - i >= length) {
121 | return i;
122 | }
123 | i = j + 1;
124 | }
125 | return 0;
126 | }
127 |
128 | /**
129 | * 根据FCB来获取文件内容
130 | * @param fcb
131 | * @return
132 | */
133 | public String readFile(FCB fcb) {
134 | ByteBuffer resultBuffer = this.io.read(fcb.dataStartBlockId, fcb.size);
135 | return new String(resultBuffer.array(), Config.CHARSET);
136 | }
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/src/controller/IO.java:
--------------------------------------------------------------------------------
1 | package controller;
2 |
3 | import java.io.File;
4 | import java.nio.ByteBuffer;
5 | import java.util.Vector;
6 |
7 | import com.google.gson.Gson;
8 |
9 | import model.sys.Block;
10 | import model.sys.Config;
11 | import model.sys.FCB;
12 |
13 | public class IO {
14 |
15 | private Vector blocks = new Vector();
16 | public boolean online = false;
17 |
18 | public IO() {
19 | super();
20 | }
21 |
22 | /**
23 | * 初始化所有block,从磁盘同步内容
24 | */
25 | public void init() {
26 | if (this.online) {
27 | return;
28 | }
29 |
30 | System.out.println("系统开始初始化...");
31 | File diskDir = new File("disk");
32 | boolean diskDirExists = diskDir.exists();
33 | if (!diskDirExists) {
34 | System.out.println("!!!物理磁盘上模拟信息不存在");
35 | System.out.println("在物理磁盘上新建目录...");
36 | diskDir.mkdirs();
37 | System.out.println("...在物理磁盘上新建目录完成");
38 | }
39 |
40 | // 初始化物理块
41 | System.out.println("初始化物理块...");
42 | for (int i = 0; i < Config.BLOCK_COUNT; i++) {
43 | Block block = new Block(i);
44 | block.sync();
45 | this.blocks.add(block);
46 | }
47 | System.out.println("...物理块初始化完成");
48 | this.online = true;
49 |
50 | // 本地已有文件信息,无需初始化
51 | if (diskDirExists) {
52 | return;
53 | }
54 |
55 | // 初始化位图以及根目录
56 | this.initRootFile();
57 | }
58 |
59 | /**
60 | * 初始化位图以及根目录
61 | */
62 | public void initRootFile() {
63 |
64 | // 初始化位图
65 | System.out.println("初始化位图...");
66 | byte[] bitMap = new byte[Config.BLOCK_COUNT / 8];
67 | bitMap[0] = 0x07; // 00000111,0和1号块被位图占用,2号块被根目录FCB占用
68 | bitMap[Config.SYS_BLOCK_COUNT / 8] |= (1 << (Config.SYS_BLOCK_COUNT % 8)); // 根目录的目录文件,放在256号块
69 |
70 | Gson gson = new Gson();
71 |
72 | // 初始化根目录
73 | // 根目录FCB,放在2号块,占一个块
74 | // 根目录目录文件,放在256号块
75 | System.out.println("初始化根目录...");
76 | FCB[] rootDir = new FCB[40];
77 | String rootDirJSON = gson.toJson(rootDir);
78 |
79 | FCB rootDirFCB = new FCB("root", -1, Config.FILE_TYPE.DIRECTORY,
80 | Config.FILE_MAX_BLOCKS, Config.SYS_BLOCK_COUNT, 2);
81 | String rootDirFCBJSON = gson.toJson(rootDirFCB);
82 |
83 | // 保存根目录FCB以及根目录目录文件
84 | this.write(rootDirFCB.blockId, 1, rootDirFCBJSON);
85 | this.write(Config.SYS_BLOCK_COUNT, rootDirFCB.size, rootDirJSON);
86 |
87 | System.out.println("...根目录初始化完成");
88 |
89 | // 保存位图
90 | this.write(0, bitMap.length / 512 + 1, bitMap);
91 | System.out.println("...位图初始化完成");
92 |
93 | // 写回物理磁盘
94 | this.update();
95 | System.out.println("...系统初始化完成");
96 | }
97 |
98 | /**
99 | * 将所有块的数据写回物理磁盘
100 | */
101 | public void update() {
102 | System.out.println("系统数据写回物理磁盘...");
103 | for (int i = 0; i < Config.BLOCK_COUNT; i++) {
104 | this.blocks.get(i).update();
105 | }
106 | System.out.println("...系统数据保存完毕");
107 | }
108 |
109 | /**
110 | * 从Block中读取内容
111 | *
112 | * @param startBlockId
113 | * 起始的块的编号
114 | * @param length
115 | * 连续读入的块的个数
116 | * @return 返回ByteBuffer,包含所需要的字符串内容
117 | */
118 | public ByteBuffer read(int startBlockId, int length) {
119 | ByteBuffer resultBuffer = ByteBuffer.allocate(0);
120 |
121 | for (int i = startBlockId; i < startBlockId + length; i++) {
122 | ByteBuffer currentBinDataBuffer = this.blocks.get(i).getBinData();
123 | byte[] temp = resultBuffer.array();
124 | resultBuffer.rewind();
125 | resultBuffer.get(temp, 0, temp.length);
126 |
127 | // resize
128 | resultBuffer = ByteBuffer.allocate(temp.length
129 | + currentBinDataBuffer.limit());
130 |
131 | // put back
132 | resultBuffer.put(temp, 0, temp.length);
133 |
134 | // append
135 | resultBuffer.put(currentBinDataBuffer.array(), 0,
136 | currentBinDataBuffer.limit());
137 |
138 | if (currentBinDataBuffer.limit() < currentBinDataBuffer.capacity()) {
139 | break;
140 | }
141 | }
142 |
143 | resultBuffer.rewind();
144 | return resultBuffer;
145 | }
146 |
147 | /**
148 | * 向Block中写入字符串内容
149 | *
150 | * @param startBlockId
151 | * 起始的块的编号
152 | * @param length
153 | * 连续写入的块的个数
154 | * @param content
155 | * 写入的内容
156 | */
157 | public void write(int startBlockId, int length, String content) {
158 | int i = startBlockId;
159 | int offset = 0;
160 | int l = Math.min(Config.BLOCK_SIZE, content.length() - offset);
161 | while (l > 0 && i < startBlockId + length) {
162 | this.blocks.get(i).wipe(); // 清空该块,从头写起
163 | this.blocks.get(i).getBinData()
164 | .put(content.getBytes(Config.CHARSET), offset, l); // 写入数据
165 | this.blocks.get(i++).getBinData().flip(); // 修改limit,保证刚刚被写入的数据在恰好在有效范围内
166 |
167 | offset += l;
168 | l = Math.min(Config.BLOCK_SIZE, content.length() - offset);
169 | }
170 |
171 | // 内容大小小于写入的块,将之后的块清空
172 | while (i < startBlockId + length) {
173 | this.blocks.get(i).wipe();
174 | this.blocks.get(i++).getBinData().flip();
175 | }
176 | }
177 |
178 | /**
179 | * 向Block中写入byte[]数据
180 | *
181 | * @param startBlockId
182 | * 起始块的编号
183 | * @param length
184 | * 连续写入的块的个数
185 | * @param content
186 | * 需要写入的byte[]数据
187 | */
188 | public void write(int startBlockId, int length, byte[] content) {
189 | int i = startBlockId;
190 | int offset = 0;
191 | int l = Math.min(Config.BLOCK_SIZE, content.length - offset);
192 | while (l > 0 && i < startBlockId + length) {
193 | this.blocks.get(i).wipe(); // 清空该块,从头写起
194 | this.blocks.get(i).getBinData().put(content, offset, l); // 写入数据
195 | this.blocks.get(i++).getBinData().flip(); // 修改limit,保证刚刚被写入的数据在恰好在有效范围内
196 |
197 | offset += l;
198 | l = Math.min(Config.BLOCK_SIZE, content.length - offset);
199 | }
200 |
201 | // 内容大小小于写入的块,将之后的块清空
202 | while (i < startBlockId + length) {
203 | this.blocks.get(i).wipe();
204 | this.blocks.get(i++).getBinData().flip();
205 | }
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/src/controller/MainController.java:
--------------------------------------------------------------------------------
1 | package controller;
2 |
3 | import java.awt.event.ActionEvent;
4 | import java.awt.event.ActionListener;
5 | import java.awt.event.KeyEvent;
6 | import java.awt.event.MouseEvent;
7 | import java.awt.event.MouseListener;
8 | import java.awt.event.WindowEvent;
9 | import java.awt.event.WindowListener;
10 |
11 | import javax.swing.JFrame;
12 | import javax.swing.JMenuItem;
13 | import javax.swing.JOptionPane;
14 | import javax.swing.JPopupMenu;
15 | import javax.swing.KeyStroke;
16 |
17 | import model.sys.Config.FILE_TYPE;
18 | import model.sys.FCB;
19 | import view.DocumentIconPanel;
20 | import view.EditView;
21 | import view.MainView;
22 |
23 | import com.google.gson.Gson;
24 |
25 | /**
26 | *
27 | * @author Tom Hu
28 | *
29 | */
30 | public class MainController {
31 |
32 | private MainView view;
33 |
34 | private SystemCore systemCore;
35 |
36 | /**
37 | * 构造函数
38 | */
39 | public MainController() {
40 | super();
41 |
42 | // Initialize systemCore
43 | this.systemCore = new SystemCore();
44 |
45 | // UI Methods
46 | this.configureMainView();
47 | }
48 |
49 | // UI Methods
50 | /**
51 | * 初始化主界面
52 | */
53 | private void configureMainView() {
54 | // 根据当前目录文件初始化view
55 | this.view = new MainView(this.systemCore.currentDir);
56 |
57 | // 添加右键监听
58 | this.view.addRightClickListener(this.rightClickListener);
59 |
60 | // 添加后退按钮监听
61 | this.view.addBackButtonActionListener(this.backButtonActionListener);
62 |
63 | // 添加前往按钮监听
64 | this.view.addGoButtonActionListener(this.goButtonActionListener);
65 |
66 | // 添加关闭事件监听
67 | this.view.addWindowListener(this.mainWindowListener);
68 |
69 | this.configureContentPanel();
70 |
71 | // 显示当前路径
72 | this.view.addressTextField.setText(this.systemCore.getCurrentPath());
73 | }
74 |
75 | /**
76 | * 初始化内容面板
77 | */
78 | private void configureContentPanel() {
79 | // 为每个图标添加监听
80 | this.addListenerForEachDocumentIcon();
81 | }
82 |
83 | /**
84 | * 显示主界面
85 | */
86 | public void showMainView() {
87 | this.view.showView();
88 | }
89 |
90 | /**
91 | * 显示编辑界面
92 | *
93 | * @param fcb
94 | * 需要编辑的文件的FCB
95 | */
96 | private void showEditView(FCB fcb) {
97 | // 弹出Edit View,根据FCB加载
98 | EditView editView = new EditView(fcb,
99 | MainController.this.systemCore.readFile(fcb));
100 |
101 | // 为Edit Window添加监听
102 | editView.addWindowListener(MainController.this.editWindowListener);
103 |
104 | // 显示Edit View
105 | editView.setVisible(true);
106 | }
107 |
108 | // Listener
109 | /**
110 | * 主界面内容面板右键点击监听
111 | */
112 | private MouseListener rightClickListener = new MouseListener() {
113 |
114 | @Override
115 | public void mouseClicked(MouseEvent e) {
116 | // Deselect documents
117 | if (e.getButton() == MouseEvent.BUTTON1) {
118 | MainController.this.view.deselectDocuments();
119 | }
120 |
121 | // Popup menu
122 | if (e.getButton() == MouseEvent.BUTTON3) {
123 | boolean isRoot = (MainController.this.systemCore.currentDirFCB.fatherBlockId == -1);
124 |
125 | JPopupMenu menu = new JPopupMenu();
126 | JMenuItem newFileMenu = new JMenuItem("New File", KeyEvent.VK_N);
127 | newFileMenu.setAccelerator(KeyStroke.getKeyStroke(
128 | KeyEvent.VK_N, ActionEvent.CTRL_MASK));
129 | newFileMenu
130 | .addActionListener(MainController.this.newFileActionListener);
131 | menu.add(newFileMenu);
132 |
133 | JMenuItem newFolderMenu = new JMenuItem("New Folder",
134 | KeyEvent.VK_F);
135 | newFolderMenu.setAccelerator(KeyStroke.getKeyStroke(
136 | KeyEvent.VK_F, ActionEvent.CTRL_MASK));
137 | newFolderMenu
138 | .addActionListener(MainController.this.newFolderActionListener);
139 | menu.add(newFolderMenu);
140 |
141 | if (isRoot) {
142 | menu.addSeparator();
143 |
144 | JMenuItem formatMenu = new JMenuItem("Format");
145 | formatMenu
146 | .addActionListener(MainController.this.formatMenuActionListener);
147 | menu.add(formatMenu);
148 | }
149 |
150 | menu.show(e.getComponent(), e.getX(), e.getY());
151 | }
152 | }
153 |
154 | @Override
155 | public void mousePressed(MouseEvent e) {
156 |
157 | }
158 |
159 | @Override
160 | public void mouseReleased(MouseEvent e) {
161 |
162 | }
163 |
164 | @Override
165 | public void mouseEntered(MouseEvent e) {
166 |
167 | }
168 |
169 | @Override
170 | public void mouseExited(MouseEvent e) {
171 |
172 | }
173 |
174 | };
175 |
176 | /**
177 | * 图标点击监听,包括单击选中,双击打开,右键弹出菜单
178 | */
179 | private MouseListener documentIconPanelMouseListener = new MouseListener() {
180 |
181 | @Override
182 | public void mouseClicked(MouseEvent e) {
183 | // Select current document
184 | MainController.this.view.deselectDocuments();
185 | DocumentIconPanel d = (DocumentIconPanel) e.getComponent();
186 | d.setSelected(true);
187 |
188 | // 获取文件FCB
189 | FCB fcb = MainController.this.systemCore.getFCB(d.getFilename(),
190 | d.getType());
191 |
192 | // Double click
193 | if (e.getClickCount() == 2) {
194 | // 判断双击的类型
195 | if (d.getType() == FILE_TYPE.DIRECTORY) {
196 | // 双击文件夹
197 | // model进入下一级文件夹
198 | MainController.this.systemCore.enterDir(d.getFilename());
199 |
200 | // 重绘view
201 | MainController.this.refreshView();
202 |
203 | } else {
204 | // 双击文件
205 | // 显示编辑窗口
206 | MainController.this.showEditView(fcb);
207 | }
208 |
209 | System.out.println("Double Click");
210 | }
211 |
212 | // Right click
213 | if (e.getButton() == MouseEvent.BUTTON3) {
214 | JPopupMenu documentMenu = new JPopupMenu();
215 | JMenuItem openMenuItem = new JMenuItem("Open", KeyEvent.VK_O);
216 | MainController.this.openMenuActionListener.fcb = fcb;
217 | openMenuItem
218 | .addActionListener(MainController.this.openMenuActionListener);
219 | documentMenu.add(openMenuItem);
220 |
221 | if (d.getType() == FILE_TYPE.FILE) {
222 | JMenuItem editMenuItem = new JMenuItem("Edit",
223 | KeyEvent.VK_E);
224 | MainController.this.editMenuActionListener.fcb = fcb;
225 | editMenuItem
226 | .addActionListener(MainController.this.editMenuActionListener);
227 | documentMenu.add(editMenuItem);
228 | }
229 |
230 | JMenuItem renameMenuItem = new JMenuItem("Rename",
231 | KeyEvent.VK_R);
232 | MainController.this.renameMenuActionListener.fcb = fcb;
233 | renameMenuItem
234 | .addActionListener(MainController.this.renameMenuActionListener);
235 | documentMenu.add(renameMenuItem);
236 |
237 | documentMenu.addSeparator();
238 |
239 | JMenuItem getInfoMenuItem = new JMenuItem("Get info",
240 | KeyEvent.VK_I);
241 | MainController.this.getInfoMenuActionListener.fcb = fcb;
242 | getInfoMenuItem
243 | .addActionListener(MainController.this.getInfoMenuActionListener);
244 | documentMenu.add(getInfoMenuItem);
245 |
246 | documentMenu.addSeparator();
247 |
248 | JMenuItem deleteMenuItem = new JMenuItem("Delete",
249 | KeyEvent.VK_D);
250 | MainController.this.deleteMenuActionListener.fcb = fcb;
251 | deleteMenuItem
252 | .addActionListener(MainController.this.deleteMenuActionListener);
253 | documentMenu.add(deleteMenuItem);
254 |
255 | documentMenu.show(e.getComponent(), e.getX(), e.getY());
256 | }
257 | }
258 |
259 | @Override
260 | public void mousePressed(MouseEvent e) {
261 |
262 | }
263 |
264 | @Override
265 | public void mouseReleased(MouseEvent e) {
266 |
267 | }
268 |
269 | @Override
270 | public void mouseEntered(MouseEvent e) {
271 |
272 | }
273 |
274 | @Override
275 | public void mouseExited(MouseEvent e) {
276 |
277 | }
278 |
279 | };
280 |
281 | /**
282 | * 监听新建文件的按钮
283 | */
284 | private ActionListener newFileActionListener = new ActionListener() {
285 |
286 | @Override
287 | public void actionPerformed(ActionEvent e) {
288 | // 新建文件
289 | // 获取文件名
290 | String filename = (String) JOptionPane.showInputDialog(
291 | MainController.this.view,
292 | "Please enter your new file's name:", "New file",
293 | JOptionPane.INFORMATION_MESSAGE);
294 |
295 | if (filename == null) {
296 | // 用户取消
297 | return;
298 | }
299 |
300 | // 不允许文件名为空
301 | while (filename.equals("")) {
302 | filename = (String) JOptionPane
303 | .showInputDialog(
304 | MainController.this.view,
305 | "Filename cannot be empty! \nPlease enter your new file's name:",
306 | "New file", JOptionPane.WARNING_MESSAGE);
307 |
308 | if (filename == null) {
309 | // 用户取消
310 | return;
311 | }
312 | }
313 |
314 | if (MainController.this.systemCore.createFile(filename)) {
315 | // 添加到model成功,即创建文件成功
316 | // 添加到view
317 | DocumentIconPanel d = new DocumentIconPanel(FILE_TYPE.FILE,
318 | filename);
319 | d.addMouseListener(MainController.this.documentIconPanelMouseListener);
320 | MainController.this.view.addDocument(d);
321 | } else {
322 | // 创建文件失败
323 | // 可能是有重名,也可能空间不够
324 | // 弹出错误信息框
325 | JOptionPane
326 | .showMessageDialog(
327 | MainController.this.view,
328 | "Two possible reasons:\n1. The name \""
329 | + filename
330 | + "\" is already taken. Please choose a different name.\n2. Not enough disk space available. Delete some files.",
331 | "New file error", JOptionPane.ERROR_MESSAGE);
332 | }
333 |
334 | }
335 | };
336 |
337 | /**
338 | * 监听新建文件夹的按钮
339 | */
340 | private ActionListener newFolderActionListener = new ActionListener() {
341 |
342 | @Override
343 | public void actionPerformed(ActionEvent e) {
344 | // 新建文件夹
345 | // 获取文件名
346 | String filename = (String) JOptionPane.showInputDialog(
347 | MainController.this.view,
348 | "Please enter your new folder's name:", "New folder",
349 | JOptionPane.INFORMATION_MESSAGE);
350 |
351 | if (filename == null) {
352 | // 用户取消
353 | return;
354 | }
355 |
356 | // 不允许文件名为空
357 | while (filename.equals("")) {
358 | filename = (String) JOptionPane
359 | .showInputDialog(
360 | MainController.this.view,
361 | "Folder's name cannot be empty! \nPlease enter your new folder's name:",
362 | "New folder", JOptionPane.WARNING_MESSAGE);
363 |
364 | if (filename == null) {
365 | // 用户取消
366 | return;
367 | }
368 | }
369 |
370 | if (MainController.this.systemCore.createDir(filename)) {
371 | // 添加到model成功,即创建文件夹成功
372 | // 添加到view
373 | DocumentIconPanel d = new DocumentIconPanel(
374 | FILE_TYPE.DIRECTORY, filename);
375 | d.addMouseListener(MainController.this.documentIconPanelMouseListener);
376 | MainController.this.view.addDocument(d);
377 | } else {
378 | // 创建文件夹失败
379 | // 可能是有重名,也可能空间不够
380 | // 弹出错误信息框
381 | JOptionPane
382 | .showMessageDialog(
383 | MainController.this.view,
384 | "Two possible reasons:\n1. The name \""
385 | + filename
386 | + "\" is already taken. Please choose a different name.\n2. Not enough disk space available. Delete some files.",
387 | "New folder error", JOptionPane.ERROR_MESSAGE);
388 | }
389 |
390 | }
391 |
392 | };
393 |
394 | private class CustomActionListener implements ActionListener {
395 | public FCB fcb = null;
396 |
397 | @Override
398 | public void actionPerformed(ActionEvent e) {
399 |
400 | }
401 | }
402 |
403 | /**
404 | * "编辑"按钮按键监听
405 | */
406 | private CustomActionListener editMenuActionListener = new CustomActionListener() {
407 |
408 | @Override
409 | public void actionPerformed(ActionEvent e) {
410 | MainController.this.showEditView(this.fcb);
411 | }
412 |
413 | };
414 |
415 | /**
416 | * "打开"按钮按键监听
417 | */
418 | private CustomActionListener openMenuActionListener = new CustomActionListener() {
419 |
420 | @Override
421 | public void actionPerformed(ActionEvent e) {
422 | if (this.fcb.type == FILE_TYPE.DIRECTORY) {
423 | // model进入下一级文件夹
424 | MainController.this.systemCore.enterDir(this.fcb.filename);
425 |
426 | // 重绘view
427 | MainController.this.refreshView();
428 | } else {
429 | // 显示编辑窗口
430 | MainController.this.showEditView(fcb);
431 | }
432 | }
433 |
434 | };
435 |
436 | /**
437 | * "删除"按钮按键监听
438 | */
439 | private CustomActionListener deleteMenuActionListener = new CustomActionListener() {
440 |
441 | @Override
442 | public void actionPerformed(ActionEvent e) {
443 | // 获取用户的选择
444 | int result = JOptionPane.showConfirmDialog(
445 | MainController.this.view,
446 | "Are you sure you want to permanently delete this item?",
447 | "Delete", JOptionPane.YES_NO_OPTION,
448 | JOptionPane.QUESTION_MESSAGE);
449 |
450 | if (result == 0) {
451 | // 确定删除
452 | if (this.fcb.type == FILE_TYPE.DIRECTORY) {
453 | // model删除文件夹
454 | MainController.this.systemCore.deleteDir(this.fcb.filename);
455 | } else {
456 | // model删除文件
457 | MainController.this.systemCore
458 | .deleteFile(this.fcb.filename);
459 | }
460 |
461 | // 重绘view
462 | MainController.this.refreshView();
463 | }
464 | }
465 |
466 | };
467 |
468 | /**
469 | * "重命名"按钮按键监听
470 | */
471 | private CustomActionListener renameMenuActionListener = new CustomActionListener() {
472 |
473 | @Override
474 | public void actionPerformed(ActionEvent e) {
475 | // 获取文件名
476 | String filename = (String) JOptionPane.showInputDialog(
477 | MainController.this.view, "Folder name:", "Rename",
478 | JOptionPane.INFORMATION_MESSAGE, null, null,
479 | this.fcb.filename);
480 |
481 | if (filename == null) {
482 | // 用户取消
483 | return;
484 | }
485 |
486 | // 不允许文件名为空
487 | while (filename.equals("")) {
488 | filename = (String) JOptionPane.showInputDialog(
489 | MainController.this.view, "Folder name:", "Rename",
490 | JOptionPane.INFORMATION_MESSAGE, null, null,
491 | this.fcb.filename);
492 | }
493 |
494 | this.fcb.filename = filename;
495 |
496 | Gson gson = new Gson();
497 |
498 | // 更新文件FCB
499 | MainController.this.systemCore.updateFCB(this.fcb);
500 |
501 | // 更新当前目录的目录文件
502 | MainController.this.systemCore.updateFile(
503 | MainController.this.systemCore.currentDirFCB,
504 | gson.toJson(MainController.this.systemCore.currentDir));
505 |
506 | // 刷新界面
507 | MainController.this.refreshView();
508 | }
509 |
510 | };
511 |
512 | /**
513 | * "格式化"按钮按键监听
514 | */
515 | private ActionListener formatMenuActionListener = new ActionListener() {
516 |
517 | @Override
518 | public void actionPerformed(ActionEvent e) {
519 | // 获取用户的选择
520 | int result = JOptionPane
521 | .showConfirmDialog(
522 | MainController.this.view,
523 | "All the data will be erased from the disk.\nAre you sure to FORMAT disk?",
524 | "FORMAT!!", JOptionPane.YES_NO_OPTION,
525 | JOptionPane.QUESTION_MESSAGE);
526 |
527 | if (result == 0) {
528 | // 确定格式化
529 | MainController.this.systemCore.format();
530 | }
531 |
532 | // 刷新界面
533 | MainController.this.refreshView();
534 | }
535 |
536 | };
537 |
538 | /**
539 | * "属性"按钮按键监听
540 | */
541 | private CustomActionListener getInfoMenuActionListener = new CustomActionListener() {
542 |
543 | @Override
544 | public void actionPerformed(ActionEvent e) {
545 | // 弹出详细信息框
546 | JOptionPane.showMessageDialog(MainController.this.view,
547 | MainController.this.systemCore.getFileInfo(this.fcb),
548 | "Info", JOptionPane.PLAIN_MESSAGE);
549 | }
550 |
551 | };
552 |
553 | /**
554 | * "后退"按钮按键监听
555 | */
556 | private ActionListener backButtonActionListener = new ActionListener() {
557 |
558 | @Override
559 | public void actionPerformed(ActionEvent e) {
560 | // 点击后退按钮
561 |
562 | if (MainController.this.systemCore.leaveDir()) {
563 | // 确认回到上一级目录
564 | // 重绘view
565 | MainController.this.refreshView();
566 | } else {
567 | // 根目录
568 | JOptionPane.showMessageDialog(MainController.this.view,
569 | "Already in root directory", "Warning",
570 | JOptionPane.WARNING_MESSAGE);
571 | }
572 | }
573 |
574 | };
575 |
576 | /**
577 | * "前往"按钮按键监听
578 | */
579 | private ActionListener goButtonActionListener = new ActionListener() {
580 |
581 | @Override
582 | public void actionPerformed(ActionEvent e) {
583 | // 获取地址栏地址
584 | String path = MainController.this.view.addressTextField.getText();
585 |
586 | if (path.charAt(0) != '/') {
587 | // 路径非法
588 | JOptionPane.showMessageDialog(MainController.this.view,
589 | "Directory doesn't exist!", "Warning",
590 | JOptionPane.WARNING_MESSAGE);
591 |
592 | return;
593 | }
594 |
595 | // 获取当前地址
596 | String currentPath = MainController.this.systemCore
597 | .getCurrentPath();
598 |
599 | // 拆分
600 | String[] pathArray = path.split("/");
601 | String[] currentPathArray = currentPath.split("/");
602 |
603 | if (pathArray.length == 0) {
604 | pathArray = new String[1];
605 | pathArray[0] = "";
606 | }
607 |
608 | if (currentPathArray.length == 0) {
609 | currentPathArray = new String[1];
610 | currentPathArray[0] = "";
611 | }
612 |
613 | // 对比
614 | int length = Math.min(pathArray.length, currentPathArray.length);
615 | int i = 0;
616 | for (i = 0; i < length; i++) {
617 | if (!pathArray[i].equals(currentPathArray[i])) {
618 | break;
619 | }
620 | }
621 |
622 | if (pathArray.length == currentPathArray.length && i == length) {
623 | // 两拆分后的数组相同,即路径没变化,无需继续下面的步骤
624 | return;
625 | }
626 |
627 | // 计算
628 | // 向后退的步数
629 | int stepOut = currentPathArray.length - i;
630 | // 向前进的步数
631 | int stepIn = pathArray.length - i;
632 |
633 | // 临时保存当前目录FCB和目录文件
634 | FCB fcb = MainController.this.systemCore.currentDirFCB;
635 | FCB[] dir = MainController.this.systemCore.currentDir;
636 |
637 | // 开始后退再前进
638 | boolean success = true;
639 | // 后退
640 | for (int j = 0; j < stepOut; j++) {
641 | MainController.this.systemCore.leaveDir();
642 | }
643 |
644 | // 前进
645 | for (int j = 0; j < stepIn; j++) {
646 | if (!MainController.this.systemCore.enterDir(pathArray[i++])) {
647 | success = false;
648 | break;
649 | }
650 | }
651 |
652 | if (success) {
653 | // 成功跳转
654 | // 刷新界面
655 | MainController.this.refreshView();
656 | } else {
657 | // 目录不存在
658 | JOptionPane.showMessageDialog(MainController.this.view,
659 | "Directory doesn't exist!", "Warning",
660 | JOptionPane.WARNING_MESSAGE);
661 |
662 | // 恢复
663 | MainController.this.systemCore.currentDirFCB = fcb;
664 | MainController.this.systemCore.currentDir = dir;
665 | MainController.this.systemCore.countFiles();
666 | }
667 |
668 | }
669 |
670 | };
671 |
672 | /**
673 | * 主窗口的监听,主要是关闭事件
674 | */
675 | private WindowListener mainWindowListener = new WindowListener() {
676 |
677 | @Override
678 | public void windowOpened(WindowEvent e) {
679 |
680 | }
681 |
682 | @Override
683 | public void windowClosing(WindowEvent e) {
684 |
685 | }
686 |
687 | @Override
688 | public void windowClosed(WindowEvent e) {
689 | System.out.println("窗口已关闭");
690 |
691 | // 系统核心退出
692 | MainController.this.systemCore.exit();
693 | }
694 |
695 | @Override
696 | public void windowIconified(WindowEvent e) {
697 |
698 | }
699 |
700 | @Override
701 | public void windowDeiconified(WindowEvent e) {
702 |
703 | }
704 |
705 | @Override
706 | public void windowActivated(WindowEvent e) {
707 |
708 | }
709 |
710 | @Override
711 | public void windowDeactivated(WindowEvent e) {
712 |
713 | }
714 |
715 | };
716 |
717 | /**
718 | * 编辑窗口的监听
719 | */
720 | private WindowListener editWindowListener = new WindowListener() {
721 |
722 | @Override
723 | public void windowOpened(WindowEvent e) {
724 |
725 | }
726 |
727 | @Override
728 | public void windowClosing(WindowEvent e) {
729 |
730 | EditView editView = (EditView) e.getComponent();
731 |
732 | if (!editView.edited) {
733 | // 文本没有变化
734 | return;
735 | }
736 |
737 | // 获取用户的选择
738 | int result = JOptionPane.showConfirmDialog(editView,
739 | "Would you like to SAVE before leaving?", "Exit",
740 | JOptionPane.YES_NO_CANCEL_OPTION,
741 | JOptionPane.QUESTION_MESSAGE);
742 |
743 | if (result == 0) {
744 | // 退出并保存
745 | editView.saveOnExit = true;
746 | editView.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
747 | } else if (result == 1) {
748 | // 退出不保存
749 | editView.saveOnExit = false;
750 | editView.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
751 | } else {
752 | // 取消
753 | editView.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
754 | }
755 | }
756 |
757 | @Override
758 | public void windowClosed(WindowEvent e) {
759 | System.out.println("编辑窗口关闭");
760 |
761 | EditView editView = (EditView) e.getComponent();
762 |
763 | if (editView.edited && editView.saveOnExit) {
764 | // 保存文件
765 | MainController.this.systemCore.updateFile(
766 | editView.getDataFCB(), editView.getContent());
767 | }
768 | }
769 |
770 | @Override
771 | public void windowIconified(WindowEvent e) {
772 |
773 | }
774 |
775 | @Override
776 | public void windowDeiconified(WindowEvent e) {
777 |
778 | }
779 |
780 | @Override
781 | public void windowActivated(WindowEvent e) {
782 |
783 | }
784 |
785 | @Override
786 | public void windowDeactivated(WindowEvent e) {
787 |
788 | }
789 |
790 | };
791 |
792 | /**
793 | * 根据当前FCB对应的目录文件刷新View
794 | */
795 | private void refreshView() {
796 | this.view.reloadContent(this.systemCore.currentDir);
797 |
798 | // 重新添加监听
799 | this.addListenerForEachDocumentIcon();
800 |
801 | // 更新当前路径
802 | this.view.addressTextField.setText(MainController.this.systemCore
803 | .getCurrentPath());
804 | }
805 |
806 | /**
807 | * 为每个图标添加监听
808 | */
809 | private void addListenerForEachDocumentIcon() {
810 | this.view
811 | .addDocumentIconPanelMouseListener(this.documentIconPanelMouseListener);
812 | }
813 | }
814 |
--------------------------------------------------------------------------------
/src/controller/SystemCore.java:
--------------------------------------------------------------------------------
1 | package controller;
2 |
3 | import java.nio.ByteBuffer;
4 | import java.util.Date;
5 |
6 | import model.sys.Config;
7 | import model.sys.Config.FILE_TYPE;
8 | import model.sys.FCB;
9 |
10 | import com.google.gson.Gson;
11 |
12 | public class SystemCore {
13 |
14 | private DiskManager diskManager;
15 | public FCB currentDirFCB;
16 | public FCB[] currentDir;
17 | private int fileCount = 0;
18 |
19 | /**
20 | * 构造函数
21 | */
22 | public SystemCore() {
23 | super();
24 | this.diskManager = new DiskManager();
25 |
26 | // 初始化系统核心
27 | this.init();
28 | }
29 |
30 | /**
31 | * 初始化系统核心
32 | */
33 | private void init() {
34 | // 初始化
35 | this.diskManager.init();
36 |
37 | Gson gson = new Gson();
38 |
39 | // 读取根目录FCB
40 | ByteBuffer currentDirFCBBuffer = this.diskManager.io.read(2, 1);
41 | String currentDirFCBJSON = new String(currentDirFCBBuffer.array(),
42 | Config.CHARSET);
43 | this.currentDirFCB = gson.fromJson(currentDirFCBJSON, FCB.class);
44 |
45 | // 读取根目录目录文件
46 | ByteBuffer currentDirJSONBuffer = this.diskManager.io.read(
47 | this.currentDirFCB.dataStartBlockId, this.currentDirFCB.size);
48 | String currentDirJSON = new String(currentDirJSONBuffer.array(),
49 | Config.CHARSET);
50 | this.currentDir = gson.fromJson(currentDirJSON, FCB[].class);
51 |
52 | // 计算文件个数
53 | this.countFiles();
54 | }
55 |
56 | /**
57 | * 创建文件
58 | *
59 | * @param filename
60 | * 文件名
61 | * @return 是否创建成功
62 | */
63 | public boolean createFile(String filename) {
64 | if (!this.checkFilename(filename, FILE_TYPE.FILE)) {
65 | return false;
66 | }
67 |
68 | int fcbBlockStart = this.diskManager.getFreeSpace(1);
69 | int fileDataBlockStart = this.diskManager.getFreeSpace(
70 | Config.FILE_MAX_BLOCKS, fcbBlockStart + 1);
71 |
72 | if (fcbBlockStart != 0 && fileDataBlockStart != 0) {
73 | FCB fcb = new FCB(filename, this.currentDirFCB.blockId,
74 | Config.FILE_TYPE.FILE, Config.FILE_MAX_BLOCKS,
75 | fileDataBlockStart, fcbBlockStart);
76 |
77 | // 申请分配空间
78 | this.diskManager.alloc(fcbBlockStart, 1);
79 | this.diskManager.alloc(fileDataBlockStart, Config.FILE_MAX_BLOCKS);
80 |
81 | // 当前文件夹下加入该文件
82 | this.currentDir[fileCount++] = fcb;
83 |
84 | Gson gson = new Gson();
85 |
86 | // 更新目录FCB
87 | this.updateFCB(this.currentDirFCB);
88 |
89 | // 更新当前目录文件
90 | this.updateFile(this.currentDirFCB, gson.toJson(this.currentDir));
91 |
92 | // 将新建文件的FCB写入Block
93 | this.diskManager.io.write(fcbBlockStart, 1, gson.toJson(fcb));
94 |
95 | // 为新建的文件写内容
96 | this.updateFile(fcb, "");
97 | } else {
98 | System.out.println("空间不够");
99 |
100 | return false;
101 | }
102 |
103 | return true;
104 | }
105 |
106 | /**
107 | * 向FCB所指向的数据区更新内容
108 | *
109 | * @param fcb
110 | * 需要更新的文件的FCB
111 | * @param content
112 | * 所更新的内容
113 | */
114 | public void updateFile(FCB fcb, String content) {
115 | this.diskManager.io.write(fcb.dataStartBlockId, fcb.size, content);
116 | }
117 |
118 | /**
119 | * 独处文件内容
120 | *
121 | * @param fcb
122 | * 文件对应的FCB
123 | * @return 返回文件内容
124 | */
125 | public String readFile(FCB fcb) {
126 | return this.diskManager.readFile(fcb);
127 | }
128 |
129 | /**
130 | * 获取格式化后的文件信息
131 | *
132 | * @param fcb
133 | * 指定的FCB
134 | * @return 格式化后的文件信息
135 | */
136 | public String getFileInfo(FCB fcb) {
137 | String result = "";
138 |
139 | result += ("Name: " + fcb.filename + "\n");
140 | result += ("Type: "
141 | + (fcb.type == FILE_TYPE.FILE ? "file" : "directory") + "\n");
142 | result += ("Path: " + this.getCurrentPath() + "\n");
143 | result += ("Created: " + fcb.createdDate + "\n");
144 | result += ("Updated: " + fcb.updatedDate);
145 |
146 | return result;
147 | }
148 |
149 | /**
150 | * 创建文件夹
151 | *
152 | * @param filename
153 | * 文件夹名
154 | * @return 是否创建成功
155 | */
156 | public boolean createDir(String filename) {
157 | if (!this.checkFilename(filename, FILE_TYPE.DIRECTORY)) {
158 | // 有重名
159 | return false;
160 | }
161 |
162 | int fcbBlockStart = this.diskManager.getFreeSpace(1);
163 | int fileDataBlockStart = this.diskManager.getFreeSpace(
164 | Config.FILE_MAX_BLOCKS, fcbBlockStart + 1);
165 |
166 | if (fcbBlockStart != 0 && fileDataBlockStart != 0) {
167 | FCB dirFcb = new FCB(filename, this.currentDirFCB.blockId,
168 | Config.FILE_TYPE.DIRECTORY, Config.FILE_MAX_BLOCKS,
169 | fileDataBlockStart, fcbBlockStart);
170 | FCB[] dir = new FCB[40];
171 |
172 | // 申请分配空间
173 | this.diskManager.alloc(fcbBlockStart, 1);
174 | this.diskManager.alloc(fileDataBlockStart, Config.FILE_MAX_BLOCKS);
175 |
176 | // 当前文件夹下加入该文件
177 | this.currentDir[fileCount++] = dirFcb;
178 |
179 | Gson gson = new Gson();
180 |
181 | // 更新目录FCB
182 | this.updateFCB(this.currentDirFCB);
183 |
184 | // 更新当前目录文件
185 | this.updateFile(this.currentDirFCB, gson.toJson(this.currentDir));
186 |
187 | // 将新建文件的FCB写入Block
188 | this.diskManager.io.write(fcbBlockStart, 1, gson.toJson(dirFcb));
189 |
190 | // 为新建的文件写内容
191 | this.updateFile(dirFcb, gson.toJson(dir));
192 | } else {
193 | System.out.println("空间不够");
194 |
195 | return false;
196 | }
197 |
198 | return true;
199 | }
200 |
201 | /**
202 | * 删除文件夹
203 | *
204 | * @param filename
205 | * 文件夹名
206 | */
207 | public void deleteDir(String filename) {
208 | FCB fcb = this.getFCB(filename, FILE_TYPE.DIRECTORY);
209 |
210 | if (fcb == null) {
211 | return;
212 | }
213 |
214 | // 删除对应的FCB
215 | this.deleteFCB(fcb);
216 |
217 | // 更新目录FCB
218 | this.updateFCB(this.currentDirFCB);
219 |
220 | // 递归删除
221 | this.recursiveDeleteDir(fcb);
222 | }
223 |
224 | /**
225 | * 递归删除文件夹以及文件夹内所有文件
226 | *
227 | * @param fcb
228 | * 需要删除的文件夹对应的FCB
229 | */
230 | private void recursiveDeleteDir(FCB fcb) {
231 | // 释放空间
232 | // 释放该FCB所在的块
233 | this.diskManager.free(fcb.blockId, 1);
234 | // 释放该FCB所指向的数据区
235 | this.diskManager.free(fcb.dataStartBlockId, fcb.size);
236 |
237 | Gson gson = new Gson();
238 |
239 | // 获取目录下的所有文件
240 | ByteBuffer bf = this.diskManager.io
241 | .read(fcb.dataStartBlockId, fcb.size);
242 | FCB[] dir = gson.fromJson(new String(bf.array(), Config.CHARSET),
243 | FCB[].class);
244 |
245 | // 遍历,删除
246 | for (int i = 0; i < dir.length; i++) {
247 | if (dir[i] == null) {
248 | // 如果目录为空,直接跳出
249 | break;
250 | }
251 |
252 | if (dir[i].type == FILE_TYPE.DIRECTORY) {
253 | // 删除文件夹
254 | // 递归调用
255 | this.recursiveDeleteDir(dir[i]);
256 | } else {
257 | // 删除文件
258 | // 释放空间
259 | // 释放该FCB所在的块
260 | this.diskManager.free(fcb.blockId, 1);
261 | // 释放该FCB所指向的数据区
262 | this.diskManager.free(fcb.dataStartBlockId, fcb.size);
263 | }
264 | }
265 | }
266 |
267 | /**
268 | * 删除文件
269 | *
270 | * @param filename
271 | * 文件名
272 | */
273 | public void deleteFile(String filename) {
274 | FCB fcb = this.getFCB(filename, FILE_TYPE.FILE);
275 |
276 | if (fcb == null) {
277 | return;
278 | }
279 |
280 | // 删除对应的FCB
281 | this.deleteFCB(fcb);
282 |
283 | // 更新目录FCB
284 | this.updateFCB(this.currentDirFCB);
285 |
286 | // 释放空间
287 | // 释放该FCB所在的块
288 | this.diskManager.free(fcb.blockId, 1);
289 | // 释放该FCB所指向的数据区
290 | this.diskManager.free(fcb.dataStartBlockId, fcb.size);
291 | }
292 |
293 | /**
294 | * 删除当前文件夹下FCB,并更新Block数据
295 | *
296 | * @param fcb
297 | */
298 | private void deleteFCB(FCB fcb) {
299 | int i = 0;
300 | for (; i < this.currentDir.length; i++) {
301 | if (this.currentDir[i].filename.equals(fcb.filename)
302 | && this.currentDir[i].type == fcb.type) {
303 | this.currentDir[i] = null;
304 | break;
305 | }
306 | }
307 |
308 | // 保持文件目录的连续
309 | while (i < this.currentDir.length - 1) {
310 | if (this.currentDir[i + 1] == null) {
311 | break;
312 | }
313 | this.currentDir[i] = this.currentDir[++i];
314 | this.currentDir[i] = null;
315 | }
316 |
317 | Gson gson = new Gson();
318 |
319 | // 写回Block
320 | this.updateFile(this.currentDirFCB, gson.toJson(this.currentDir));
321 | }
322 |
323 | /**
324 | * 进入下一级文件夹
325 | *
326 | * @param dirname
327 | * 文件夹名
328 | * @return 是否进入成功
329 | */
330 | public boolean enterDir(String dirname) {
331 | FCB dirFCB = this.getFCB(dirname, FILE_TYPE.DIRECTORY);
332 | if (dirFCB == null) {
333 | // 所对应文件夹不存在
334 | System.out.println("找不到该目录");
335 | return false;
336 | }
337 |
338 | Gson gson = new Gson();
339 |
340 | // 替换currentDirFCB和currentDir
341 | this.currentDirFCB = dirFCB;
342 | ByteBuffer bf = this.diskManager.io.read(
343 | this.currentDirFCB.dataStartBlockId, this.currentDirFCB.size);
344 | String json = new String(bf.array(), Config.CHARSET);
345 | FCB[] dir = gson.fromJson(json, FCB[].class);
346 | this.currentDir = dir;
347 |
348 | // 计算文件个数
349 | this.countFiles();
350 |
351 | return true;
352 | }
353 |
354 | /**
355 | * 返回上一级文件夹
356 | *
357 | * @return 是否返回成功
358 | */
359 | public boolean leaveDir() {
360 | if (this.currentDirFCB.fatherBlockId == -1) {
361 | // 根目录,无法返回
362 | System.out.println("已经到根目录,不存在上级目录");
363 | return false;
364 | }
365 |
366 | Gson gson = new Gson();
367 |
368 | // 获取上一级文件夹的FCB
369 | ByteBuffer bf = this.diskManager.io.read(
370 | this.currentDirFCB.fatherBlockId, 1);
371 | FCB fcb = gson.fromJson(new String(bf.array(), Config.CHARSET),
372 | FCB.class);
373 |
374 | // 获取上一级文件夹的目录文件
375 | // 即获取包含其目录下所有FCB的数组
376 | ByteBuffer dirBf = this.diskManager.io.read(fcb.dataStartBlockId,
377 | fcb.size);
378 | FCB[] dir = gson.fromJson(new String(dirBf.array(), Config.CHARSET),
379 | FCB[].class);
380 |
381 | // 替换currentDirFCB和currentDir
382 | this.currentDir = dir;
383 | this.currentDirFCB = fcb;
384 |
385 | // 计算文件个数
386 | this.countFiles();
387 |
388 | return true;
389 | }
390 |
391 | /**
392 | * 获取当前文件夹的路径
393 | *
394 | * @return
395 | */
396 | public String getCurrentPath() {
397 | if (this.currentDirFCB.fatherBlockId == -1) {
398 | return "/";
399 | }
400 | return this.recursiveGetPath(this.currentDirFCB);
401 | }
402 |
403 | /**
404 | * 通过给定FCB,递归向上获取该FCB的路径
405 | *
406 | * @param fcb
407 | * 给定的FCB
408 | * @return 给定FCB的路径
409 | */
410 | private String recursiveGetPath(FCB fcb) {
411 | if (fcb.type != FILE_TYPE.DIRECTORY) {
412 | return null;
413 | }
414 |
415 | if (fcb.fatherBlockId == -1) {
416 | return "";
417 | }
418 |
419 | Gson gson = new Gson();
420 |
421 | // 获取父亲的FCB
422 | ByteBuffer fatherBf = this.diskManager.io.read(fcb.fatherBlockId, 1);
423 | String fatherFCBJson = new String(fatherBf.array(), Config.CHARSET);
424 | FCB fatherFCB = gson.fromJson(fatherFCBJson, FCB.class);
425 |
426 | return this.recursiveGetPath(fatherFCB) + "/" + fcb.filename;
427 | }
428 |
429 | /**
430 | * 格式化,清空所有数据
431 | */
432 | public void format() {
433 | this.diskManager.format();
434 |
435 | this.init();
436 | }
437 |
438 | /**
439 | * 退出系统核心
440 | */
441 | public void exit() {
442 | System.out.println("准备退出系统...");
443 | // 写回磁盘
444 | this.diskManager.update();
445 | System.out.println("退出");
446 | }
447 |
448 | /**
449 | * 在当前目录下通过文件名找FCB
450 | *
451 | * @param filename
452 | * 文件名
453 | * @return 找到的FCB
454 | */
455 | public FCB getFCB(String filename, FILE_TYPE type) {
456 | for (int i = 0; i < this.currentDir.length; i++) {
457 | if (this.currentDir[i] == null) {
458 | break;
459 | }
460 | if (this.currentDir[i].filename.equals(filename)
461 | && this.currentDir[i].type == type) {
462 | return this.currentDir[i];
463 | }
464 | }
465 | return null;
466 | }
467 |
468 | /**
469 | * 更新所给的FCB
470 | *
471 | * @param fcb
472 | * 指定的FCB
473 | */
474 | public void updateFCB(FCB fcb) {
475 | fcb.updatedDate = new Date();
476 |
477 | Gson gson = new Gson();
478 |
479 | this.diskManager.io.write(fcb.blockId, 1, gson.toJson(fcb));
480 | }
481 |
482 | /**
483 | * 检查文件名,不允许一样的名字
484 | *
485 | * @param filename
486 | * 文件名
487 | * @param type
488 | * 文件类型
489 | * @return 布尔值
490 | */
491 | private boolean checkFilename(String filename, FILE_TYPE type) {
492 | for (int i = 0; i < this.currentDir.length; i++) {
493 | if (this.currentDir[i] == null) {
494 | break;
495 | }
496 | if (this.currentDir[i].filename.equals(filename)
497 | && this.currentDir[i].type == type) {
498 | return false;
499 | }
500 | }
501 | return true;
502 | }
503 |
504 | /**
505 | * 计算当前文件夹下的文件个数
506 | */
507 | public void countFiles() {
508 | for (this.fileCount = 0; this.fileCount < this.currentDir.length; this.fileCount++) {
509 | if (this.currentDir[this.fileCount] == null) {
510 | break;
511 | }
512 | }
513 | }
514 | }
515 |
--------------------------------------------------------------------------------
/src/model/sys/Block.java:
--------------------------------------------------------------------------------
1 | package model.sys;
2 |
3 | import java.io.File;
4 | import java.io.FileInputStream;
5 | import java.io.FileNotFoundException;
6 | import java.io.FileOutputStream;
7 | import java.io.IOException;
8 | import java.nio.ByteBuffer;
9 | import java.nio.channels.FileChannel;
10 |
11 | /**
12 | *
13 | * @author Tom Hu
14 | *
15 | */
16 | public class Block {
17 |
18 | // Core data
19 | private int id; // id为该块的编号
20 | private ByteBuffer binData; // binData为该块存储内容的buffer
21 | private String filePath; // filePath为该块在物理磁盘上*.bin文件的存储路径
22 |
23 | /**
24 | * 构造函数
25 | *
26 | * @param id
27 | */
28 | public Block(int id) {
29 | super();
30 | this.id = id;
31 | this.binData = ByteBuffer.allocate(Config.BLOCK_SIZE);
32 | this.filePath = "disk/" + this.id + ".bin";
33 | }
34 |
35 | /**
36 | * 将该块内容清空
37 | */
38 | public void wipe() {
39 | this.binData.clear(); //并未真正清空数据,只是重置了标记
40 | }
41 |
42 | /**
43 | * 从物理磁盘上读入该块的内容,存入binData
44 | */
45 | public void sync() {
46 | File binFile = new File(this.filePath);
47 | if (!binFile.exists()) {
48 | // System.out.println("Warning: \"" + this.filePath + "\" not found");
49 | return;
50 | }
51 |
52 | FileChannel inputChannel;
53 | try {
54 | inputChannel = new FileInputStream(binFile).getChannel();
55 | inputChannel.read(this.binData);
56 | this.binData.flip();
57 | inputChannel.close();
58 | } catch (FileNotFoundException e) {
59 | e.printStackTrace();
60 | } catch (IOException e) {
61 | e.printStackTrace();
62 | }
63 | }
64 |
65 | /**
66 | * 将binData数据写回物理磁盘
67 | */
68 | public void update() {
69 | File binFile = new File(this.filePath);
70 |
71 | FileChannel outputChannel;
72 | try {
73 | outputChannel = new FileOutputStream(binFile).getChannel();
74 | this.binData.rewind();
75 | outputChannel.write(this.binData);
76 | this.binData.flip();
77 | outputChannel.close();
78 | } catch (FileNotFoundException e) {
79 | e.printStackTrace();
80 | } catch (IOException e) {
81 | e.printStackTrace();
82 | }
83 | }
84 |
85 | /**
86 | * 获取当前块数据
87 | */
88 | public ByteBuffer getBinData() {
89 | return this.binData;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/model/sys/Config.java:
--------------------------------------------------------------------------------
1 | package model.sys;
2 |
3 | import java.nio.charset.Charset;
4 |
5 | /**
6 | *
7 | * @author Tom Hu
8 | *
9 | */
10 |
11 | public class Config {
12 |
13 | public static final int BLOCK_SIZE = 512;
14 | public static final int BLOCK_COUNT = 8000;
15 |
16 | public static final int SYS_BLOCK_COUNT = 256;
17 |
18 | public static final int FILE_MAX_BLOCKS = 10;
19 |
20 | public static enum FILE_TYPE {
21 | FILE, DIRECTORY
22 | };
23 |
24 | public static final Charset CHARSET = Charset.forName("UTF-8");
25 | }
26 |
--------------------------------------------------------------------------------
/src/model/sys/FCB.java:
--------------------------------------------------------------------------------
1 | package model.sys;
2 |
3 | import java.io.Serializable;
4 | import java.util.Date;
5 |
6 | import model.sys.Config.FILE_TYPE;
7 |
8 | /**
9 | *
10 | * @author Tom Hu
11 | *
12 | */
13 | public class FCB implements Serializable {
14 |
15 | private static final long serialVersionUID = -5262771405010721496L;
16 |
17 | public String filename; // 文件名
18 | public FILE_TYPE type; // FILE 或 DORECTORY
19 | public String address; // 文件地址
20 | public Date createdDate;
21 | public Date updatedDate;
22 | public int fatherBlockId; // 指向上一级FCB
23 | public int size; // 当前文件控制块所对应的数据区块个数,即占用空间
24 | public int dataStartBlockId; // 数据区Block的id
25 | public int blockId; // 当前FCB所在Block的id
26 | // public int realSize = 0; // FCB对应文件的实际大小,其中文件夹大小等于目录文件大小加上其目录下所有文件的大小
27 |
28 | // Constructor
29 | public FCB(String filename, int fatherBlockId, FILE_TYPE type, int size,
30 | int dataStartBlockId, int blockId) {
31 | this.filename = filename;
32 | this.fatherBlockId = fatherBlockId;
33 | this.type = type;
34 | this.size = size;
35 | this.createdDate = new Date();
36 | this.updatedDate = (Date) this.createdDate.clone();
37 | this.dataStartBlockId = dataStartBlockId;
38 | this.blockId = blockId;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/view/Config.java:
--------------------------------------------------------------------------------
1 | package view;
2 |
3 | public class Config {
4 |
5 | // Window
6 | public static final int WINDOW_WIDTH = 800;
7 | public static final int WINDOW_HEIGHT = 600;
8 |
9 | // File icon
10 | public static final int FILE_ICON_SIZE = 80;
11 | public static final int FILE_ICON_PANEL_SIZE = 100;
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/view/DocumentIconPanel.java:
--------------------------------------------------------------------------------
1 | package view;
2 |
3 | import java.awt.Color;
4 | import java.awt.Dimension;
5 | import java.awt.Image;
6 |
7 | import javax.swing.BorderFactory;
8 | import javax.swing.BoxLayout;
9 | import javax.swing.ImageIcon;
10 | import javax.swing.JLabel;
11 | import javax.swing.JPanel;
12 |
13 | import model.sys.Config.FILE_TYPE;
14 |
15 | public class DocumentIconPanel extends JPanel {
16 |
17 | /**
18 | *
19 | */
20 | private static final long serialVersionUID = 1952213928294715915L;
21 | private JLabel mainLabel;
22 | private String filename;
23 | private FILE_TYPE type;
24 |
25 | // Constructor
26 | public DocumentIconPanel(FILE_TYPE type, String filename) {
27 | super();
28 | // TODO Auto-generated constructor stub
29 |
30 | this.filename = filename;
31 | this.type = type;
32 |
33 | this.setSize(new Dimension(Config.FILE_ICON_PANEL_SIZE,
34 | Config.FILE_ICON_PANEL_SIZE));
35 | this.setPreferredSize(new Dimension(Config.FILE_ICON_PANEL_SIZE,
36 | Config.FILE_ICON_PANEL_SIZE));
37 |
38 | // Set layout
39 | this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
40 |
41 | // Set background color
42 | this.setBackground(Color.WHITE);
43 |
44 | // UI Methods
45 | this.configureDocumentIcon(type, filename);
46 | // this.configureFileNameLabel();
47 | }
48 |
49 | // UI Methods
50 | private void configureDocumentIcon(FILE_TYPE type, String filename) {
51 |
52 | // initialize icon
53 | ImageIcon icon = null;
54 | switch (type) {
55 | case FILE:
56 | icon = new ImageIcon("resource/DocumentIcon.png");
57 | break;
58 | case DIRECTORY:
59 | icon = new ImageIcon("resource/FolderIcon.png");
60 | break;
61 | default:
62 | System.out.println("error when load icon");
63 | break;
64 | }
65 |
66 | // Scale image
67 | icon.setImage(icon.getImage().getScaledInstance(Config.FILE_ICON_SIZE,
68 | Config.FILE_ICON_SIZE, Image.SCALE_DEFAULT));
69 |
70 | this.mainLabel = new JLabel(filename, icon, JLabel.CENTER);
71 | this.mainLabel.setBorder(BorderFactory.createEmptyBorder(0,
72 | (Config.FILE_ICON_PANEL_SIZE - Config.FILE_ICON_SIZE) / 2, 0,
73 | (Config.FILE_ICON_PANEL_SIZE - Config.FILE_ICON_SIZE) / 2));
74 | this.mainLabel.setVerticalTextPosition(JLabel.BOTTOM);
75 | this.mainLabel.setHorizontalTextPosition(JLabel.CENTER);
76 |
77 | // Add to fileIconPanel
78 | this.add(this.mainLabel);
79 | }
80 |
81 | public void setSelected(boolean selected) {
82 | if (selected) {
83 | this.setBackground(Color.BLUE);
84 | this.mainLabel.setForeground(Color.WHITE);
85 | } else {
86 | this.setBackground(Color.WHITE);
87 | this.mainLabel.setForeground(Color.BLACK);
88 | }
89 | }
90 |
91 | public String getFilename() {
92 | return filename;
93 | }
94 |
95 | public void setFilename(String filename) {
96 | this.filename = filename;
97 | }
98 |
99 | public FILE_TYPE getType() {
100 | return type;
101 | }
102 |
103 | public void setType(FILE_TYPE type) {
104 | this.type = type;
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/src/view/EditView.java:
--------------------------------------------------------------------------------
1 | package view;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Color;
5 | import java.awt.event.KeyEvent;
6 |
7 | import javax.swing.JFrame;
8 | import javax.swing.JMenu;
9 | import javax.swing.JMenuBar;
10 | import javax.swing.JMenuItem;
11 | import javax.swing.JTextPane;
12 | import javax.swing.event.DocumentEvent;
13 | import javax.swing.event.DocumentListener;
14 |
15 | import model.sys.FCB;
16 |
17 | // TODO - layout views
18 | public class EditView extends JFrame implements DocumentListener {
19 |
20 | private static final long serialVersionUID = 5359647733388619559L;
21 | private JTextPane textPane;
22 |
23 | private FCB dataFCB;
24 |
25 | public boolean saveOnExit = true;
26 | public boolean edited = false;
27 |
28 | public EditView(FCB fcb, String content) {
29 | super();
30 |
31 | // initialize
32 | this.dataFCB = fcb;
33 | this.textPane = new JTextPane();
34 |
35 | this.configureMenuBar();
36 | this.configureTextPane(content);
37 |
38 | // Main View
39 | this.configureJFrame();
40 | }
41 |
42 | // UI Method
43 | private void configureJFrame() {
44 | this.setTitle(this.dataFCB.filename + " - Edit");// Set title
45 | this.setSize(Config.WINDOW_WIDTH, Config.WINDOW_HEIGHT);// Set size of
46 | // window
47 | this.setResizable(false);// Can't change the size
48 | this.setLocationRelativeTo(null);// Set the position of the window -
49 | // Screen's Center
50 | this.setBackground(Color.WHITE);
51 |
52 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
53 | }
54 |
55 | private void configureMenuBar() {
56 | // Components
57 | JMenuBar menuBar;
58 | JMenu fileMenu;
59 | JMenu helpMenu;
60 | JMenuItem helpMenuItem;
61 |
62 | // Create the Menu Bar
63 | menuBar = new JMenuBar();
64 |
65 | // Build File Menu
66 | fileMenu = new JMenu("Edit");
67 | fileMenu.setMnemonic(KeyEvent.VK_F);
68 | fileMenu.setEnabled(false);
69 |
70 | // Build About Menu
71 | helpMenu = new JMenu("Help");
72 | helpMenu.setMnemonic(KeyEvent.VK_H);
73 |
74 | // Add Menu Items to Menu "About"
75 | helpMenuItem = new JMenuItem("Help", KeyEvent.VK_H);
76 | helpMenu.add(helpMenuItem);
77 |
78 | // Add Menus "File" to Menu Bar
79 | menuBar.add(fileMenu);
80 | menuBar.add(helpMenu);
81 |
82 | // Add Components
83 | this.setJMenuBar(menuBar);
84 | }
85 |
86 | private void configureTextPane(String content) {
87 | this.textPane.setText(content);
88 | this.textPane.getDocument().addDocumentListener(this);
89 | this.add(this.textPane, BorderLayout.CENTER);
90 | }
91 |
92 | /**
93 | * 获取当前TextPane的内容
94 | *
95 | * @return TextPane中所有文字内容
96 | */
97 | public String getContent() {
98 | return this.textPane.getText();
99 | }
100 |
101 | public FCB getDataFCB() {
102 | return dataFCB;
103 | }
104 |
105 | // Document Listener
106 | @Override
107 | public void insertUpdate(DocumentEvent e) {
108 | this.edited = true;
109 |
110 | // 改变窗口标题
111 | this.setTitle(this.dataFCB.filename + " - Edited");
112 | }
113 |
114 | @Override
115 | public void removeUpdate(DocumentEvent e) {
116 | this.edited = true;
117 |
118 | // 改变窗口标题
119 | this.setTitle(this.dataFCB.filename + " - Edited");
120 | }
121 |
122 | @Override
123 | public void changedUpdate(DocumentEvent e) {
124 |
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/src/view/MainView.java:
--------------------------------------------------------------------------------
1 | package view;
2 |
3 | import java.awt.BorderLayout;
4 | import java.awt.Color;
5 | import java.awt.Component;
6 | import java.awt.Dimension;
7 | import java.awt.FlowLayout;
8 | import java.awt.event.ActionListener;
9 | import java.awt.event.ComponentEvent;
10 | import java.awt.event.ComponentListener;
11 | import java.awt.event.KeyEvent;
12 | import java.awt.event.MouseListener;
13 |
14 | import javax.swing.BorderFactory;
15 | import javax.swing.BoxLayout;
16 | import javax.swing.JButton;
17 | import javax.swing.JFrame;
18 | import javax.swing.JMenu;
19 | import javax.swing.JMenuBar;
20 | import javax.swing.JMenuItem;
21 | import javax.swing.JPanel;
22 | import javax.swing.JScrollPane;
23 | import javax.swing.JTextField;
24 | import javax.swing.ScrollPaneConstants;
25 |
26 | import model.sys.FCB;
27 |
28 | /**
29 | *
30 | * @author Tom Hu
31 | *
32 | */
33 | public class MainView extends JFrame {
34 |
35 | private static final long serialVersionUID = 6313156717813295316L;
36 |
37 | private JButton backButton;
38 | public JTextField addressTextField;
39 | public JButton goButton;
40 | public JPanel contentPanel;
41 |
42 | // Constructor
43 | public MainView(FCB[] fcbDir) {
44 | super();
45 |
46 | // initialize
47 | this.contentPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
48 | this.backButton = new JButton("Back");
49 | this.addressTextField = new JTextField("test");
50 | this.goButton = new JButton("Go");
51 |
52 | // UI Methods
53 | this.configureMenuBar();
54 | this.configureToolPanel();
55 | this.configureContentScrollPane(fcbDir);
56 |
57 | // Main View
58 | this.configureJFrame();
59 | }
60 |
61 | // UI Method
62 | private void configureJFrame() {
63 | this.setTitle("File System Simulator");// Set title
64 | this.setSize(Config.WINDOW_WIDTH, Config.WINDOW_HEIGHT);// Set size of
65 | // window
66 | this.setResizable(false);// Can't change the size
67 | this.setLocationRelativeTo(null);// Set the position of the window -
68 | // Screen's Center
69 | this.setBackground(Color.WHITE);
70 | }
71 |
72 | private void configureMenuBar() {
73 | // Components
74 | JMenuBar menuBar;
75 | JMenu fileSystemMenu;
76 | JMenu aboutMenu;
77 | JMenuItem aboutMenuItem;
78 |
79 | // Create the Menu Bar
80 | menuBar = new JMenuBar();
81 |
82 | // Build Elevator Menu
83 | fileSystemMenu = new JMenu("File System");
84 | fileSystemMenu.setMnemonic(KeyEvent.VK_F);
85 | fileSystemMenu.setEnabled(false);
86 |
87 | // Build About Menu
88 | aboutMenu = new JMenu("About");
89 | aboutMenu.setMnemonic(KeyEvent.VK_A);
90 |
91 | // Add Menu Items to Menu "About"
92 | aboutMenuItem = new JMenuItem("About", KeyEvent.VK_A);
93 | aboutMenu.add(aboutMenuItem);
94 |
95 | // Add Menus "File" and "Help" to Menu Bar
96 | menuBar.add(fileSystemMenu);
97 | menuBar.add(aboutMenu);
98 |
99 | // Add Components
100 | this.setJMenuBar(menuBar);
101 | }
102 |
103 | private void configureToolPanel() {
104 | // initialize toolPanel
105 | JPanel toolPanel = new JPanel();
106 |
107 | // for debug
108 | toolPanel.setBackground(Color.BLACK);
109 |
110 | // Set Layout
111 | toolPanel.setLayout(new BoxLayout(toolPanel, BoxLayout.X_AXIS));
112 | toolPanel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20));
113 |
114 | // Add to toolPanel
115 | toolPanel.add(this.backButton);
116 | toolPanel.add(this.addressTextField);
117 | toolPanel.add(this.goButton);
118 |
119 | // Add to mainView
120 | this.add(toolPanel, BorderLayout.PAGE_START);
121 | }
122 |
123 | private void configureContentScrollPane(FCB[] fcbDir) {
124 | // Clear all children components
125 | this.contentPanel.removeAll();
126 |
127 | // Set background color
128 | this.contentPanel.setBackground(Color.WHITE);
129 |
130 | // Add component listener for contentPanel
131 | this.contentPanel.addComponentListener(new ComponentListener() {
132 |
133 | @Override
134 | public void componentResized(ComponentEvent e) {
135 | System.out.println("resize");
136 |
137 | Dimension d = MainView.this.contentPanel.getPreferredSize();
138 | int con = MainView.this.contentPanel.getComponents().length;
139 | int col = Config.WINDOW_WIDTH
140 | / (Config.FILE_ICON_PANEL_SIZE + 5);
141 | int row = con / col + 1;
142 | int newHeight = row * (Config.FILE_ICON_PANEL_SIZE + 5) + 5;
143 | d.height = newHeight;
144 | d.width = Config.WINDOW_WIDTH;
145 | MainView.this.contentPanel.setPreferredSize(d);
146 |
147 | }
148 |
149 | @Override
150 | public void componentMoved(ComponentEvent e) {
151 | }
152 |
153 | @Override
154 | public void componentShown(ComponentEvent e) {
155 | }
156 |
157 | @Override
158 | public void componentHidden(ComponentEvent e) {
159 | }
160 | });
161 |
162 | // For test
163 | for (int i = 0; i < fcbDir.length; i++) {
164 | if (fcbDir[i] == null) {
165 | break;
166 | }
167 |
168 | DocumentIconPanel t = new DocumentIconPanel(fcbDir[i].type,
169 | fcbDir[i].filename);
170 |
171 | this.contentPanel.add(t);
172 | }
173 |
174 | // initialize content scroll pane
175 | System.out.println("initialize contentScrollPane");
176 | JScrollPane contentScrollPane = new JScrollPane(this.contentPanel);
177 | contentScrollPane
178 | .setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
179 |
180 | // Add to mainView
181 | System.out.println("add to mainView");
182 | this.add(contentScrollPane, BorderLayout.CENTER);
183 | }
184 |
185 | // Show view
186 | public void showView() {
187 | System.out.println("show view");
188 |
189 | // Show View
190 | this.setVisible(true);
191 | this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
192 | }
193 |
194 | // Actions
195 | public void addDocument(DocumentIconPanel documentIconPanel) {
196 | // For test
197 | this.contentPanel.add(documentIconPanel);
198 | this.contentPanel.revalidate();
199 | }
200 |
201 | public void addRightClickListener(MouseListener rightClickListener) {
202 | this.contentPanel.addMouseListener(rightClickListener);
203 | }
204 |
205 | public void addBackButtonActionListener(ActionListener actionListener) {
206 | this.backButton.addActionListener(actionListener);
207 | }
208 |
209 | public void addGoButtonActionListener(ActionListener actionListener) {
210 | this.goButton.addActionListener(actionListener);
211 | }
212 |
213 | public void addDocumentIconPanelMouseListener(
214 | MouseListener documentIconPanelMouseListener) {
215 | System.out.println("add document listener");
216 | for (Component item : this.contentPanel.getComponents()) {
217 | ((DocumentIconPanel) item)
218 | .addMouseListener(documentIconPanelMouseListener);
219 | }
220 | }
221 |
222 | public void deselectDocuments() {
223 | for (Component item : this.contentPanel.getComponents()) {
224 | ((DocumentIconPanel) item).setSelected(false);
225 | }
226 | }
227 |
228 | public void reloadContent(FCB[] fcbDir) {
229 | // 移除所有components
230 | this.contentPanel.removeAll();
231 |
232 | // 重新添加
233 | for (int i = 0; i < fcbDir.length; i++) {
234 | if (fcbDir[i] == null) {
235 | break;
236 | }
237 |
238 | DocumentIconPanel t = new DocumentIconPanel(fcbDir[i].type,
239 | fcbDir[i].filename);
240 |
241 | this.contentPanel.add(t);
242 | }
243 |
244 | this.contentPanel.repaint();
245 | this.contentPanel.revalidate();
246 | }
247 | }
248 |
--------------------------------------------------------------------------------