Implement validator in Eclipse - eclipse

I am trying to implement validation in Eclipse. My work has many little projects that serve to customize our product for various customers. Since there are a ton of developers we are trying to find a way to enforce various standards and best practices.
For example, we have many XML configurations that we want to validate. The built-in validators ensure files are well-formed and follow a schema, but we would like to add validations such as checking that a Java class referenced in XML actually exists on the classpath. Another example is validating that a Java class implementing a certain interface does not have any object variables (i.e. the code needs to operate only on parameters and not maintain state).
It appears that there are two ways to add validation. The first is through a builder which adds markers. The second is through stand-alone validation. However, we are not actually building anything, and I have not found any useful tutorials or examples on validation (does not help that help.eclipse.org is currently being moved and is unavailable).
When I right-click a test project and select "validate" I get a message stating there was an error during validation, and my test message does not show up in the problem view. However, there are no errors in the Eclipse log. The host Eclipse shows nothing in the console. No exceptions logged anywhere, and no message. The project does have the required custom nature.
I was following these instructions but there is no code or fully functioning example, and Google has not been kind enough to fill in the blanks. Combined with the Eclipse help site being down right now, I am at a loss as to how to proceed.
plugin.xml:
<plugin>
<extension name="My Validator" point="org.eclipse.wst.validation.validator"
id="com.mycompany.pluginname.validator.MyValidator">
<validator>
<projectNature id="com.mycompany.pluginname.nature.MyNature"/>
<helper class="org.eclipse.wst.validation.internal.operations.WorkbenchContext"/>
<markerId markerIdValue="com.mycompany.pluginname.validator.DefaultMarker"/>
<run class="com.mycompany.pluginname.validation.validator.MyValidator"/>
<runStrategy project="true"/>
</validator>
</extension>
<extension point="org.eclipse.core.resources.markers" name="My Validator"
id="com.mycompany.pluginname.validator.DefaultMarker">
<super type="org.eclipse.core.resources.problemmarker"/>
<persistent value="true"/>
<attribute name="owner"/>
<attribute name="validationSeverity"/>
<attribute name="targetObject"/>
<attribute name="groupName"/>
<attribute name="messageId"/>
</extension>
</plugin>
Validator code:
package com.mycompany.pluginname.validation.validator;
import org.eclipse.core.resources.IProject;
import org.eclipse.wst.validation.internal.core.Message;
import org.eclipse.wst.validation.internal.core.ValidationException;
import org.eclipse.wst.validation.internal.operations.IWorkbenchContext;
import org.eclipse.wst.validation.internal.provisional.core.*;
import com.mycompany.pluginname.validation.plugin.ValidationPlugin;
#SuppressWarnings("restriction")
public class MyValidator
implements IValidator {
#Override
public void cleanup(IReporter argReporter) {
argReporter.removeAllMessages(this);
}
#Override
public void validate(IValidationContext argContext, IReporter argReporter)
throws ValidationException {
String bundle = ValidationPlugin.getDefault().getTranslationsBundleName();
IProject prj = ((IWorkbenchContext) argContext).getProject();
String[] attributes =
new String[] {"owner", "validationSeverity", "targetObject", "groupName", "messageId"};
IMessage msg = new Message(bundle, IMessage.HIGH_SEVERITY, "test", attributes, prj);
argReporter.addMessage(this, msg);
}
}
I also find it odd that adding validation would require using restricted packages and interfaces. It also seems odd that it wants an IMessage rather than an IMarker.
I did look at Eclipse plugin with custom validation which seems to be oriented around creating a new editor, where I want to validate files regardless of the editor used (in fact I do not want to create an editor).
Edit: I updated to use the V2 framework, but nothing appears in the problem view. What am I doing wrong? Is there a tutorial somewhere that explains how this works? I was able to figure out the following, but obviously it is not correct:
public ValidationResult validate(ValidationEvent argEvent, ValidationState argState,
IProgressMonitor argMonitor) {
final IResource resource = argEvent.getResource();
final ValidationResult result = new ValidationResult();
try {
List<String> contents = Resources.readFile((IFile) resource);
for (int i = 0; i < contents.size(); ++i) {
int offset = contents.get(i).indexOf("bad_string");
if (offset >= 0) {
result.add(ValidatorMessage.create("Found bad string", resource));
result.incrementError(1);
}
}
}
catch (Exception ex) {
result.add(ValidatorMessage.create(ex.getMessage(), resource));
}
return result;
}
I admit this is a stab in the dark: the documentation is not very descriptive and I have not found any tutorials on this V2 validator. Oh, I have a filter on this validator so it only receives specific XML files, which is why there is no input validation.
Also, since I am a pedant myself, I am using the old-style for loop there because I expect to show the line number with the error to the user. But obviously I am not quite there yet.
Another edit: here is the working code. The only issue is the squiggly is not on the correct line because the offset is from the start of the file, not the line. But it does work:
public ValidationResult validate(ValidationEvent argEvent, ValidationState argState,
IProgressMonitor argMonitor) {
final IResource resource = argEvent.getResource();
final ValidationResult result = new ValidationResult();
try {
List<String> contents = Resources.readFile((IFile) resource);
int location = 0;
for (int i = 0; i < contents.size(); ++i) {
int offset = contents.get(i).indexOf(CONSTANT);
if (offset >= 0) {
ValidatorMessage vm = ValidatorMessage.create("Message", resource);
vm.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
vm.setAttribute(IMarker.SOURCE_ID, IMarker.PROBLEM);
vm.setAttribute(IMarker.LINE_NUMBER, i + 1);
vm.setAttribute(IMarker.CHAR_START, location + offset);
vm.setAttribute(IMarker.CHAR_END, location + offset + CONSTANT.length());
result.add(vm);
}
// TODO: account for different line endings.
location += (line.length() + 2);
}
}
catch (Exception ex) {
ValidationPlugin.getDefault().warn(ex);
result.add(ValidatorMessage.create(ex.toString(), resource));
}
return result;
}
Plugin.xml:
<extension name="My Validator" point="org.eclipse.wst.validation.validatorV2"
id="com.company.plugin.validation.validator.MyValidator">
<validator class="com.company.plugin.validation.validator.MyValidator">
<include>
<rules>
<file type="file" name="FileName.xml"/>
</rules>
</include>
</validator>
</extension>
I actually found another SO question along these lines that corroborates what I found: Setting IMarker.CHAR_START and IMarker.CHAR_END attributes for IMarkers Annotations

