使用NVelocity自动生成Favorite收藏夹的导航页面
你是否碰到你的收藏夹凌乱不堪,或者很多收藏网页地址虽然分类,可是使用起来却感觉不太好,有没有想过把你的收藏夹内容自动生成一个网页的导航页面生成类似Hao123(
http://www.hao123.com/
),或者番茄花园导航(
http://www.tomatolei.com/
)一样的网页,用来迅速导航到自己的网址呢?
即使使用场景有争议,不过我们也来关注一下这种自动生成页面的技术吧,说不定可以使用在合适的环境下,如下面我测试了两个类别的收藏夹Favorite目录下,其中“搜索引擎”和“新闻网络”两个目录是我的测试收藏夹分类,Icon和Images是我构造生成主页所需要的一些内容目录。我们根据Index.htm来生成index.htm来作为导航的主页面。
由于我们不想改变收藏夹的IE快捷方式的内容,因此收藏夹里面的内容还是以IE快捷方式来保存,如下图所示。
其中IE快捷方式的内容如下所示,文件时采用INI格式进行保存的。
BASEURL=http://g.cn
[InternetShortcut]
URL=http://g.cn
其中的Default中的BaseURL是域名地址,InternetShortcut中的URL是具体的网页地址,基本上这些内容就可以构造成一个IE快捷方式了。
整体运行的效果如下图所示。其中内容可以动态生成,由于采用了NVelocity引擎来生成内容,速度非常快,和你直接浏览一个html页面的速度相差无几。
下面我们来具体介绍下如何实现这个页面的自动生成吧。
单击Home主页的时候,我们就自动生成一个页面,生成代码如下所示。
代码
///
<summary>
///
导航到主页面
///
</summary>
public
void
GoHome()
{
string
tempFile
=
"
Favorite/index.txt
"
;
try
{
HelperClassAdapter helper
=
new
HelperClassAdapter(tempFile);
helper.Execute();
//
MessageUtil.ShowTips("生成成功");
bool
found
=
false
;
foreach
(IDockContent content
in
this
.dockPanel.Documents)
{
MainIE form
=
content
as
MainIE;
if
(form
!=
null
&&
form.extendedWebBrowser1.DocumentTitle
==
"
收藏夹导航
"
)
{
found
=
true
;
form.Activate();
break
;
}
}
if
(
!
found)
{
this
._currentUrl
=
"
my:home
"
;
AddMainIE();
}
}
catch
(Exception ex)
{
LogHelper.Error(ex);
MessageUtil.ShowTips(ex.Message);
}
}
这里其实主要的就是生成代码的类HelperClassAdapter,这个类通过获得模版文件,通过Nevelocity引擎把指定格式的内容附加到模版文件中,然后输出文件即可。
代码
public
class
HelperClassAdapter : AbstractAdapter
{
public
HelperClassAdapter(
string
templateFile)
:
base
(templateFile)
{
}
public
override
void
Execute()
{
string
startPath
=
Path.Combine(Application.StartupPath,
"
Favorite
"
);
if
(
!
Directory.Exists(startPath))
{
Directory.CreateDirectory(startPath);
}
//
遍历目录和文件,根据文件模版输出到文件中
#region
根据目录放置的字典
Dictionary
<
string
, List
<
FavoriteInfo
>>
fileDict
=
new
Dictionary
<
string
, List
<
FavoriteInfo
>>
();
string
[] dirArray
=
Directory.GetDirectories(startPath);
foreach
(
string
dir
in
dirArray)
{
string
dirName
=
new
DirectoryInfo(dir).Name;
if
(dirName.ToLower()
==
"
icon
"
||
dirName.ToLower()
==
"
images
"
)
//
系统图标跳过
continue
;
string
[] fileArray
=
Directory.GetFiles(dir);
List
<
FavoriteInfo
>
fileList
=
new
List
<
FavoriteInfo
>
();
FavoriteInfo info
=
null
;
foreach
(
string
file
in
fileArray)
{
if
(
!
string
.IsNullOrEmpty(file))
{
info
=
new
FavoriteInfo();
info.Category
=
dirName;
info.Title
=
new
FileInfo(file).Name.Replace(
"
.url
"
,
""
);
INIFileUtil iniUtil
=
new
INIFileUtil(file);
info.Url
=
iniUtil.IniReadValue(
"
InternetShortcut
"
,
"
URL
"
);
fileList.Add(info);
}
}
if
(
!
fileDict.ContainsKey(dir))
{
fileDict.Add(dir, fileList);
}
}
#endregion
//
设置输出文件名称和页面变量,然后输出文件
this
.FileNameOfOutput
=
Path.Combine(Application.StartupPath,
"
Favorite/index.htm
"
);
context.Put(
"
FileDict
"
, fileDict);
OutputFile();
}
}
其中模版文件index.txt文件很简单,部分关键代码如下所示,其中主要关注有$开始的运算符和变量,这些代码是实现对象到页面内容的转换的。其中#foreach($FavoriteList in ${FileDict.Values})是遍历里面的对象,进行循环处理的,语法也很简单。
代码
DIV
id
=content
>
<!--
/ sidebar
--><!--
/ board
-->
<
div
class
=box
id
=coolsites
>
<
h2
align
="center"
class
="style2"
>
收藏夹导航
<
a
class
=""
id
=cslayout
title
=居中对齐/左对齐
>
居中对齐/左对齐
</
a
></
h2
>
<
div
id
=list
>
#foreach($FavoriteList in ${FileDict.Values})
<
dl
>
#set($count = 0)
#foreach($FavoriteInfo in $FavoriteList)
#if($count==0)
<
dt
><
a
href
="#"
>
[ ${FavoriteInfo.Category.ToString()} ]
</
a
>
#end
#set($count = $count + 1)
<
dd
><
a
href
="${FavoriteInfo.Url.ToString()}"
>
${FavoriteInfo.Title.ToString()}
</
a
>
#end
</
dd
></
dl
>
#end
</
div
>
</
div
>
</
div
>
另外FavoriteInfo是一些基本属性的实体类,代码很简单:
代码
public
class
FavoriteInfo
{
private
string
m_category;
//
目录分类名称
private
string
m_title;
//
网页标题
private
string
m_url;
//
网页链接
///
<summary>
///
目录分类名称
///
</summary>
public
string
Category
{
get
{
return
m_category; }
set
{ m_category
=
value; }
}
///
<summary>
///
网页标题
///
</summary>
public
string
Title
{
get
{
return
m_title; }
set
{ m_title
=
value; }
}
///
<summary>
///
网页链接
///
</summary>
public
string
Url
{
get
{
return
m_url; }
set
{ m_url
=
value; }
}
}
HelperClassAdapter的基类如下所示,主要是定义一些公共的属性变量及操作,方便被继承成各种类型的子类。
代码
public
abstract
class
AbstractAdapter : IAdapter
{
#region
保护成员变量
protected
VelocityContext context;
protected
Template template;
protected
string
templateFile;
protected
const
string
NVELOCITY_PROPERTY
=
"
nvelocity.properties
"
;
#endregion
#region
成员变量及公共属性
private
string
fileExtension
=
"
.htm
"
;
//
输出的文件后缀名,如".cs"。
private
string
directoryOfOutput
=
"
Articles
"
;
//
输出文件的文件夹名称
private
string
fileNameOfOutput;
//
输出的文件名称
///
<summary>
///
输出文件的文件夹名称, 如"Entity","Common"等
///
</summary>
public
string
DirectoryOfOutput
{
set
{ directoryOfOutput
=
value; }
get
{
return
directoryOfOutput; }
}
///
<summary>
///
输出的文件名称. 如果想输出的文件为 "A.cs", 那么文件名为"A".
///
默认的文件名称为模板文件的名称,没有后缀名.
///
</summary>
public
string
FileNameOfOutput
{
set
{ fileNameOfOutput
=
value; }
get
{
return
fileNameOfOutput; }
}
///
<summary>
///
输出的文件后缀名,如".cs"。
///
</summary>
public
string
FileExtension
{
get
{
return
fileExtension; }
set
{ fileExtension
=
value; }
}
#endregion
///
<summary>
///
根据参数初始化基类的Adapter
///
</summary>
///
<param name="templateFile">
模板文件名称
</param>
public
AbstractAdapter(
string
templateFile)
{
#region
判断输入的参数是否有效
if
(templateFile
==
null
)
{
const
string
error
=
"
Invalid parameter of templateFile.
"
;
LogHelper.Error(error);
throw
new
ArgumentException(error,
"
templateFile
"
);
}
#endregion
this
.templateFile
=
templateFile;
InitTemplateEngine();
//
初始化模板引擎
fileNameOfOutput
=
GetFileNameFromTemplate(templateFile);
//
默认情况下, 输出的文件名称为模板名称
directoryOfOutput
=
"
Articles
"
;
//
默认情况下,放到Common目录下面
}
///
<summary>
///
创建输出到文件中的方法
///
</summary>
public
virtual
void
Execute()
{
}
///
<summary>
///
根据模板创建输出的文件
///
</summary>
public
virtual
void
OutputFile()
{
if
(template
!=
null
)
{
string
filePath
=
Path.GetDirectoryName(
this
.fileNameOfOutput);
DirectoryInfo dir
=
new
DirectoryInfo(filePath);
if
(
!
dir.Exists)
{
dir.Create();
}
LogHelper.Debug(
string
.Format(
"
Class file output path:{0}
"
,
this
.fileNameOfOutput));
using
(StreamWriter writer
=
new
StreamWriter(
this
.fileNameOfOutput,
false
))
{
template.Merge(context, writer);
}
}
}
///
<summary>
///
根据模板创建代码输出到字符串流中
///
</summary>
///
<returns>
根据模板生成的代码
</returns>
public
virtual
string
OutputString()
{
string
code
=
string
.Empty;
if
(template
!=
null
)
{
using
(StringWriter writer
=
new
StringWriter())
{
template.Merge(context, writer);
code
=
writer.GetStringBuilder().ToString();
}
}
return
code;
}
///
<summary>
///
初始化NVelocity模板引擎并加载程序的配置信息e
///
</summary>
protected
void
InitTemplateEngine()
{
Velocity.Init(NVELOCITY_PROPERTY);
//
Velocity.Init();
//
Make a context object and populate with the data. This is where the Velocity engine
//
gets the data to resolve the references (ex. $foreignList) in the template
context
=
new
VelocityContext();
try
{
template
=
Velocity.GetTemplate(templateFile);
}
catch
(ResourceNotFoundException re)
{
string
error
=
string
.Format(
"
Cannot find template
"
+
templateFile);
LogHelper.Error(error);
throw
new
Exception(error, re);
}
catch
(ParseErrorException pee)
{
string
error
=
string
.Format(
"
Syntax error in template
"
+
templateFile
+
"
:
"
+
pee.StackTrace);
LogHelper.Error(error);
throw
new
Exception(error, pee);
}
}
#region
辅助方法
///
<summary>
///
从模板文件名称获取输出文件名的方法
///
</summary>
///
<param name="templateFileName">
带后缀名的模板文件名
</param>
///
<returns>
输出的文件名(无后缀名)
</returns>
private
string
GetFileNameFromTemplate(
string
templateFileName)
{
int
sindex1
=
templateFileName.LastIndexOf(
'
/
'
);
int
sindex2
=
templateFileName.LastIndexOf(
'
\\
'
);
int
sindex
=
(sindex1
>
sindex2)
?
sindex1 : sindex2;
int
eindex
=
templateFileName.IndexOf(
'
.
'
);
if
(sindex
<
eindex)
{
return
templateFileName.Substring(sindex
+
1
, eindex
-
sindex
-
1
);
}
else
{
return
templateFileName.Substring(sindex);
}
}
#endregion
}
public
interface
IAdapter
{
///
<summary>
///
创建输出到文件中的方法
///
</summary>
void
Execute();
}
另外,读取INI文件,需要的辅助类代码INIFileUtil如下所示。
代码
public
class
INIFileUtil
{
public
string
path;
public
INIFileUtil(
string
INIPath)
{
path
=
INIPath;
}
[DllImport(
"
kernel32
"
)]
private
static
extern
long
WritePrivateProfileString(
string
section,
string
key,
string
val,
string
filePath);
[DllImport(
"
kernel32
"
)]
private
static
extern
int
GetPrivateProfileString(
string
section,
string
key,
string
def, StringBuilder retVal,
int
size,
string
filePath);
[DllImport(
"
kernel32
"
)]
private
static
extern
int
GetPrivateProfileString(
string
section,
string
key,
string
defVal, Byte[] retVal,
int
size,
string
filePath);
///
<summary>
///
写INI文件
///
</summary>
///
<param name="Section"></param>
///
<param name="Key"></param>
///
<param name="Value"></param>
public
void
IniWriteValue(
string
Section,
string
Key,
string
Value)
{
WritePrivateProfileString(Section,Key,Value,
this
.path);
}
///
<summary>
///
读取INI文件
///
</summary>
///
<param name="Section"></param>
///
<param name="Key"></param>
///
<returns></returns>
public
string
IniReadValue(
string
Section,
string
Key)
{
StringBuilder temp
=
new
StringBuilder(
255
);
int
i
=
GetPrivateProfileString(Section,Key,
""
,temp,
255
,
this
.path);
return
temp.ToString();
}
public
byte
[] IniReadValues(
string
section,
string
key)
{
byte
[] temp
=
new
byte
[
255
];
int
i
=
GetPrivateProfileString(section, key,
""
, temp,
255
,
this
.path);
return
temp;
}
///
<summary>
///
删除ini文件下所有段落
///
</summary>
public
void
ClearAllSection()
{
IniWriteValue(
null
,
null
,
null
);
}
///
<summary>
///
删除ini文件下personal段落下的所有键
///
</summary>
///
<param name="Section"></param>
public
void
ClearSection(
string
Section)
{
IniWriteValue(Section,
null
,
null
);
}
}