sigh that document is very out of date. You should use the org.eclipse.wst.validation.validatorV2 extension point, extending the newer org.eclipse.wst.validation.AbstractValidator class.

Related

Why does CanDeserialize always return false when deserialization succeeds?

I am attempting to deserialize an xml string into an object, nothing strange about that. Everything was fine until I upgraded my project to .Net5.
In the upgrade, I had to add a reference to the package Microsoft.XmlSerializer.Generator, and alter the project file to add the following:
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.XmlSerializer.Generator" Version="1.0.0" />
</ItemGroup>
That allowed me to create the XmlSerializer (first error was just weird). Now, however, every call to CanDeserialize on the XmlReader return false if the class has the XmlRoot attribute. Now, I can deserialize the xml text. That does work. But why would CanDeserialize fail based on that condition?
Below is the class and the code I am using to test in a console app (.Net5).
[Serializable, XmlRoot("TestObj")]
//[Serializable]
public class TestObj
{
public int TestVal;
}
static void Main(string[] args)
{
var serializer = new XmlSerializer(typeof(TestObj));
//generated by doing a test serialization of the class
var teststr = "<TestObj xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"><TestVal>2</TestVal></TestObj>";
using (var str = new StringReader(teststr))
using (var reader = XmlReader.Create(str))
{
if (serializer.CanDeserialize(reader))
Console.WriteLine(((TestObj)serializer.Deserialize(reader)).TestVal);
else
{
Console.WriteLine("Value cannot be deserialized into the given Type");
//try it anyway
var o = (TestObj)serializer.Deserialize(reader);
Console.WriteLine(o.TestVal);
}
}
}
My workaround is just to eliminate the CanDeserialize call and wrap the deserialization in a try.. catch, but I'm still curious why this is happening.
Okay, comments on the question from #dbc did lead me to try something else. A couple of details I left of the original question because it didn't seem relevant (due to the testing involved) is that the class I am trying to deserialize in my non-test is in a .Net Standard 2.1 library. While the project doing the deserialization is .Net5.
In the end, I got this work. I had to reference the Microsoft.XmlSerializer.Generator package in my .Net Standard based library, but not reference it in the .Net5 project.
In short, leaving the package reference off the Standard project caused one error, and including it in both caused another.

Show warning when EL not found

I'm creating a JSF Applikation and i would like to get some kind of warning (preferably i the console) if i make typos in my EL-Expression.
Example:
On my page i wanted to show some Text that is localized. The locale-config in faces-config.xml is properly set up and works with the var 'msgs'.
I used this code on my page:
<h:outputText value="#{msg.title_edit_customer}"/>
When i checked my Page in the browser, nothing got showed.
I took me a while to realize that i made a typo - with #{msgs.... it worked as expected.
Can i activate some kind of Debug-Output, so i can see directly that there is an invalid EL somewhere?
My Setup: Eclipse 4.4.2, Tomcat 8, MyFaces 2.2.8
Thanks to #SJuan76 i could figure it out:
Create your own javax.el.ELResolver, all the Methods can return null/false.
Open the Source of the Class org.apache.myfaces.el.unified.resolver.ScopedAttributeResolver and copy the methods facesContext(ELContext) and findScopedMap(FacesContext, Object) (since ScopedAttributeResolver is final, we can't extend it).
Edit the getValue-Method:
#Override
public Object getValue(ELContext context, Object base, Object property) {
if(!context.isPropertyResolved()){
//Douple Check false-positives
boolean foundInScope = false;
final Map<String, Object> scopedMap = findScopedMap(
facesContext(context), property);
if (scopedMap != null) {
Object object = scopedMap.get(property);
if (object != null) {
foundInScope = true;
}
}
if (!foundInScope) {
log.warn(String.format("EL-Property %s couldn't be resolved",
property));
}
}
return null;
}
Edit faces-config.xml to register your resolver:
<application>
<el-resolver>com.myPackage.DebugELResolver</el-resolver>
</application>
Since there are many Resolvers and each one does a little bit of the Resolving, our new Resolver should come last. Add following to web.xml:
<context-param>
<param-name>org.apache.myfaces.EL_RESOLVER_COMPARATOR</param-name>
<param-value>org.apache.myfaces.el.unified.CustomLastELResolverComparator</param-value>
</context-param>
Now you'll get some log-Output, every time an expression couldn't be resolved:
03.07.2015 07:34:21 com.myPackage.DebugELResolver [http-nio-8080-exec-2] WARN EL-Property msgx couldn't be resolved
I couldn't figure out how to get the actual EL-Expression (e.g. #{msgx.title_edit_customer}.

NLog config file to get configuration setting values from a web.config

Is there a method to get a value from the <ApplicationSettings> section of a web.config within NLog layout variables?
I already store SMTP details within my web.config and don't want to duplicate the settings just to use within my NLog.config.
Ideally I'd like to do something like: ${aspnet-config:SmtpHostServer}
which then fetches the value from the web.config
I couldn't see any obvious way to do this other than creating my own LayoutRenderer (see below). If you're putting into your own assembly don't forget to add the following into your NLog.Config:
<extensions>
<add assembly="YOURASSEMBLYNAMEHERE" />
</extensions>
Hope this helps someone else:
[LayoutRenderer("aspnet-config")]
public class AspNetConfigValueLayoutRenderer : LayoutRenderer
{
[DefaultParameter]
public string Variable
{
get;
set;
}
protected override void Append(StringBuilder builder, LogEventInfo logEvent)
{
if (this.Variable == null)
{
return;
}
HttpContext context = HttpContext.Current;
if (context == null)
{
return;
}
builder.Append(Convert.ToString(System.Configuration.ConfigurationManager.AppSettings[this.Variable], CultureInfo.InvariantCulture));
}
}
Updated Answer
NLog ver. 4.6 includes ${appsetting:SmtpHostServer} in the core NLog-nuget-package. No longer requires NLog.Extended. See also https://github.com/nlog/NLog/wiki/AppSetting-Layout-Renderer
NLog.Extensions.Logging ver. 1.4 includes ${configsetting} that allows one to read settings from appsettings.json. See also https://github.com/NLog/NLog/wiki/ConfigSetting-Layout-Renderer
Original Answer
Nowadays this is possible without custom code:
Use NLog.Extended and use
${appsetting:SmtpHostServer}.
See docs for ${appsetting}
Please note: this isn't supported in .NET Core / .NET standard yet.

how to change the themes in asp.net mvc 2

I would like to have an option wherein a user can choose his theme for the site from the dropdown list and the theme applies to that page [atleast].
I want this to be done in ASP.NET MVC 2 without using jquery like frameworks.
How can this be accomplished.
I am using the default webforms viewengine and donot want to go for a custom viewengine for this purpose.
It seems this is not supported out of the box, but here's what I did to implement theming:
First, I Added the App_Themes folder to my project, and set up a couple of themes
I then decided to try and mimic the Web-forms profile provider as close as possible, and added a profile-property to web.config:
<profile>
<properties>
<add name="ThemePreference" type="string" defaultValue="Blue" />
</properties>
</profile>
So, basically what I wanted to do was to be able to load the different css's from the appropriate theme-folder when the theme changed. I did this by implementing a helper method attached to the UrlHelper class so that I could write:
<link href="#Url.Theme("~/Content/Site.css")" rel="stylesheet" type="text/css" />
This should then load the appropriate themed Site.css, and fall back to ~/Content/Site.css if no file was found.
The helper is pretty simple:
public static class UrlHelpers
{
public static string Theme(this UrlHelper url, string u)
{
if (u.StartsWith("~")) u = u.TrimStart('~');
SettingsProperty settingsProperty = ProfileBase.Properties["ThemePreference"];
return url.Content("~/App_Themes/"+settingsProperty.DefaultValue + u);
}
}
Now, in this version of the code it simply gets the default-value, so you'll need to tweak the code slightly. But as you can see, this is not limited to css-files, but works with everything from .master files to images.
Update - Using Session instead of profile
public static class UrlHelpers
{
public static string Theme(this UrlHelper url, string u)
{
if (u.StartsWith("~")) u = u.TrimStart('~');
object currentThemeName = null;
if (url.RequestContext.HttpContext.Session != null)
{
currentThemeName = url.RequestContext.HttpContext.Session["ThemePreference"];
}
return currentThemeName != null ? url.Content(String.Format("~/App_Themes/{0}{1}", currentThemeName, u)) : url.Content("~"+u);
}
}
The return-line in this method checks if it found a ThemePreference session-value, and then returnes the appropriate URL for the content requested, otherwise it simply returns the content as it was requested with no App_Theme prefix.
In your controlleraction for the DropDown postmethod, you'd simply do:
Session.Add("ThemePreference", whateverValueYouGotFromDropdown);
Update ends
With some tweaking and fixing, this should do the trick.
Hope it helps some, even though it's not a complete walkthrough :)

MEF Contrib Provider Model Not Importing Parts

I have been trying to use the configurable provider model for handling my MEF imports and exports from MEF Contrib (link). I've read the Codeplex documentation and Code Junkie's blog post (link); however, I can't seem to get the container to create the parts. Where am I going wrong?
Program.cs
namespace MEFTest
{
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Run();
}
// [ImportMany("command", typeof(IHelp))]
public IEnumerable<IHelp> Commands { get; set; }
void Run()
{
Compose();
foreach(IHelp cmd in Commands)
{
Console.WriteLine(cmd.HelpText);
}
Console.ReadKey();
}
void Compose()
{
var provider = new ConfigurableDefinitionProvider("mef.configuration");
var catalog = new DefinitionProviderPartCatalog<ConfigurableDefinitionProvider>(provider);
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
}
}
}
TestCommand.cs
namespace MEFTest
{
//[Export("command", typeof(IHelp))]
public class TestCommand : IHelp
{
private string _helpText = "This is a test.";
public string CommandName
{
get { return "Test"; }
}
public string HelpText
{
get { return _helpText; }
}
}
}
App.Config section:
<mef.configuration>
<parts>
<part type="MEFTest.TestCommand, MEFTest">
<exports>
<export contract="IHelp" />
</exports>
</part>
<part type="MEFTest.Program, MEFTest">
<imports>
<import member="Commands" contract="IHelp" />
</imports>
</part>
</parts>
</mef.configuration>
I don't get any build errors and it runs fine if I switch to the typical attribute-based system that is part of the MEF core (with the appropriate catalog too). Program.Commands is always NULL in the above example. I tried to just use a singular property instead of a collection and get the same results.
When I debug I can get the provider.Parts collection so I know it's accessing the configuration information correctly; however, I get an InvalidOperationException whenever I debug and try to drill into catalog.Parts.
Anyone have any experience as to where I'm going wrong here?
As documented here, you also need this in your config file:
<configSections>
<section
name="mef.configuration"
type="MefContrib.Models.Provider.Definitions.Configurable.PartCatalogConfigurationSection, MefContrib.Models.Provider" />
</configSections>
If you already have that, then it might be interesting to show us the stack trace of the InvalidOperationException that you get when accessing provider.Parts.
I had the same problems and could not get it to work, but here are some details:
It seems that ComposeParts() does not work as expected (at least in the version I used) because it uses static methods, based on Reflection to find all required Imports (so it seems that this part cannot be changed from outside of MEF). Unfortunately we want to use xml configuration and not the MEF attributes.
It works if you add [Import] attributes to the members of the class you you use with ComposeParts(). In your case this would be "Programm". In this case all exports defined in the configuration file will be found.
I could not find any documentation or examples on the MEF Contrib page relating to that problem. Also there is no unittest in the MEF contrib projekt that uses ComposeParts().
A workaround would be to use container.GetExportedValues() to retrieve the values, but in this case you have to set the classes members manually.
Hope that helps.